Merge "DO NOT MERGE - Re-add ContentCapture support from standard SDK toolkit." into qt-r1-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 7c21875..f311fc8 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -252,6 +252,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DynamicAndroidInstallationService)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DefaultContainerService)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CaptivePortalLogin)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/api/test-current.txt b/api/test-current.txt
index 78bfa90..aac58ed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3172,6 +3172,7 @@
   @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
     method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForContentCapture();
     method public android.view.View getTooltipView();
+    method public boolean isAutofilled();
     method public static boolean isDefaultFocusHighlightEnabled();
     method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public final boolean isImportantForContentCapture();
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
index f56f101..9e9dac1 100644
--- a/cmds/incident/Android.bp
+++ b/cmds/incident/Android.bp
@@ -30,7 +30,7 @@
     ],
 
     static_libs: [
-        "libplatformprotos",
+        "libprotoutil",
     ],
 
     cflags: [
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index eca781f..dfb4f99 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -21,6 +21,7 @@
 #include <android/os/BnIncidentReportStatusListener.h>
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
+#include <android/util/ProtoOutputStream.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <utils/Looper.h>
@@ -36,6 +37,9 @@
 using namespace android::base;
 using namespace android::binder;
 using namespace android::os;
+using android::util::FIELD_COUNT_SINGLE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
 
 // ================================================================================
 class StatusListener : public BnIncidentReportStatusListener {
@@ -129,11 +133,11 @@
 static IncidentSection const*
 find_section(const char* name)
 {
-    size_t low = 0;
-    size_t high = INCIDENT_SECTION_COUNT - 1;
+    ssize_t low = 0;
+    ssize_t high = INCIDENT_SECTION_COUNT - 1;
 
     while (low <= high) {
-        size_t mid = (low + high) >> 1;
+        ssize_t mid = (low + high) / 2;
         IncidentSection const* section = INCIDENT_SECTIONS + mid;
 
         int cmp = strcmp(section->name, name);
@@ -208,6 +212,7 @@
     fprintf(out, "and one of these destinations:\n");
     fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
     fprintf(out, "  -d           send the report into dropbox\n");
+    fprintf(out, "  -r REASON    human readable description of why the report is taken.\n");
     fprintf(out, "  -s PKG/CLS   send broadcast to the broadcast receiver.\n");
     fprintf(out, "\n");
     fprintf(out, "  SECTION     the field numbers of the incident report fields to include\n");
@@ -221,11 +226,12 @@
     IncidentReportArgs args;
     enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST } destination = DEST_UNSET;
     int privacyPolicy = PRIVACY_POLICY_AUTOMATIC;
+    string reason;
     string receiverArg;
 
     // Parse the args
     int opt;
-    while ((opt = getopt(argc, argv, "bhdlp:s:")) != -1) {
+    while ((opt = getopt(argc, argv, "bhdlp:r:s:")) != -1) {
         switch (opt) {
             case 'h':
                 usage(stdout);
@@ -250,6 +256,13 @@
             case 'p':
                 privacyPolicy = get_privacy_policy(optarg);
                 break;
+            case 'r':
+                if (reason.size() > 0) {
+                    usage(stderr);
+                    return 1;
+                }
+                reason = optarg;
+                break;
             case 's':
                 if (destination != DEST_UNSET) {
                     usage(stderr);
@@ -291,6 +304,7 @@
                 } else {
                     IncidentSection const* ic = find_section(arg);
                     if (ic == NULL) {
+                        ALOGD("Invalid section: %s\n", arg);
                         fprintf(stderr, "Invalid section: %s\n", arg);
                         return 1;
                     }
@@ -301,6 +315,14 @@
     }
     args.setPrivacyPolicy(privacyPolicy);
 
+    if (reason.size() > 0) {
+        ProtoOutputStream proto;
+        proto.write(/* reason field id */ 2 | FIELD_TYPE_STRING | FIELD_COUNT_SINGLE, reason);
+        vector<uint8_t> header;
+        proto.serializeToVector(&header);
+        args.addHeader(header);
+    }
+
     // Start the thread pool.
     sp<ProcessState> ps(ProcessState::self());
     ps->startThreadPool();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index abcccf8..24df883 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -299,6 +299,10 @@
         CarPowerStateChanged car_power_state_changed = 203;
         GarageModeInfo garage_mode_info = 204;
         TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
+        ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
+        ContentCaptureServiceEvents content_capture_service_events = 207;
+        ContentCaptureSessionEvents content_capture_session_events = 208;
+        ContentCaptureFlushed content_capture_flushed = 209;
     }
 
     // Pulled events will start at field 10000.
@@ -746,6 +750,22 @@
     // This is only applicable when the state is FINISHED.
     // The default value is STOP_REASON_UNKNOWN.
     optional android.app.job.StopReasonEnum stop_reason = 4;
+
+    // The standby bucket of the app that scheduled the job. These match the framework constants
+    // defined in JobSchedulerService.java with the addition of UNKNOWN using -1, as ACTIVE is
+    // already assigned 0.
+    enum Bucket {
+        UNKNOWN = -1;
+        ACTIVE = 0;
+        WORKING_SET = 1;
+        FREQUENT = 2;
+        RARE = 3;
+        NEVER = 4;
+    }
+    optional Bucket standby_bucket = 5 [default = UNKNOWN];
+
+    // The job id (as assigned by the app).
+    optional int32 job_id = 6;
 }
 
 /**
@@ -4831,6 +4851,95 @@
 }
 
 /**
+ * Logs information about mismatched caller for content capture.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/android/service/contentcapture/ContentCaptureService.java
+ */
+message ContentCaptureCallerMismatchReported {
+    optional string intended_package = 1;
+    optional string calling_package = 2;
+}
+
+/**
+ * Logs information about content capture service events.
+ *
+ * Logged from:
+ *   frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+ */
+message ContentCaptureServiceEvents {
+    // The type of event.
+    enum Event {
+        UNKNOWN = 0;
+        ON_CONNECTED = 1;
+        ON_DISCONNECTED = 2;
+        SET_WHITELIST = 3;
+        SET_DISABLED = 4;
+        ON_USER_DATA_REMOVED = 5;
+    }
+    optional Event event = 1;
+    // component/package of content capture service.
+    optional string service_info = 2;
+    // component/package of target.
+    // it's a concatenated list of component/package for SET_WHITELIST event
+    // separated by " ".
+    optional string target_info = 3;
+}
+
+/**
+ * Logs information about content capture session events.
+ *
+ * Logged from:
+ *   frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+ */
+message ContentCaptureSessionEvents {
+    // The type of event.
+    enum Event {
+        UNKNOWN = 0;
+        ON_SESSION_STARTED = 1;
+        ON_SESSION_FINISHED = 2;
+        SESSION_NOT_CREATED = 3;
+    }
+    optional int32 session_id = 1;
+    optional Event event = 2;
+    // (n/a on session finished)
+    optional int32 state_flags = 3;
+    // component/package of content capture service.
+    optional string service_info = 4;
+    // component/package of app.
+    // (n/a on session finished)
+    optional string app_info = 5;
+    optional bool is_child_session = 6;
+}
+
+/**
+ * Logs information about session being flushed.
+ *
+ * Logged from:
+ *   frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+ */
+message ContentCaptureFlushed {
+    optional int32 session_id = 1;
+    // component/package of content capture service.
+    optional string service_info = 2;
+    // component/package of app.
+    optional string app_info = 3;
+    // session start/finish events
+    optional int32 child_session_started = 4;
+    optional int32 child_session_finished = 5;
+    // count of view events.
+    optional int32 view_appeared_count = 6;
+    optional int32 view_disappeared_count = 7;
+    optional int32 view_text_changed_count = 8;
+
+    // Flush stats.
+    optional int32 max_events = 9;
+    optional int32 idle_flush_freq = 10;
+    optional int32 text_flush_freq = 11;
+    optional int32 flush_reason = 12;
+}
+
+/**
  * Pulls on-device BatteryStats power use calculations for the overall device.
  */
 message DeviceCalculatedPowerUse {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 4e419b6..a2fd9d4 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -330,6 +330,8 @@
 
   // Class name of the incident report receiver.
   optional string receiver_cls = 4;
+
+  optional string alert_description = 5;
 }
 
 message PerfettoDetails {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index ff1cb4f..f2c6f1a 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -36,12 +36,14 @@
 using android::util::ProtoOutputStream;
 using std::vector;
 
-using util::FIELD_TYPE_MESSAGE;
 using util::FIELD_TYPE_INT32;
 using util::FIELD_TYPE_INT64;
+using util::FIELD_TYPE_MESSAGE;
+using util::FIELD_TYPE_STRING;
 
 // field ids in IncidentHeaderProto
 const int FIELD_ID_ALERT_ID = 1;
+const int FIELD_ID_REASON = 2;
 const int FIELD_ID_CONFIG_KEY = 3;
 const int FIELD_ID_CONFIG_KEY_UID = 1;
 const int FIELD_ID_CONFIG_KEY_ID = 2;
@@ -57,9 +59,11 @@
 
 namespace {
 void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
-                  int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
+                  int64_t metricValue, const ConfigKey& configKey, const string& reason,
+                  vector<uint8_t>* protoData) {
     ProtoOutputStream headerProto;
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
+    headerProto.write(FIELD_TYPE_STRING | FIELD_ID_REASON, reason);
     uint64_t token =
             headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
     headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid());
@@ -142,7 +146,8 @@
     IncidentReportArgs incidentReport;
 
     vector<uint8_t> protoData;
-    getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
+    getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey,
+                 config.alert_description(), &protoData);
     incidentReport.addHeader(protoData);
 
     for (int i = 0; i < config.section_size(); i++) {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 0524450..e0f7d86 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -20,16 +20,21 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.IUserManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
 import com.android.internal.os.BaseCommand;
 import com.android.internal.telecom.ITelecomService;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.TelephonyProperties;
 
 import java.io.PrintStream;
 
@@ -62,10 +67,14 @@
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
     private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer";
     private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers";
+    private static final String COMMAND_SET_SIM_COUNT = "set-sim-count";
+    private static final String COMMAND_GET_SIM_CONFIG = "get-sim-config";
+    private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
 
     private ComponentName mComponent;
     private String mAccountId;
     private ITelecomService mTelecomService;
+    private ITelephony mTelephonyService;
     private IUserManager mUserManager;
 
     @Override
@@ -88,21 +97,32 @@
                 + "usage: telecom get-default-dialer\n"
                 + "usage: telecom get-system-dialer\n"
                 + "usage: telecom wait-on-handlers\n"
+                + "usage: telecom set-sim-count <COUNT>\n"
+                + "usage: telecom get-sim-config\n"
+                + "usage: telecom get-max-phones\n"
                 + "\n"
-                + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n"
-                + " already been registered with Telecom.\n"
+                + "telecom set-phone-account-enabled: Enables the given phone account, if it has"
+                        + " already been registered with Telecom.\n"
                 + "\n"
-                + "telecom set-phone-account-disabled: Disables the given phone account, if it \n"
-                + " has already been registered with telecom.\n"
+                + "telecom set-phone-account-disabled: Disables the given phone account, if it"
+                        + " has already been registered with telecom.\n"
                 + "\n"
-                + "telecom set-default-dialer: Sets the override default dialer to the given "
-                + "component; this will override whatever the dialer role is set to. \n"
+                + "telecom set-default-dialer: Sets the override default dialer to the given"
+                        + " component; this will override whatever the dialer role is set to.\n"
                 + "\n"
-                + "telecom get-default-dialer: Displays the current default dialer. \n"
+                + "telecom get-default-dialer: Displays the current default dialer.\n"
                 + "\n"
-                + "telecom get-system-dialer: Displays the current system dialer. \n"
+                + "telecom get-system-dialer: Displays the current system dialer.\n"
                 + "\n"
-                + "telecom wait-on-handlers: Wait until all handlers finish their work. \n"
+                + "telecom wait-on-handlers: Wait until all handlers finish their work.\n"
+                + "\n"
+                + "telecom set-sim-count: Set num SIMs (2 for DSDS, 1 for single SIM."
+                        + " This may restart the device.\n"
+                + "\n"
+                + "telecom get-sim-config: Get the mSIM config string. \"DSDS\" for DSDS mode,"
+                        + " or \"\" for single SIM\n"
+                + "\n"
+                + "telecom get-max-phones: Get the max supported phones from the modem.\n"
         );
     }
 
@@ -115,6 +135,15 @@
             showError("Error: Could not access the Telecom Manager. Is the system running?");
             return;
         }
+
+        mTelephonyService = ITelephony.Stub.asInterface(
+                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+        if (mTelephonyService == null) {
+            Log.w(this, "onRun: Can't access telephony service.");
+            showError("Error: Could not access the Telephony Service. Is the system running?");
+            return;
+        }
+
         mUserManager = IUserManager.Stub
                 .asInterface(ServiceManager.getService(Context.USER_SERVICE));
         if (mUserManager == null) {
@@ -170,6 +199,15 @@
             case COMMAND_WAIT_ON_HANDLERS:
                 runWaitOnHandler();
                 break;
+            case COMMAND_SET_SIM_COUNT:
+                runSetSimCount();
+                break;
+            case COMMAND_GET_SIM_CONFIG:
+                runGetSimConfig();
+                break;
+            case COMMAND_GET_MAX_PHONES:
+                runGetMaxPhones();
+                break;
             default:
                 Log.w(this, "onRun: unknown command: %s", command);
                 throw new IllegalArgumentException ("unknown command '" + command + "'");
@@ -271,6 +309,35 @@
 
     }
 
+    private void runSetSimCount() throws RemoteException {
+        if (!callerIsRoot()) {
+            System.out.println("set-sim-count requires adb root");
+            return;
+        }
+        int numSims = Integer.parseInt(nextArgRequired());
+        System.out.println("Setting sim count to " + numSims + ". Device may reboot");
+        mTelephonyService.switchMultiSimConfig(numSims);
+    }
+
+    /**
+     * Prints the mSIM config to the console.
+     * "DSDS" for a phone in DSDS mode
+     * "" (empty string) for a phone in SS mode
+     */
+    private void runGetSimConfig() throws RemoteException {
+        System.out.println(SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG));
+    }
+
+    private void runGetMaxPhones() throws RemoteException {
+        // This assumes the max number of SIMs is 2, which it currently is
+        if (TelephonyManager.MULTISIM_ALLOWED
+                == mTelephonyService.isMultiSimSupported("com.android.commands.telecom")) {
+            System.out.println("2");
+        } else {
+            System.out.println("1");
+        }
+    }
+
     private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
         if (TextUtils.isEmpty(mArgs.peekNextArg())) {
             return null;
@@ -289,6 +356,10 @@
         return new PhoneAccountHandle(component, accountId, userHandle);
     }
 
+    private boolean callerIsRoot() {
+        return Process.ROOT_UID == Process.myUid();
+    }
+
     private ComponentName parseComponentName(String component) {
         ComponentName cn = ComponentName.unflattenFromString(component);
         if (cn == null) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ca04536a..2db1bcc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -123,7 +123,6 @@
 import android.renderscript.RenderScriptCacheDir;
 import android.security.NetworkSecurityPolicy;
 import android.security.net.config.NetworkSecurityConfigProvider;
-import android.service.voice.VoiceInteractionSession;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.system.StructStat;
@@ -3555,11 +3554,13 @@
             @NonNull RemoteCallback callback) {
         final ActivityClientRecord r = mActivities.get(activityToken);
         if (r == null) {
+            Log.w(TAG, "requestDirectActions(): no activity for " + activityToken);
             callback.sendResult(null);
             return;
         }
         final int lifecycleState = r.getLifecycleState();
         if (lifecycleState < ON_START || lifecycleState >= ON_STOP) {
+            Log.w(TAG, "requestDirectActions(" + r + "): wrong lifecycle: " + lifecycleState);
             callback.sendResult(null);
             return;
         }
@@ -5627,6 +5628,16 @@
         }
     }
 
+    /**
+     * Updates the application info.
+     *
+     * This only works in the system process. Must be called on the main thread.
+     */
+    public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
+        Preconditions.checkState(mSystemThread, "Must only be called in the system process");
+        handleApplicationInfoChanged(ai);
+    }
+
     @VisibleForTesting(visibility = PACKAGE)
     public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
         // Updates triggered by package installation go through a package update
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 864af8c..3bf659b 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -120,6 +120,7 @@
 
         mActivityTaskManager = ActivityTaskManager.getService();
         mSurfaceView = new SurfaceView(context);
+        mSurfaceView.setAlpha(0f);
         mSurfaceCallback = new SurfaceCallback();
         mSurfaceView.getHolder().addCallback(mSurfaceCallback);
         addView(mSurfaceView);
@@ -189,6 +190,17 @@
     }
 
     /**
+     * Sets the corner radius for the Activity displayed here. The corners will be
+     * cropped from the window painted by the contained Activity.
+     *
+     * @param cornerRadius the radius for the corners, in pixels
+     * @hide
+     */
+    public void setCornerRadius(float cornerRadius) {
+        mSurfaceView.setCornerRadius(cornerRadius);
+    }
+
+    /**
      * Launch a new activity into this container.
      * <p>Activity resolved by the provided {@link Intent} must have
      * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
@@ -336,6 +348,16 @@
     }
 
     @Override
+    public void setAlpha(float alpha) {
+        mSurfaceView.setAlpha(alpha);
+    }
+
+    @Override
+    public float getAlpha() {
+        return mSurfaceView.getAlpha();
+    }
+
+    @Override
     public boolean gatherTransparentRegion(Region region) {
         // The tap exclude region may be affected by any view on top of it, so we detect the
         // possible change by monitoring this function.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4e22cc1..685eedc 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1920,8 +1920,8 @@
     private int installExistingPackageAsUser(String packageName, int installReason, int userId)
             throws NameNotFoundException {
         try {
-            int res = mPM.installExistingPackageAsUser(packageName, userId, 0 /*installFlags*/,
-                    installReason);
+            int res = mPM.installExistingPackageAsUser(packageName, userId,
+                    INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, installReason, null);
             if (res == INSTALL_FAILED_INVALID_URI) {
                 throw new NameNotFoundException("Package " + packageName + " doesn't exist");
             }
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 1fdc8ca5..3f6880f 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -169,4 +169,12 @@
      * @param taskInfo info about the task which received the back press
      */
     void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
+
+    /*
+     * Called when contents are drawn for the first time on a display which can only contain one
+     * task.
+     *
+     * @param displayId the id of the display on which contents are drawn.
+     */
+    void onSingleTaskDisplayDrawn(int displayId);
 }
diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl
index 928c627..9e7f2fe 100644
--- a/core/java/android/app/IUriGrantsManager.aidl
+++ b/core/java/android/app/IUriGrantsManager.aidl
@@ -31,11 +31,12 @@
             in Uri uri, int mode, int sourceUserId, int targetUserId);
     /**
      * Gets the URI permissions granted to an arbitrary package (or all packages if null)
-     * NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
+     * NOTE: this is different from getUriPermissions(), which returns the URIs the package
      * granted to another packages (instead of those granted to it).
      */
     ParceledListSlice getGrantedUriPermissions(in String packageName, int userId);
     /** Clears the URI permissions granted to an arbitrary package. */
     void clearGrantedUriPermissions(in String packageName, int userId);
-    ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
+    ParceledListSlice getUriPermissions(in String packageName, boolean incoming,
+            boolean persistedOnly);
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 00f3ad5..36daf32 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -173,4 +173,8 @@
     public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo)
             throws RemoteException {
     }
+
+    @Override
+    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index ec0180c5..69682c6 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -53,6 +53,10 @@
      * NOTE: HANDLE is only valid for a single session with the device. */
     public static final String EXTRA_MESSAGE_HANDLE =
             "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE";
+    public static final String EXTRA_MESSAGE_TIMESTAMP =
+            "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP";
+    public static final String EXTRA_MESSAGE_READ_STATUS =
+            "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS";
     public static final String EXTRA_SENDER_CONTACT_URI =
             "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
     public static final String EXTRA_SENDER_CONTACT_NAME =
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 07ba2c6..038994f 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -590,6 +590,9 @@
         public @NonNull Builder setServiceSolicitationUuid(
                 @Nullable ParcelUuid serviceSolicitationUuid) {
             mServiceSolicitationUuid = serviceSolicitationUuid;
+            if (serviceSolicitationUuid == null) {
+                mServiceSolicitationUuidMask = null;
+            }
             return this;
         }
 
@@ -600,13 +603,16 @@
          * indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to
          * ignore that bit.
          *
+         * @param serviceSolicitationUuid can only be null if solicitationUuidMask is null.
+         * @param solicitationUuidMask can be null or a mask with no restriction.
+         *
          * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
          *             {@code serviceSolicitationUuidMask} is not {@code null}.
          */
         public @NonNull Builder setServiceSolicitationUuid(
                 @Nullable ParcelUuid serviceSolicitationUuid,
                 @Nullable ParcelUuid solicitationUuidMask) {
-            if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) {
+            if (solicitationUuidMask != null && serviceSolicitationUuid == null) {
                 throw new IllegalArgumentException(
                         "SolicitationUuid is null while SolicitationUuidMask is not null!");
             }
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 8760efe..dc1c700 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -103,7 +103,7 @@
         try {
             Preconditions.checkNotNull(clip);
             clip.prepareToLeaveProcess(true);
-            mService.setPrimaryClip(clip, mContext.getOpPackageName());
+            mService.setPrimaryClip(clip, mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -116,7 +116,7 @@
      */
     public void clearPrimaryClip() {
         try {
-            mService.clearPrimaryClip(mContext.getOpPackageName());
+            mService.clearPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -132,7 +132,7 @@
      */
     public @Nullable ClipData getPrimaryClip() {
         try {
-            return mService.getPrimaryClip(mContext.getOpPackageName());
+            return mService.getPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -149,7 +149,8 @@
      */
     public @Nullable ClipDescription getPrimaryClipDescription() {
         try {
-            return mService.getPrimaryClipDescription(mContext.getOpPackageName());
+            return mService.getPrimaryClipDescription(mContext.getOpPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -163,7 +164,7 @@
      */
     public boolean hasPrimaryClip() {
         try {
-            return mService.hasPrimaryClip(mContext.getOpPackageName());
+            return mService.hasPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -174,7 +175,8 @@
             if (mPrimaryClipChangedListeners.isEmpty()) {
                 try {
                     mService.addPrimaryClipChangedListener(
-                            mPrimaryClipChangedServiceListener, mContext.getOpPackageName());
+                            mPrimaryClipChangedServiceListener, mContext.getOpPackageName(),
+                            mContext.getUserId());
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -189,7 +191,8 @@
             if (mPrimaryClipChangedListeners.isEmpty()) {
                 try {
                     mService.removePrimaryClipChangedListener(
-                            mPrimaryClipChangedServiceListener);
+                            mPrimaryClipChangedServiceListener, mContext.getOpPackageName(),
+                            mContext.getUserId());
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -226,7 +229,7 @@
     @Deprecated
     public boolean hasText() {
         try {
-            return mService.hasClipboardText(mContext.getOpPackageName());
+            return mService.hasClipboardText(mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2c5860a..0a1bc85 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2482,8 +2482,8 @@
      */
     public @NonNull List<UriPermission> getPersistedUriPermissions() {
         try {
-            return UriGrantsManager.getService()
-                    .getPersistedUriPermissions(mPackageName, true).getList();
+            return UriGrantsManager.getService().getUriPermissions(
+                    mPackageName, true /* incoming */, true /* persistedOnly */).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2498,8 +2498,18 @@
      */
     public @NonNull List<UriPermission> getOutgoingPersistedUriPermissions() {
         try {
-            return UriGrantsManager.getService()
-                    .getPersistedUriPermissions(mPackageName, false).getList();
+            return UriGrantsManager.getService().getUriPermissions(
+                    mPackageName, false /* incoming */, true /* persistedOnly */).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public @NonNull List<UriPermission> getOutgoingUriPermissions() {
+        try {
+            return UriGrantsManager.getService().getUriPermissions(
+                    mPackageName, false /* incoming */, false /* persistedOnly */).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index 135a436..0d5a460 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -26,17 +26,18 @@
  * {@hide}
  */
 interface IClipboard {
-    void setPrimaryClip(in ClipData clip, String callingPackage);
-    void clearPrimaryClip(String callingPackage);
-    ClipData getPrimaryClip(String pkg);
-    ClipDescription getPrimaryClipDescription(String callingPackage);
-    boolean hasPrimaryClip(String callingPackage);
+    void setPrimaryClip(in ClipData clip, String callingPackage, int userId);
+    void clearPrimaryClip(String callingPackage, int userId);
+    ClipData getPrimaryClip(String pkg, int userId);
+    ClipDescription getPrimaryClipDescription(String callingPackage, int userId);
+    boolean hasPrimaryClip(String callingPackage, int userId);
     void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
-            String callingPackage);
-    void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener);
+            String callingPackage, int userId);
+    void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
+            String callingPackage, int userId);
 
     /**
      * Returns true if the clipboard contains text; false otherwise.
      */
-    boolean hasClipboardText(String callingPackage);
+    boolean hasClipboardText(String callingPackage, int userId);
 }
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 8e84079..b0b2c33 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -52,7 +52,7 @@
             in IntentSender statusReceiver, int userId);
 
     void installExistingPackage(String packageName, int installFlags, int installReason,
-            in IntentSender statusReceiver, int userId);
+            in IntentSender statusReceiver, int userId, in List<String> whiteListedPermissions);
 
     void setPermissionsResult(int sessionId, boolean accepted);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6ab4657..a7eecd7 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -634,7 +634,7 @@
     int getInstallLocation();
 
     int installExistingPackageAsUser(String packageName, int userId, int installFlags,
-            int installReason);
+            int installReason, in List<String> whiteListedPermissions);
 
     void verifyPendingInstall(int id, int verificationCode);
     void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 89eabc2..3cecd7f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -627,6 +627,10 @@
      * Install the given package, which already exists on the device, for the user for which this
      * installer was created.
      *
+     * <p>This will
+     * {@link PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) whitelist
+     * all restricted permissions}.
+     *
      * @param packageName The package to install.
      * @param installReason Reason for install.
      * @param statusReceiver Where to deliver the result.
@@ -639,8 +643,9 @@
             @Nullable IntentSender statusReceiver) {
         Preconditions.checkNotNull(packageName, "packageName cannot be null");
         try {
-            mInstaller.installExistingPackage(packageName, 0, installReason, statusReceiver,
-                    mUserId);
+            mInstaller.installExistingPackage(packageName,
+                    PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, installReason,
+                    statusReceiver, mUserId, null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bdab1e2..b09eada 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -223,6 +223,7 @@
 
     /** @hide */
     @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+            MATCH_ALL,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ModuleInfoFlags {}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 633966c..794be9e 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1390,11 +1390,9 @@
                 @StyleableRes int[] attrs,
                 @AttrRes int defStyleAttr,
                 @StyleRes int defStyleRes) {
-            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "obtainStyledAttributes");
-            TypedArray array;
             synchronized (mKey) {
                 final int len = attrs.length;
-                array = TypedArray.obtain(wrapper.getResources(), len);
+                final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
 
                 // XXX note that for now we only work with compiled XML files.
                 // To support generic XML files we will need to manually parse
@@ -1405,9 +1403,8 @@
                         array.mDataAddress, array.mIndicesAddress);
                 array.mTheme = wrapper;
                 array.mXml = parser;
+                return array;
             }
-            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
-            return array;
         }
 
         @NonNull
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b0142ea..f662b61 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -98,7 +98,7 @@
      * identifiers, while removable cameras have a unique identifier for each
      * individual device, even if they are the same model.</p>
      *
-     * <p>This list doesn't contain physical cameras that can only used as part of a logical
+     * <p>This list doesn't contain physical cameras that can only be used as part of a logical
      * multi-camera device.</p>
      *
      * @return The list of currently connected camera devices.
@@ -263,7 +263,7 @@
      * immutable for a given camera.</p>
      *
      * <p>From API level 29, this function can also be used to query the capabilities of physical
-     * cameras that can only be used as part of logical multi-camera. These cameras cannot not be
+     * cameras that can only be used as part of logical multi-camera. These cameras cannot be
      * opened directly via {@link #openCamera}</p>
      *
      * @param cameraId The id of the camera device to query. This could be either a standalone
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index eddfd7d..6a2fc8a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -235,6 +235,62 @@
             new HashMap<String, CameraMetadataNative>();
 
     private boolean mIsReprocess;
+
+    //
+    // Enumeration values for types of CaptureRequest
+    //
+
+    /**
+     * @hide
+     */
+    public static final int REQUEST_TYPE_REGULAR = 0;
+
+    /**
+     * @hide
+     */
+    public static final int REQUEST_TYPE_REPROCESS = 1;
+
+    /**
+     * @hide
+     */
+    public static final int REQUEST_TYPE_ZSL_STILL = 2;
+
+    /**
+     * Note: To add another request type, the FrameNumberTracker in CameraDeviceImpl must be
+     * adjusted accordingly.
+     * @hide
+     */
+    public static final int REQUEST_TYPE_COUNT = 3;
+
+
+    private int mRequestType = -1;
+
+    /**
+     * Get the type of the capture request
+     *
+     * Return one of REGULAR, ZSL_STILL, or REPROCESS.
+     * @hide
+     */
+    public int getRequestType() {
+        if (mRequestType == -1) {
+            if (mIsReprocess) {
+                mRequestType = REQUEST_TYPE_REPROCESS;
+            } else {
+                Boolean enableZsl = mLogicalCameraSettings.get(CaptureRequest.CONTROL_ENABLE_ZSL);
+                boolean isZslStill = false;
+                if (enableZsl != null && enableZsl) {
+                    int captureIntent = mLogicalCameraSettings.get(
+                            CaptureRequest.CONTROL_CAPTURE_INTENT);
+                    if (captureIntent == CameraMetadata.CONTROL_CAPTURE_INTENT_STILL_CAPTURE) {
+                        isZslStill = true;
+                    }
+                }
+                mRequestType = isZslStill ? REQUEST_TYPE_ZSL_STILL : REQUEST_TYPE_REGULAR;
+            }
+        }
+        return mRequestType;
+    }
+
     // If this request is part of constrained high speed request list that was created by
     // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
     private boolean mIsPartOfCHSRequestList = false;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index fc12b09..cc8c182 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -95,6 +95,8 @@
             new SparseArray<CaptureCallbackHolder>();
 
     private int mRepeatingRequestId = REQUEST_ID_NONE;
+    // Latest repeating request list's types
+    private int[] mRepeatingRequestTypes;
     // Map stream IDs to input/output configurations
     private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
             new SimpleEntry<>(REQUEST_ID_NONE, null);
@@ -108,7 +110,7 @@
     private static final long NANO_PER_SECOND = 1000000000; //ns
 
     /**
-     * A list tracking request and its expected last regular frame number and last reprocess frame
+     * A list tracking request and its expected last regular/reprocess/zslStill frame
      * number. Updated when calling ICameraDeviceUser methods.
      */
     private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList =
@@ -943,11 +945,12 @@
      * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
      *
      * @param requestId the request ID of the current repeating request.
-     *
      * @param lastFrameNumber last frame number returned from binder.
+     * @param repeatingRequestTypes the repeating requests' types.
      */
     private void checkEarlyTriggerSequenceComplete(
-            final int requestId, final long lastFrameNumber) {
+            final int requestId, final long lastFrameNumber,
+            final int[] repeatingRequestTypes) {
         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
         // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
         if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
@@ -997,10 +1000,10 @@
                         requestId));
             }
         } else {
-            // This function is only called for regular request so lastFrameNumber is the last
-            // regular frame number.
+            // This function is only called for regular/ZslStill request so lastFrameNumber is the
+            // last regular/ZslStill frame number.
             mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
-                    lastFrameNumber));
+                    lastFrameNumber, repeatingRequestTypes));
 
             // It is possible that the last frame has already arrived, so we need to check
             // for sequence completion right away
@@ -1008,6 +1011,14 @@
         }
     }
 
+    private int[] getRequestTypes(final CaptureRequest[] requestArray) {
+        int[] requestTypes = new int[requestArray.length];
+        for (int i = 0; i < requestArray.length; i++) {
+            requestTypes[i] = requestArray[i].getRequestType();
+        }
+        return requestTypes;
+    }
+
     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
             Executor executor, boolean repeating) throws CameraAccessException {
 
@@ -1077,9 +1088,11 @@
             if (repeating) {
                 if (mRepeatingRequestId != REQUEST_ID_NONE) {
                     checkEarlyTriggerSequenceComplete(mRepeatingRequestId,
-                            requestInfo.getLastFrameNumber());
+                            requestInfo.getLastFrameNumber(),
+                            mRepeatingRequestTypes);
                 }
                 mRepeatingRequestId = requestInfo.getRequestId();
+                mRepeatingRequestTypes = getRequestTypes(requestArray);
             } else {
                 mRequestLastFrameNumbersList.add(
                     new RequestLastFrameNumbersHolder(requestList, requestInfo));
@@ -1117,6 +1130,8 @@
 
                 int requestId = mRepeatingRequestId;
                 mRepeatingRequestId = REQUEST_ID_NONE;
+                int[] requestTypes = mRepeatingRequestTypes;
+                mRepeatingRequestTypes = null;
 
                 long lastFrameNumber;
                 try {
@@ -1129,7 +1144,7 @@
                     return;
                 }
 
-                checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
+                checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber, requestTypes);
             }
         }
     }
@@ -1162,8 +1177,10 @@
 
             long lastFrameNumber = mRemoteDevice.flush();
             if (mRepeatingRequestId != REQUEST_ID_NONE) {
-                checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
+                checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber,
+                        mRepeatingRequestTypes);
                 mRepeatingRequestId = REQUEST_ID_NONE;
+                mRepeatingRequestTypes = null;
             }
         }
     }
@@ -1470,7 +1487,7 @@
     }
 
     /**
-     * This class holds a capture ID and its expected last regular frame number and last reprocess
+     * This class holds a capture ID and its expected last regular, zslStill, and reprocess
      * frame number.
      */
     static class RequestLastFrameNumbersHolder {
@@ -1482,6 +1499,9 @@
         // The last reprocess frame number for this request ID. It's
         // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request.
         private final long mLastReprocessFrameNumber;
+        // The last ZSL still capture frame number for this request ID. It's
+        // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no zsl request.
+        private final long mLastZslStillFrameNumber;
 
         /**
          * Create a request-last-frame-numbers holder with a list of requests, request ID, and
@@ -1490,6 +1510,7 @@
         public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo) {
             long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
             long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+            long lastZslStillFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
             long frameNumber = requestInfo.getLastFrameNumber();
 
             if (requestInfo.getLastFrameNumber() < requestList.size() - 1) {
@@ -1499,19 +1520,24 @@
                         " requests in the list: " + requestList.size());
             }
 
-            // find the last regular frame number and the last reprocess frame number
+            // find the last regular, zslStill, and reprocess frame number
             for (int i = requestList.size() - 1; i >= 0; i--) {
                 CaptureRequest request = requestList.get(i);
-                if (request.isReprocess() && lastReprocessFrameNumber ==
-                        CaptureCallback.NO_FRAMES_CAPTURED) {
+                int requestType = request.getRequestType();
+                if (requestType == CaptureRequest.REQUEST_TYPE_REPROCESS
+                        && lastReprocessFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
                     lastReprocessFrameNumber = frameNumber;
-                } else if (!request.isReprocess() && lastRegularFrameNumber ==
-                        CaptureCallback.NO_FRAMES_CAPTURED) {
+                } else if (requestType == CaptureRequest.REQUEST_TYPE_ZSL_STILL
+                        && lastZslStillFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
+                    lastZslStillFrameNumber = frameNumber;
+                } else if (requestType == CaptureRequest.REQUEST_TYPE_REGULAR
+                        && lastRegularFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
                     lastRegularFrameNumber = frameNumber;
                 }
 
-                if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED &&
-                        lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
+                if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED
+                        && lastZslStillFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED
+                        && lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
                     break;
                 }
 
@@ -1520,15 +1546,51 @@
 
             mLastRegularFrameNumber = lastRegularFrameNumber;
             mLastReprocessFrameNumber = lastReprocessFrameNumber;
+            mLastZslStillFrameNumber = lastZslStillFrameNumber;
             mRequestId = requestInfo.getRequestId();
         }
 
         /**
-         * Create a request-last-frame-numbers holder with a request ID and last regular frame
-         * number.
+         * Create a request-last-frame-numbers holder with a request ID and last regular/ZslStill
+         * frame number.
          */
-        public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) {
+        RequestLastFrameNumbersHolder(int requestId, long lastFrameNumber,
+                int[] repeatingRequestTypes) {
+            long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+            long lastZslStillFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+
+            if (repeatingRequestTypes == null) {
+                throw new IllegalArgumentException(
+                        "repeatingRequest list must not be null");
+            }
+            if (lastFrameNumber < repeatingRequestTypes.length - 1) {
+                throw new IllegalArgumentException(
+                        "lastFrameNumber: " + lastFrameNumber + " should be at least "
+                        + (repeatingRequestTypes.length - 1)
+                        + " for the number of requests in the list: "
+                        + repeatingRequestTypes.length);
+            }
+
+            long frameNumber = lastFrameNumber;
+            for (int i = repeatingRequestTypes.length - 1; i >= 0; i--) {
+                if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_ZSL_STILL
+                        && lastZslStillFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
+                    lastZslStillFrameNumber = frameNumber;
+                } else if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_REGULAR
+                        && lastRegularFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) {
+                    lastRegularFrameNumber = frameNumber;
+                }
+
+                if (lastZslStillFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED
+                        && lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
+                    break;
+                }
+
+                frameNumber--;
+            }
+
             mLastRegularFrameNumber = lastRegularFrameNumber;
+            mLastZslStillFrameNumber = lastZslStillFrameNumber;
             mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
             mRequestId = requestId;
         }
@@ -1550,10 +1612,19 @@
         }
 
         /**
+         * Return the last ZslStill frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
+         * it contains no Zsl request.
+         */
+        public long getLastZslStillFrameNumber() {
+            return mLastZslStillFrameNumber;
+        }
+
+        /**
          * Return the last frame number overall.
          */
         public long getLastFrameNumber() {
-            return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber);
+            return Math.max(mLastZslStillFrameNumber,
+                    Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber));
         }
 
         /**
@@ -1569,43 +1640,58 @@
      */
     public class FrameNumberTracker {
 
-        private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
-        private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
-        /** the skipped frame numbers that belong to regular results */
-        private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
-        /** the skipped frame numbers that belong to reprocess results */
-        private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>();
-        /** frame number -> is reprocess */
-        private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>();
+        /** the completed frame number for each type of capture results */
+        private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT];
+
+        /** the skipped frame numbers that don't belong to each type of capture results */
+        private final LinkedList<Long>[] mSkippedOtherFrameNumbers =
+                new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
+
+        /** the skipped frame numbers that belong to each type of capture results */
+        private final LinkedList<Long>[] mSkippedFrameNumbers =
+                new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT];
+
+        /** frame number -> request type */
+        private final TreeMap<Long, Integer> mFutureErrorMap = new TreeMap<Long, Integer>();
         /** Map frame numbers to list of partial results */
         private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
 
+        public FrameNumberTracker() {
+            for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
+                mCompletedFrameNumber[i] = CaptureCallback.NO_FRAMES_CAPTURED;
+                mSkippedOtherFrameNumbers[i] = new LinkedList<Long>();
+                mSkippedFrameNumbers[i] = new LinkedList<Long>();
+            }
+        }
+
         private void update() {
             Iterator iter = mFutureErrorMap.entrySet().iterator();
             while (iter.hasNext()) {
                 TreeMap.Entry pair = (TreeMap.Entry)iter.next();
                 Long errorFrameNumber = (Long)pair.getKey();
-                Boolean reprocess = (Boolean)pair.getValue();
-                Boolean removeError = true;
-                if (reprocess) {
-                    if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) {
-                        mCompletedReprocessFrameNumber = errorFrameNumber;
-                    } else if (mSkippedReprocessFrameNumbers.isEmpty() != true &&
-                            errorFrameNumber == mSkippedReprocessFrameNumbers.element()) {
-                        mCompletedReprocessFrameNumber = errorFrameNumber;
-                        mSkippedReprocessFrameNumbers.remove();
-                    } else {
-                        removeError = false;
-                    }
+                int requestType = (int) pair.getValue();
+                Boolean removeError = false;
+                if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) {
+                    mCompletedFrameNumber[requestType] = errorFrameNumber;
+                    removeError = true;
                 } else {
-                    if (errorFrameNumber == mCompletedFrameNumber + 1) {
-                        mCompletedFrameNumber = errorFrameNumber;
-                    } else if (mSkippedRegularFrameNumbers.isEmpty() != true &&
-                            errorFrameNumber == mSkippedRegularFrameNumbers.element()) {
-                        mCompletedFrameNumber = errorFrameNumber;
-                        mSkippedRegularFrameNumbers.remove();
+                    if (!mSkippedFrameNumbers[requestType].isEmpty()) {
+                        if (errorFrameNumber == mSkippedFrameNumbers[requestType].element()) {
+                            mCompletedFrameNumber[requestType] = errorFrameNumber;
+                            mSkippedFrameNumbers[requestType].remove();
+                            removeError = true;
+                        }
                     } else {
-                        removeError = false;
+                        for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) {
+                            int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT;
+                            if (!mSkippedOtherFrameNumbers[otherType].isEmpty() && errorFrameNumber
+                                    == mSkippedOtherFrameNumbers[otherType].element()) {
+                                mCompletedFrameNumber[requestType] = errorFrameNumber;
+                                mSkippedOtherFrameNumbers[otherType].remove();
+                                removeError = true;
+                                break;
+                            }
+                        }
                     }
                 }
                 if (removeError) {
@@ -1618,18 +1704,14 @@
          * This function is called every time when a result or an error is received.
          * @param frameNumber the frame number corresponding to the result or error
          * @param isError true if it is an error, false if it is not an error
-         * @param isReprocess true if it is a reprocess result, false if it is a regular result.
+         * @param requestType the type of capture request: Reprocess, ZslStill, or Regular.
          */
-        public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) {
+        public void updateTracker(long frameNumber, boolean isError, int requestType) {
             if (isError) {
-                mFutureErrorMap.put(frameNumber, isReprocess);
+                mFutureErrorMap.put(frameNumber, requestType);
             } else {
                 try {
-                    if (isReprocess) {
-                        updateCompletedReprocessFrameNumber(frameNumber);
-                    } else {
-                        updateCompletedFrameNumber(frameNumber);
-                    }
+                    updateCompletedFrameNumber(frameNumber, requestType);
                 } catch (IllegalArgumentException e) {
                     Log.e(TAG, e.getMessage());
                 }
@@ -1646,13 +1728,13 @@
          * @param frameNumber the frame number corresponding to the result
          * @param result the total or partial result
          * @param partial {@true} if the result is partial, {@code false} if total
-         * @param isReprocess true if it is a reprocess result, false if it is a regular result.
+         * @param requestType the type of capture request: Reprocess, ZslStill, or Regular.
          */
         public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
-                boolean isReprocess) {
+                int requestType) {
             if (!partial) {
                 // Update the total result's frame status as being successful
-                updateTracker(frameNumber, /*isError*/false, isReprocess);
+                updateTracker(frameNumber, /*isError*/false, requestType);
                 // Don't keep a list of total results, we don't need to track them
                 return;
             }
@@ -1688,92 +1770,105 @@
         }
 
         public long getCompletedFrameNumber() {
-            return mCompletedFrameNumber;
+            return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REGULAR];
         }
 
         public long getCompletedReprocessFrameNumber() {
-            return mCompletedReprocessFrameNumber;
+            return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REPROCESS];
+        }
+
+        public long getCompletedZslStillFrameNumber() {
+            return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_ZSL_STILL];
         }
 
         /**
-         * Update the completed frame number for regular results.
+         * Update the completed frame number for results of 3 categories
+         * (Regular/Reprocess/ZslStill).
          *
-         * It validates that all previous frames have arrived except for reprocess frames.
+         * It validates that all previous frames of the same category have arrived.
          *
-         * If there is a gap since previous regular frame number, assume the frames in the gap are
-         * reprocess frames and store them in the skipped reprocess frame number queue to check
-         * against when reprocess frames arrive.
+         * If there is a gap since previous frame number of the same category, assume the frames in
+         * the gap are other categories and store them in the skipped frame number queue to check
+         * against when frames of those categories arrive.
          */
-        private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException {
-            if (frameNumber <= mCompletedFrameNumber) {
+        private void updateCompletedFrameNumber(long frameNumber,
+                int requestType) throws IllegalArgumentException {
+            if (frameNumber <= mCompletedFrameNumber[requestType]) {
                 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
-            } else if (frameNumber <= mCompletedReprocessFrameNumber) {
-                // if frame number is smaller than completed reprocess frame number,
-                // it must be the head of mSkippedRegularFrameNumbers
-                if (mSkippedRegularFrameNumbers.isEmpty() == true ||
-                        frameNumber < mSkippedRegularFrameNumbers.element()) {
-                    throw new IllegalArgumentException("frame number " + frameNumber +
-                            " is a repeat");
-                } else if (frameNumber > mSkippedRegularFrameNumbers.element()) {
-                    throw new IllegalArgumentException("frame number " + frameNumber +
-                            " comes out of order. Expecting " +
-                            mSkippedRegularFrameNumbers.element());
+            }
+
+            // Assume there are only 3 different types of capture requests.
+            int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT;
+            int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT;
+            long maxOtherFrameNumberSeen =
+                    Math.max(mCompletedFrameNumber[otherType1], mCompletedFrameNumber[otherType2]);
+            if (frameNumber < maxOtherFrameNumberSeen) {
+                // if frame number is smaller than completed frame numbers of other categories,
+                // it must be:
+                // - the head of mSkippedFrameNumbers for this category, or
+                // - in one of other mSkippedOtherFrameNumbers
+                if (!mSkippedFrameNumbers[requestType].isEmpty()) {
+                    // frame number must be head of current type of mSkippedFrameNumbers if
+                    // mSkippedFrameNumbers isn't empty.
+                    if (frameNumber < mSkippedFrameNumbers[requestType].element()) {
+                        throw new IllegalArgumentException("frame number " + frameNumber
+                                + " is a repeat");
+                    } else if (frameNumber > mSkippedFrameNumbers[requestType].element()) {
+                        throw new IllegalArgumentException("frame number " + frameNumber
+                                + " comes out of order. Expecting "
+                                + mSkippedFrameNumbers[requestType].element());
+                    }
+                    // frame number matches the head of the skipped frame number queue.
+                    mSkippedFrameNumbers[requestType].remove();
+                } else {
+                    // frame number must be in one of the other mSkippedOtherFrameNumbers.
+                    int index1 = mSkippedOtherFrameNumbers[otherType1].indexOf(frameNumber);
+                    int index2 = mSkippedOtherFrameNumbers[otherType2].indexOf(frameNumber);
+                    boolean inSkippedOther1 = index1 != -1;
+                    boolean inSkippedOther2 = index2 != -1;
+                    if (!(inSkippedOther1 ^ inSkippedOther2)) {
+                        throw new IllegalArgumentException("frame number " + frameNumber
+                                + " is a repeat or invalid");
+                    }
+
+                    // We know the category of frame numbers in skippedOtherFrameNumbers leading up
+                    // to the current frame number. Move them into the correct skippedFrameNumbers.
+                    LinkedList<Long> srcList, dstList;
+                    int index;
+                    if (inSkippedOther1) {
+                        srcList = mSkippedOtherFrameNumbers[otherType1];
+                        dstList = mSkippedFrameNumbers[otherType2];
+                        index = index1;
+                    } else {
+                        srcList = mSkippedOtherFrameNumbers[otherType2];
+                        dstList = mSkippedFrameNumbers[otherType1];
+                        index = index2;
+                    }
+                    for (int i = 0; i < index; i++) {
+                        dstList.add(srcList.removeFirst());
+                    }
+
+                    // Remove current frame number from skippedOtherFrameNumbers
+                    srcList.remove();
                 }
-                // frame number matches the head of the skipped frame number queue.
-                mSkippedRegularFrameNumbers.remove();
             } else {
-                // there is a gap of unseen frame numbers which should belong to reprocess result
-                // put all the skipped frame numbers in the queue
-                for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
+                // there is a gap of unseen frame numbers which should belong to the other
+                // 2 categories. Put all the skipped frame numbers in the queue.
+                for (long i =
+                        Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1;
                         i < frameNumber; i++) {
-                    mSkippedReprocessFrameNumbers.add(i);
+                    mSkippedOtherFrameNumbers[requestType].add(i);
                 }
             }
 
-            mCompletedFrameNumber = frameNumber;
-        }
-
-        /**
-         * Update the completed frame number for reprocess results.
-         *
-         * It validates that all previous frames have arrived except for regular frames.
-         *
-         * If there is a gap since previous reprocess frame number, assume the frames in the gap are
-         * regular frames and store them in the skipped regular frame number queue to check
-         * against when regular frames arrive.
-         */
-        private void updateCompletedReprocessFrameNumber(long frameNumber)
-                throws IllegalArgumentException {
-            if (frameNumber < mCompletedReprocessFrameNumber) {
-                throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
-            } else if (frameNumber < mCompletedFrameNumber) {
-                // if reprocess frame number is smaller than completed regular frame number,
-                // it must be the head of the skipped reprocess frame number queue.
-                if (mSkippedReprocessFrameNumbers.isEmpty() == true ||
-                        frameNumber < mSkippedReprocessFrameNumbers.element()) {
-                    throw new IllegalArgumentException("frame number " + frameNumber +
-                            " is a repeat");
-                } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) {
-                    throw new IllegalArgumentException("frame number " + frameNumber +
-                            " comes out of order. Expecting " +
-                            mSkippedReprocessFrameNumbers.element());
-                }
-                // frame number matches the head of the skipped frame number queue.
-                mSkippedReprocessFrameNumbers.remove();
-            } else {
-                // put all the skipped frame numbers in the queue
-                for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
-                        i < frameNumber; i++) {
-                    mSkippedRegularFrameNumbers.add(i);
-                }
-            }
-            mCompletedReprocessFrameNumber = frameNumber;
+            mCompletedFrameNumber[requestType] = frameNumber;
         }
     }
 
     private void checkAndFireSequenceComplete() {
         long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
         long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
+        long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber();
         boolean isReprocess = false;
         Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
         while (iter.hasNext()) {
@@ -1795,18 +1890,22 @@
                             requestLastFrameNumbers.getLastRegularFrameNumber();
                     long lastReprocessFrameNumber =
                             requestLastFrameNumbers.getLastReprocessFrameNumber();
-
+                    long lastZslStillFrameNumber =
+                            requestLastFrameNumbers.getLastZslStillFrameNumber();
                     // check if it's okay to remove request from mCaptureCallbackMap
-                    if (lastRegularFrameNumber <= completedFrameNumber &&
-                            lastReprocessFrameNumber <= completedReprocessFrameNumber) {
+                    if (lastRegularFrameNumber <= completedFrameNumber
+                            && lastReprocessFrameNumber <= completedReprocessFrameNumber
+                            && lastZslStillFrameNumber <= completedZslStillFrameNumber) {
                         sequenceCompleted = true;
                         mCaptureCallbackMap.removeAt(index);
                         if (DEBUG) {
                             Log.v(TAG, String.format(
-                                    "Remove holder for requestId %d, because lastRegularFrame %d " +
-                                    "is <= %d and lastReprocessFrame %d is <= %d", requestId,
+                                    "Remove holder for requestId %d, because lastRegularFrame %d "
+                                    + "is <= %d, lastReprocessFrame %d is <= %d, "
+                                    + "lastZslStillFrame %d is <= %d", requestId,
                                     lastRegularFrameNumber, completedFrameNumber,
-                                    lastReprocessFrameNumber, completedReprocessFrameNumber));
+                                    lastReprocessFrameNumber, completedReprocessFrameNumber,
+                                    lastZslStillFrameNumber, completedZslStillFrameNumber));
                         }
                     }
                 }
@@ -1925,10 +2024,12 @@
                     return; // Camera already closed
                 }
 
-                checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
+                checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber,
+                        mRepeatingRequestTypes);
                 // Check if there is already a new repeating request
                 if (mRepeatingRequestId == repeatingRequestId) {
                     mRepeatingRequestId = REQUEST_ID_NONE;
+                    mRepeatingRequestTypes = null;
                 }
             }
         }
@@ -2040,7 +2141,7 @@
 
                 boolean isPartialResult =
                         (resultExtras.getPartialResultCount() < mTotalPartialCount);
-                boolean isReprocess = request.isReprocess();
+                int requestType = request.getRequestType();
 
                 // Check if we have a callback for this
                 if (holder == null) {
@@ -2051,7 +2152,7 @@
                     }
 
                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
-                            isReprocess);
+                            requestType);
 
                     return;
                 }
@@ -2064,7 +2165,7 @@
                     }
 
                     mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
-                            isReprocess);
+                            requestType);
                     return;
                 }
 
@@ -2172,7 +2273,7 @@
 
                 // Collect the partials for a total result; or mark the frame as totally completed
                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
-                        isReprocess);
+                        requestType);
 
                 // Fire onCaptureSequenceCompleted
                 if (!isPartialResult) {
@@ -2307,7 +2408,8 @@
                 if (DEBUG) {
                     Log.v(TAG, String.format("got error frame %d", frameNumber));
                 }
-                mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
+                mFrameNumberTracker.updateTracker(frameNumber,
+                        /*error*/true, request.getRequestType());
                 checkAndFireSequenceComplete();
 
                 // Dispatch the failure callback
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index bdba77c..336edc1 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1507,8 +1507,7 @@
                             dataspace != HAL_DATASPACE_DYNAMIC_DEPTH &&
                             dataspace != HAL_DATASPACE_HEIF) &&
                  mAllOutputFormats.get(format) == 0)) {
-            // Only throw if this is really not supported at all
-            throw new IllegalArgumentException("format not available");
+            return null;
         }
 
         Size[] sizes = new Size[sizesCount];
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a69ca99..3bc40a7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3612,8 +3612,9 @@
      * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
      *                        the callback must not be shared - it uniquely specifies this request.
      *                        The callback is invoked on the default internal Handler.
-     * @throws IllegalArgumentException if {@code request} specifies any mutable
-     *         {@code NetworkCapabilities}.
+     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback) {
@@ -3648,8 +3649,9 @@
      * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
      *                        the callback must not be shared - it uniquely specifies this request.
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
-     * @throws IllegalArgumentException if {@code request} specifies any mutable
-     *         {@code NetworkCapabilities}.
+     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
@@ -3685,6 +3687,9 @@
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
+     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, int timeoutMs) {
@@ -3719,6 +3724,9 @@
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable} is called.
+     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
@@ -3789,9 +3797,9 @@
      * @param operation Action to perform when the network is available (corresponds
      *                  to the {@link NetworkCallback#onAvailable} call.  Typically
      *                  comes from {@link PendingIntent#getBroadcast}. Cannot be null.
-     * @throws IllegalArgumentException if {@code request} contains either
-     *         {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
-     *         {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
+     * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+     * @throws SecurityException if missing the appropriate permissions.
+     * @throws RuntimeException if request limit per UID is exceeded.
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull PendingIntent operation) {
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 87f8739..fe42111 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -58,15 +58,15 @@
     @Nullable
     public LinkAddress ipAddress;
     /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     @Nullable
     public InetAddress gateway;
     /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     @NonNull
     public final ArrayList<InetAddress> dnsServers;
     /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    @UnsupportedAppUsage
     @Nullable
     public String domains;
 
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 66ddf21..6178b2b 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1033,6 +1033,8 @@
                     Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
                 }
             } else {
+                // Clear the parcel before writing the exception
+                reply.setDataSize(0);
                 reply.setDataPosition(0);
                 reply.writeException(e);
             }
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
index ed8d3f7..a94fd65 100644
--- a/core/java/android/os/IncidentManager.java
+++ b/core/java/android/os/IncidentManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,6 +35,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Class to take an incident report.
@@ -248,6 +250,24 @@
         public @NonNull String toString() {
             return "PendingReport(" + getUri().toString() + ")";
         }
+
+        /**
+         * @inheritDoc
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof PendingReport)) {
+                return false;
+            }
+            final PendingReport that = (PendingReport) obj;
+            return this.mUri.equals(that.mUri)
+                    && this.mFlags == that.mFlags
+                    && this.mRequestingPackage.equals(that.mRequestingPackage)
+                    && this.mTimestamp == that.mTimestamp;
+        }
     }
 
     /**
@@ -355,21 +375,35 @@
     }
 
     /**
-     * Listener for the status of an incident report being authroized or denied.
+     * Listener for the status of an incident report being authorized or denied.
      *
      * @see #requestAuthorization
      * @see #cancelAuthorization
      */
     public static class AuthListener {
+        Executor mExecutor;
+
         IIncidentAuthListener.Stub mBinder = new IIncidentAuthListener.Stub() {
             @Override
             public void onReportApproved() {
-                AuthListener.this.onReportApproved();
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        AuthListener.this.onReportApproved();
+                    });
+                } else {
+                    AuthListener.this.onReportApproved();
+                }
             }
 
             @Override
             public void onReportDenied() {
-                AuthListener.this.onReportDenied();
+                if (mExecutor != null) {
+                    mExecutor.execute(() -> {
+                        AuthListener.this.onReportDenied();
+                    });
+                } else {
+                    AuthListener.this.onReportDenied();
+                }
             }
         };
 
@@ -410,7 +444,23 @@
     @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
     public void requestAuthorization(int callingUid, String callingPackage, int flags,
             AuthListener listener) {
+        requestAuthorization(callingUid, callingPackage, flags,
+                mContext.getMainExecutor(), listener);
+    }
+
+    /**
+     * Request authorization of an incident report.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL)
+    public void requestAuthorization(int callingUid, @NonNull String callingPackage, int flags,
+             @NonNull @CallbackExecutor Executor executor, @NonNull AuthListener listener) {
         try {
+            if (listener.mExecutor != null) {
+                throw new RuntimeException("Do not reuse AuthListener objects when calling"
+                        + " requestAuthorization");
+            }
+            listener.mExecutor = executor;
             getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, null, null,
                     flags, listener.mBinder);
         } catch (RemoteException ex) {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 2b170c2..3ea3bbc 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -640,14 +640,9 @@
                 ZygoteConfig.USAP_POOL_ENABLED, USAP_POOL_ENABLED_DEFAULT);
 
         if (!propertyString.isEmpty()) {
-            if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) {
-                // TODO(b/119800099): Tweak usap configuration in jitzygote mode.
-                mUsapPoolEnabled = false;
-            } else {
-                mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
-                    ZygoteConfig.USAP_POOL_ENABLED,
-                    Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT));
-            }
+            mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
+                  ZygoteConfig.USAP_POOL_ENABLED,
+                  Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT));
         }
 
         boolean valueChanged = origVal != mUsapPoolEnabled;
@@ -665,6 +660,16 @@
     private boolean fetchUsapPoolEnabledPropWithMinInterval() {
         final long currentTimestamp = SystemClock.elapsedRealtime();
 
+        if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) {
+            // TODO(b/119800099): In jitzygote mode, we want to start using USAP processes
+            // only once the boot classpath has been compiled. There is currently no callback
+            // from the runtime to notify the zygote about end of compilation, so for now just
+            // arbitrarily start USAP processes 15 seconds after boot.
+            if (currentTimestamp <= 15000) {
+                return false;
+            }
+        }
+
         if (mIsFirstPropCheck
                 || (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) {
             mIsFirstPropCheck = false;
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 942bf94..14c299d 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.annotation.Nullable;
 import android.os.IVold;
 
 /**
@@ -101,4 +102,11 @@
      * @param listener The listener that will be notified on reset events.
      */
     public abstract void addResetListener(ResetListener listener);
+
+    /**
+     * Notified when any app op changes so that storage mount points can be updated if the app op
+     * affects them.
+     */
+    public abstract void onAppOpsChanged(int code, int uid,
+            @Nullable String packageName, int mode);
 }
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 02f9925..2b3a2ab 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -591,7 +591,7 @@
     private void registerVolumeGroupCb() {
         if (mVolumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
             mAudioManager.registerVolumeGroupCallback(Runnable::run, mVolumeGroupCallback);
-            mLastProgress = mAudioManager.getVolumeIndexForAttributes(mAttributes);
+            updateSlider();
         }
     }
 
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 22ce39d..fd81178 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -36,7 +36,6 @@
 import android.graphics.ImageDecoder;
 import android.graphics.Point;
 import android.media.ExifInterface;
-import android.media.MediaFile;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -1681,46 +1680,35 @@
     public static AssetFileDescriptor openImageThumbnail(File file) throws FileNotFoundException {
         final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
                 file, ParcelFileDescriptor.MODE_READ_ONLY);
-        Bundle extras = null;
-
         try {
             final ExifInterface exif = new ExifInterface(file.getAbsolutePath());
 
-            switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
-                case ExifInterface.ORIENTATION_ROTATE_90:
-                    extras = new Bundle(1);
-                    extras.putInt(EXTRA_ORIENTATION, 90);
-                    break;
-                case ExifInterface.ORIENTATION_ROTATE_180:
-                    extras = new Bundle(1);
-                    extras.putInt(EXTRA_ORIENTATION, 180);
-                    break;
-                case ExifInterface.ORIENTATION_ROTATE_270:
-                    extras = new Bundle(1);
-                    extras.putInt(EXTRA_ORIENTATION, 270);
-                    break;
-            }
-
             final long[] thumb = exif.getThumbnailRange();
             if (thumb != null) {
+                // If we use thumb to decode, we need to handle the rotation by ourselves.
+                Bundle extras = null;
+                switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
+                    case ExifInterface.ORIENTATION_ROTATE_90:
+                        extras = new Bundle(1);
+                        extras.putInt(EXTRA_ORIENTATION, 90);
+                        break;
+                    case ExifInterface.ORIENTATION_ROTATE_180:
+                        extras = new Bundle(1);
+                        extras.putInt(EXTRA_ORIENTATION, 180);
+                        break;
+                    case ExifInterface.ORIENTATION_ROTATE_270:
+                        extras = new Bundle(1);
+                        extras.putInt(EXTRA_ORIENTATION, 270);
+                        break;
+                }
+
                 return new AssetFileDescriptor(pfd, thumb[0], thumb[1], extras);
             }
         } catch (IOException e) {
         }
 
-        // Use ImageDecoder to do full image decode of heif format file
-        // will have right orientation. So, we don't need to add orientation
-        // information into extras.
-        final String mimeType = MediaFile.getMimeTypeForFile(file.getName());
-        if (mimeType.equals("image/heif")
-                || mimeType.equals("image/heif-sequence")
-                || mimeType.equals("image/heic")
-                || mimeType.equals("image/heic-sequence")) {
-            return new AssetFileDescriptor(pfd, 0 /* startOffset */,
-                    AssetFileDescriptor.UNKNOWN_LENGTH, null /* extras */);
-        }
-
-        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
+        // Do full file decoding, we don't need to handle the orientation
+        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, null);
     }
 
     private static void rethrowIfNecessary(Exception e) throws FileNotFoundException {
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index d507447..9a384c6 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -849,6 +849,8 @@
     public static final String CALL_MEDIASTORE_DOWNLOADS_DELETED = "mediastore_downloads_deleted";
     /** @hide */
     public static final String CALL_CREATE_EXTERNAL_PUBLIC_DIR = "create_external_public_dir";
+    /** @hide */
+    public static final String CALL_REVOKE_MEDIASTORE_URI_PERMS = "revoke_mediastore_uri_perms";
 
     /** @hide */
     public static final String EXTRA_IDS = "ids";
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index aa774a6..2a590ce 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1077,7 +1077,7 @@
          * Relative path of this media item within the storage device where it
          * is persisted. For example, an item stored at
          * {@code /storage/0000-0000/DCIM/Vacation/IMG1024.JPG} would have a
-         * path of {@code DCIM/Vacation}.
+         * path of {@code DCIM/Vacation/}.
          * <p>
          * This value should only be used for organizational purposes, and you
          * should not attempt to construct or access a raw filesystem path using
@@ -3304,6 +3304,14 @@
     @TestApi
     public static @NonNull File getVolumePath(@NonNull String volumeName)
             throws FileNotFoundException {
+        final StorageManager sm = AppGlobals.getInitialApplication()
+                .getSystemService(StorageManager.class);
+        return getVolumePath(sm.getVolumes(), volumeName);
+    }
+
+    /** {@hide} */
+    public static @NonNull File getVolumePath(@NonNull List<VolumeInfo> volumes,
+            @NonNull String volumeName) throws FileNotFoundException {
         if (TextUtils.isEmpty(volumeName)) {
             throw new IllegalArgumentException();
         }
@@ -3312,19 +3320,18 @@
             case VOLUME_INTERNAL:
             case VOLUME_EXTERNAL:
                 throw new FileNotFoundException(volumeName + " has no associated path");
-            case VOLUME_EXTERNAL_PRIMARY:
-                return Environment.getExternalStorageDirectory();
         }
 
-        final StorageManager sm = AppGlobals.getInitialApplication()
-                .getSystemService(StorageManager.class);
-        for (VolumeInfo vi : sm.getVolumes()) {
-            if (Objects.equals(vi.getNormalizedFsUuid(), volumeName)) {
-                final File path = vi.getPathForUser(UserHandle.myUserId());
+        final boolean wantPrimary = VOLUME_EXTERNAL_PRIMARY.equals(volumeName);
+        for (VolumeInfo volume : volumes) {
+            final boolean matchPrimary = wantPrimary
+                    && volume.isPrimary();
+            final boolean matchSecondary = !wantPrimary
+                    && Objects.equals(volume.getNormalizedFsUuid(), volumeName);
+            if (matchPrimary || matchSecondary) {
+                final File path = volume.getPathForUser(UserHandle.myUserId());
                 if (path != null) {
                     return path;
-                } else {
-                    throw new FileNotFoundException("Failed to find path for " + vi);
                 }
             }
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 185d4a2..8096eb1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8836,6 +8836,15 @@
         public static final String AWARE_ENABLED = "aware_enabled";
 
         private static final Validator AWARE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+         * Controls whether aware_lock is enabled.
+         * @hide
+         */
+        public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled";
+
+        private static final Validator AWARE_LOCK_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * This are the settings to be backed up.
          *
@@ -8970,7 +8979,8 @@
             SILENCE_CALL_GESTURE_COUNT,
             SILENCE_TIMER_GESTURE_COUNT,
             DARK_MODE_DIALOG_SEEN,
-            GLOBAL_ACTIONS_PANEL_ENABLED
+            GLOBAL_ACTIONS_PANEL_ENABLED,
+            AWARE_LOCK_ENABLED
         };
 
         /**
@@ -9157,6 +9167,7 @@
             VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
             VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR);
             VALIDATORS.put(GLOBAL_ACTIONS_PANEL_ENABLED, GLOBAL_ACTIONS_PANEL_ENABLED_VALIDATOR);
+            VALIDATORS.put(AWARE_LOCK_ENABLED, AWARE_LOCK_ENABLED_VALIDATOR);
         }
 
         /**
@@ -15382,4 +15393,4 @@
         }
         return packages[0];
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 4fe6c2c..397a6cd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -29,6 +29,7 @@
 import android.annotation.TestApi;
 import android.app.Service;
 import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
@@ -40,6 +41,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.view.contentcapture.ContentCaptureCondition;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureEvent;
@@ -114,6 +116,9 @@
     private Handler mHandler;
     private IContentCaptureServiceCallback mCallback;
 
+    private long mCallerMismatchTimeout = 1000;
+    private long mLastCallerMismatchLog;
+
     /**
      * Binder that receives calls from the system server.
      */
@@ -176,9 +181,10 @@
             new IContentCaptureDirectManager.Stub() {
 
         @Override
-        public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events) {
+        public void sendEvents(@SuppressWarnings("rawtypes") ParceledListSlice events, int reason,
+                ContentCaptureOptions options) {
             mHandler.sendMessage(obtainMessage(ContentCaptureService::handleSendEvents,
-                            ContentCaptureService.this, Binder.getCallingUid(), events));
+                    ContentCaptureService.this, Binder.getCallingUid(), events, reason, options));
         }
     };
 
@@ -424,14 +430,23 @@
     }
 
     private void handleSendEvents(int uid,
-            @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) {
+            @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents, int reason,
+            @Nullable ContentCaptureOptions options) {
+        final List<ContentCaptureEvent> events = parceledEvents.getList();
+        if (events.isEmpty()) {
+            Log.w(TAG, "handleSendEvents() received empty list of events");
+            return;
+        }
+
+        // Metrics.
+        final FlushMetrics metrics = new FlushMetrics();
+        ComponentName activityComponent = null;
 
         // Most events belong to the same session, so we can keep a reference to the last one
         // to avoid creating too many ContentCaptureSessionId objects
         int lastSessionId = NO_SESSION_ID;
         ContentCaptureSessionId sessionId = null;
 
-        final List<ContentCaptureEvent> events = parceledEvents.getList();
         for (int i = 0; i < events.size(); i++) {
             final ContentCaptureEvent event = events.get(i);
             if (!handleIsRightCallerFor(event, uid)) continue;
@@ -439,22 +454,44 @@
             if (sessionIdInt != lastSessionId) {
                 sessionId = new ContentCaptureSessionId(sessionIdInt);
                 lastSessionId = sessionIdInt;
+                if (i != 0) {
+                    writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason);
+                    metrics.reset();
+                }
+            }
+            final ContentCaptureContext clientContext = event.getContentCaptureContext();
+            if (activityComponent == null && clientContext != null) {
+                activityComponent = clientContext.getActivityComponent();
             }
             switch (event.getType()) {
                 case ContentCaptureEvent.TYPE_SESSION_STARTED:
-                    final ContentCaptureContext clientContext = event.getContentCaptureContext();
                     clientContext.setParentSessionId(event.getParentSessionId());
                     mSessionUids.put(sessionIdInt, uid);
                     onCreateContentCaptureSession(clientContext, sessionId);
+                    metrics.sessionStarted++;
                     break;
                 case ContentCaptureEvent.TYPE_SESSION_FINISHED:
                     mSessionUids.delete(sessionIdInt);
                     onDestroyContentCaptureSession(sessionId);
+                    metrics.sessionFinished++;
+                    break;
+                case ContentCaptureEvent.TYPE_VIEW_APPEARED:
+                    onContentCaptureEvent(sessionId, event);
+                    metrics.viewAppearedCount++;
+                    break;
+                case ContentCaptureEvent.TYPE_VIEW_DISAPPEARED:
+                    onContentCaptureEvent(sessionId, event);
+                    metrics.viewDisappearedCount++;
+                    break;
+                case ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED:
+                    onContentCaptureEvent(sessionId, event);
+                    metrics.viewTextChangedCount++;
                     break;
                 default:
                     onContentCaptureEvent(sessionId, event);
             }
         }
+        writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason);
     }
 
     private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) {
@@ -499,7 +536,13 @@
         if (rightUid != uid) {
             Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to "
                     + rightUid);
-            //TODO(b/111276913): log metrics as this could be a malicious app forging a sessionId
+            long now = System.currentTimeMillis();
+            if (now - mLastCallerMismatchLog > mCallerMismatchTimeout) {
+                StatsLog.write(StatsLog.CONTENT_CAPTURE_CALLER_MISMATCH_REPORTED,
+                        getPackageManager().getNameForUid(rightUid),
+                        getPackageManager().getNameForUid(uid));
+                mLastCallerMismatchLog = now;
+            }
             return false;
         }
         return true;
@@ -530,4 +573,22 @@
             Slog.w(TAG, "Error async reporting result to client: " + e);
         }
     }
+
+    /**
+     * Logs the metrics for content capture events flushing.
+     */
+    private void writeFlushMetrics(int sessionId, @Nullable ComponentName app,
+            @NonNull FlushMetrics flushMetrics, @Nullable ContentCaptureOptions options,
+            int flushReason) {
+        if (mCallback == null) {
+            Log.w(TAG, "writeSessionFlush(): no server callback");
+            return;
+        }
+
+        try {
+            mCallback.writeSessionFlush(sessionId, app, flushMetrics, options, flushReason);
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to write flush metrics: " + e);
+        }
+    }
 }
diff --git a/core/java/android/service/contentcapture/FlushMetrics.aidl b/core/java/android/service/contentcapture/FlushMetrics.aidl
new file mode 100644
index 0000000..d0b935f
--- /dev/null
+++ b/core/java/android/service/contentcapture/FlushMetrics.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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.contentcapture;
+
+/* @hide */
+parcelable FlushMetrics;
diff --git a/core/java/android/service/contentcapture/FlushMetrics.java b/core/java/android/service/contentcapture/FlushMetrics.java
new file mode 100644
index 0000000..01f3a12
--- /dev/null
+++ b/core/java/android/service/contentcapture/FlushMetrics.java
@@ -0,0 +1,79 @@
+/*
+ * 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.contentcapture;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Holds metrics for content capture events flushing.
+ *
+ * @hide
+ */
+public final class FlushMetrics implements Parcelable {
+    public int viewAppearedCount;
+    public int viewDisappearedCount;
+    public int viewTextChangedCount;
+    public int sessionStarted;
+    public int sessionFinished;
+
+    /**
+     * Resets all flush metrics.
+     */
+    public void reset() {
+        viewAppearedCount = 0;
+        viewDisappearedCount = 0;
+        viewTextChangedCount = 0;
+        sessionStarted = 0;
+        sessionFinished = 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(sessionStarted);
+        out.writeInt(sessionFinished);
+        out.writeInt(viewAppearedCount);
+        out.writeInt(viewDisappearedCount);
+        out.writeInt(viewTextChangedCount);
+    }
+
+    @NonNull
+    public static final Creator<FlushMetrics> CREATOR = new Creator<FlushMetrics>() {
+        @NonNull
+        @Override
+        public FlushMetrics createFromParcel(Parcel in) {
+            final FlushMetrics flushMetrics = new FlushMetrics();
+            flushMetrics.sessionStarted = in.readInt();
+            flushMetrics.sessionFinished = in.readInt();
+            flushMetrics.viewAppearedCount = in.readInt();
+            flushMetrics.viewDisappearedCount = in.readInt();
+            flushMetrics.viewTextChangedCount = in.readInt();
+            return flushMetrics;
+        }
+
+        @Override
+        public FlushMetrics[] newArray(int size) {
+            return new FlushMetrics[size];
+        }
+    };
+}
diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
index 0550ad3..ea6e76b 100644
--- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
+++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl
@@ -18,6 +18,8 @@
 
 import android.content.ComponentName;
 import android.view.contentcapture.ContentCaptureCondition;
+import android.service.contentcapture.FlushMetrics;
+import android.content.ContentCaptureOptions;
 
 import java.util.List;
 
@@ -30,4 +32,8 @@
     void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities);
     void setContentCaptureConditions(String packageName, in List<ContentCaptureCondition> conditions);
     void disableSelf();
- }
+
+    // Logs aggregated content capture flush metrics to Statsd
+    void writeSessionFlush(int sessionId, in ComponentName app, in FlushMetrics flushMetrics,
+            in ContentCaptureOptions options, int flushReason);
+}
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 4191040..79c2152 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -65,8 +65,8 @@
     private CharSequence mLabel;
     private CharSequence mSubtitle;
     private CharSequence mContentDescription;
-    // Default to active until clients of the new API can update.
-    private int mState = STATE_ACTIVE;
+    // Default to inactive until clients of the new API can update.
+    private int mState = STATE_INACTIVE;
 
     private IQSService mService;
 
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
new file mode 100644
index 0000000..aa6123f
--- /dev/null
+++ b/core/java/android/service/wallpaper/Android.bp
@@ -0,0 +1,12 @@
+android_library {
+
+    name: "WallpaperSharedLib",
+    srcs: [
+        "*.java",
+        "I*.aidl",
+    ],
+
+    // 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/AndroidManifest.xml b/core/java/android/service/wallpaper/AndroidManifest.xml
new file mode 100644
index 0000000..f1bdb76
--- /dev/null
+++ b/core/java/android/service/wallpaper/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.service.wallpaper">
+
+    <uses-sdk android:minSdkVersion="29" />
+</manifest>
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index 876c64e..04fa110 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -73,22 +73,20 @@
      * Returns true if the character is a new emoji still not supported in our version of ICU.
      */
     public static boolean isNewEmoji(int c) {
-        // Emoji characters new in Unicode emoji 11
-        // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
-        // TODO: Remove once emoji-data.text 11 is in ICU or update to 11.
-        if (c < 0x1F6F9 || c > 0x1F9FF) {
+        // Emoji characters new in Unicode emoji 12
+        // From https://www.unicode.org/Public/emoji/12.0/emoji-data.txt
+        // TODO: Remove once emoji-data.text 12 is in ICU or update to 12.
+        if (c < 0x1F6D5 || c > 0x1FA95) {
             // Optimization for characters outside the new emoji range.
             return false;
         }
-        return c == 0x265F || c == 0x267E || c == 0x1F6F9 || c == 0x1F97A
-                || (0x1F94D <= c && c <= 0x1F94F)
-                || (0x1F96C <= c && c <= 0x1F970)
-                || (0x1F973 <= c && c <= 0x1F976)
-                || (0x1F97C <= c && c <= 0x1F97F)
-                || (0x1F998 <= c && c <= 0x1F9A2)
-                || (0x1F9B0 <= c && c <= 0x1F9B9)
-                || (0x1F9C1 <= c && c <= 0x1F9C2)
-                || (0x1F9E7 <= c && c <= 0x1F9FF);
+        return c == 0x1F6D5 || c == 0x1F6FA || c == 0x1F93F || c == 0x1F971 || c == 0x1F97B
+                || (0x1F7E0 <= c && c <= 0x1F7EB) || (0x1F90D <= c && c <= 0x1F90F)
+                || (0x1F9A5 <= c && c <= 0x1F9AA) || (0x1F9AE <= c && c <= 0x1F9AF)
+                || (0x1F9BA <= c && c <= 0x1F9BF) || (0x1F9C3 <= c && c <= 0x1F9CA)
+                || (0x1F9CD <= c && c <= 0x1F9CF) || (0x1FA70 <= c && c <= 0x1FA73)
+                || (0x1FA78 <= c && c <= 0x1FA7A) || (0x1FA80 <= c && c <= 0x1FA82)
+                || (0x1FA90 <= c && c <= 0x1FA95);
     }
 
     /**
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 80b1607..7d287e3 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -175,12 +175,10 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(mFd);
-        try {
-            // Don't let writing to a parcel to close our fd - plz
-            parcel.writeParcelable(pfd, flags & ~Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
-        } finally {
-            pfd.detachFd();
+        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(mFd)) {
+            parcel.writeParcelable(pfd, flags);
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
         }
     }
 
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d269323..caab00c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -254,6 +254,8 @@
      */
     boolean startMovingTask(IWindow window, float startX, float startY);
 
+    void finishMovingTask(IWindow window);
+
     void updatePointerIcon(IWindow window);
 
     /**
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index c2f9601..93f52a0 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -213,7 +213,7 @@
         // it with the final value here.
         if (mRenderProperty == RenderNodeAnimator.ALPHA) {
             mViewTarget.ensureTransformationInfo();
-            mViewTarget.mTransformationInfo.mAlpha = mFinalValue;
+            mViewTarget.setAlphaInternal(mFinalValue);
         }
 
         moveToRunningState();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7fdda2a..add7376b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -24,8 +24,11 @@
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
 import android.content.res.Configuration;
+import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.HardwareRenderer;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
@@ -127,6 +130,8 @@
     final Rect mTmpRect = new Rect();
     final Configuration mConfiguration = new Configuration();
 
+    Paint mRoundedViewportPaint;
+
     int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -180,6 +185,7 @@
     int mWindowSpaceTop = -1;
     int mSurfaceWidth = -1;
     int mSurfaceHeight = -1;
+    float mCornerRadius;
     @UnsupportedAppUsage
     int mFormat = -1;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -196,6 +202,29 @@
 
     private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
 
+    /**
+     * A callback which reflects an alpha value of this view onto the underlying surfaces.
+     *
+     * <p class="note"><strong>Note:</strong> This doesn't have to be defined as a member variable,
+     * but can be defined as an inline lambda when calling ViewRootImpl#registerRtFrameCallback().
+     * However when we do so, the callback is triggered only for a few times and stops working for
+     * some reason. It's suspected that there is a problem around garbage collection, and until
+     * the cause is fixed, we will keep this callback in a member variable.</p>
+    */
+    private HardwareRenderer.FrameDrawingCallback mSetSurfaceAlphaCallback = frame -> {
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+            // In this case, the alpha value is reflected on the screen in #updateSurface() later.
+            return;
+        }
+
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.setAlpha(mSurfaceControl, getAlpha());
+        t.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, frame);
+        t.setEarlyWakeup();
+        t.apply();
+    };
+
     public SurfaceView(Context context) {
         this(context, null);
     }
@@ -283,6 +312,17 @@
         updateSurface();
     }
 
+    @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null) {
+            return;
+        }
+        viewRoot.registerRtFrameCallback(mSetSurfaceAlphaCallback);
+        invalidate();
+    }
+
     private void performDrawFinished() {
         if (mPendingReportDraws > 0) {
             mDrawFinished = true;
@@ -395,7 +435,7 @@
             // draw() is not called when SKIP_DRAW is set
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
                 // punch a whole in the view-hierarchy below us
-                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+                clearSurfaceViewPort(canvas);
             }
         }
         super.draw(canvas);
@@ -407,12 +447,39 @@
             // draw() is not called when SKIP_DRAW is set
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                 // punch a whole in the view-hierarchy below us
-                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+                clearSurfaceViewPort(canvas);
             }
         }
         super.dispatchDraw(canvas);
     }
 
+    private void clearSurfaceViewPort(Canvas canvas) {
+        if (mCornerRadius > 0f) {
+            canvas.getClipBounds(mTmpRect);
+            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
+                    mCornerRadius, mCornerRadius, mRoundedViewportPaint);
+        } else {
+            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        }
+    }
+
+    /**
+     * Sets the corner radius for the SurfaceView. This will round both the corners of the
+     * underlying surface, as well as the corners of the hole created to expose the surface.
+     *
+     * @param cornerRadius the new radius of the corners in pixels
+     * @hide
+     */
+    public void setCornerRadius(float cornerRadius) {
+        mCornerRadius = cornerRadius;
+        if (mCornerRadius > 0f && mRoundedViewportPaint == null) {
+            mRoundedViewportPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mRoundedViewportPaint.setBlendMode(BlendMode.CLEAR);
+            mRoundedViewportPaint.setColor(0);
+        }
+        invalidate();
+    }
+
     /**
      * Control whether the surface view's surface is placed on top of another
      * regular surface view in the window (but still behind the window itself).
@@ -615,6 +682,13 @@
                         }
                         updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
 
+                        // Alpha value change is handled in setAlpha() directly using a local
+                        // transaction. However it can happen that setAlpha() is called while
+                        // local transactions cannot be applied, so the value is stored in a View
+                        // but not yet reflected on the Surface.
+                        mSurfaceControl.setAlpha(getAlpha());
+                        mBackgroundControl.setAlpha(getAlpha());
+
                         // While creating the surface, we will set it's initial
                         // geometry. Outside of that though, we should generally
                         // leave it to the RenderThread.
@@ -634,6 +708,7 @@
                             // size.
                             mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
                         }
+                        mSurfaceControl.setCornerRadius(mCornerRadius);
                         if (sizeChanged && !creating) {
                             mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
                         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 628451d..04b62d8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4242,7 +4242,7 @@
          * completely transparent and 1 means completely opaque.
          */
         @ViewDebug.ExportedProperty
-        float mAlpha = 1f;
+        private float mAlpha = 1f;
 
         /**
          * The opacity of the view as manipulated by the Fade transition. This is a
@@ -9030,6 +9030,7 @@
     /**
      * @hide
      */
+    @TestApi
     public boolean isAutofilled() {
         return (mPrivateFlags3 & PFLAG3_IS_AUTOFILLED) != 0;
     }
@@ -9522,18 +9523,15 @@
      * <pre>
      *   ContentCaptureSession mainSession = rootView.getContentCaptureSession();
      *   mainSession.setContentCaptureContext(ContentCaptureContext.forLocusId(Uri.parse(myUrl));
-     * <pre>
+     * </pre>
      *
      * <p>Then if the page had an {@code IFRAME}, you would create a new session for it:
      *
-     * <p>For example, if your activity is associated with a web domain, you could create a session
-     * {@code onCreate()} and associate it with the root view of the activity:
-     *
      * <pre>
      *   ContentCaptureSession iframeSession = mainSession.createContentCaptureSession(
      *       ContentCaptureContext.forLocusId(Uri.parse(iframeUrl)));
      *   iframeView.setContentCaptureSession(iframeSession);
-     * <pre>
+     * </pre>
      *
      * @param contentCaptureSession a session created by
      * {@link ContentCaptureSession#createContentCaptureSession(
@@ -16702,7 +16700,7 @@
         return false;
     }
 
-    private void setAlphaInternal(float alpha) {
+    void setAlphaInternal(float alpha) {
         float oldAlpha = mTransformationInfo.mAlpha;
         mTransformationInfo.mAlpha = alpha;
         // Report visibility changes, which can affect children, to accessibility
@@ -26037,6 +26035,21 @@
     }
 
     /**
+     * Finish a window move task.
+     * @hide
+     */
+    public void finishMovingTask() {
+        if (ViewDebug.DEBUG_POSITIONING) {
+            Log.d(VIEW_LOG_TAG, "finishMovingTask");
+        }
+        try {
+            mAttachInfo.mSession.finishMovingTask(mAttachInfo.mWindow);
+        } catch (RemoteException e) {
+            Log.e(VIEW_LOG_TAG, "Unable to finish moving", e);
+        }
+    }
+
+    /**
      * Handles drag events sent by the system following a call to
      * {@link android.view.View#startDragAndDrop(ClipData,DragShadowBuilder,Object,int)
      * startDragAndDrop()}.
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index afee1e5..957673d 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -972,7 +972,6 @@
      * @param value The value to set the property to
      */
     private void setValue(int propertyConstant, float value) {
-        final View.TransformationInfo info = mView.mTransformationInfo;
         final RenderNode renderNode = mView.mRenderNode;
         switch (propertyConstant) {
             case TRANSLATION_X:
@@ -1009,7 +1008,7 @@
                 renderNode.setTranslationZ(value - renderNode.getElevation());
                 break;
             case ALPHA:
-                info.mAlpha = value;
+                mView.setAlphaInternal(value);
                 renderNode.setAlpha(value);
                 break;
         }
@@ -1047,7 +1046,7 @@
             case Z:
                 return node.getElevation() + node.getTranslationZ();
             case ALPHA:
-                return mView.mTransformationInfo.mAlpha;
+                return mView.getAlpha();
         }
         return 0;
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a25f2ee..1f89de8 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -261,6 +261,13 @@
     int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
 
     /**
+     * A display which can only contain one task is being shown because the first activity is
+     * started or it's being turned on.
+     * @hide
+     */
+    int TRANSIT_SHOW_SINGLE_TASK_DISPLAY = 28;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_" }, value = {
@@ -287,7 +294,8 @@
             TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
             TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
             TRANSIT_CRASHING_ACTIVITY_CLOSE,
-            TRANSIT_TASK_CHANGE_WINDOWING_MODE
+            TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+            TRANSIT_SHOW_SINGLE_TASK_DISPLAY
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionType {}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index c36b894..1f7ae0e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1204,10 +1204,6 @@
         AutofillValue value = null;
 
         synchronized (mLock) {
-            if (mForAugmentedAutofillOnly) {
-                if (sVerbose) Log.v(TAG,  "notifyValueChanged(): ignoring on augmented only mode");
-                return;
-            }
             // If the session is gone some fields might still be highlighted, hence we have to
             // remove the isAutofilled property even if no sessions are active.
             if (mLastAutofilledData == null) {
@@ -1229,6 +1225,13 @@
                 }
             }
 
+            if (mForAugmentedAutofillOnly) {
+                if (sVerbose) {
+                    Log.v(TAG,  "notifyValueChanged(): not notifying system server on "
+                            + "augmented-only mode");
+                }
+                return;
+            }
             if (!mEnabled || !isActiveLocked()) {
                 if (sVerbose) {
                     Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 210dea1..31cb4e2 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -41,7 +41,7 @@
 import java.util.Random;
 
 /**
- * Session used to notify the Android system about events associated with views.
+ * Session used when notifying the Android system about events associated with views.
  */
 public abstract class ContentCaptureSession implements AutoCloseable {
 
diff --git a/core/java/android/view/contentcapture/DataRemovalRequest.java b/core/java/android/view/contentcapture/DataRemovalRequest.java
index 3792846b..68d4695 100644
--- a/core/java/android/view/contentcapture/DataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/DataRemovalRequest.java
@@ -32,6 +32,9 @@
 
 /**
  * Class used by apps to remove content capture data associated with {@link LocusId LocusIds}.
+ *
+ * <p>An app which has tagged data with a LocusId can therefore delete them later. This is intended
+ * to let apps propagate deletions of user data into the operating system.
  */
 public final class DataRemovalRequest implements Parcelable {
 
diff --git a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
index 8d8117b..959bf13 100644
--- a/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl
@@ -18,6 +18,7 @@
 
 import android.content.pm.ParceledListSlice;
 import android.view.contentcapture.ContentCaptureEvent;
+import android.content.ContentCaptureOptions;
 
 /**
   * Interface between an app (ContentCaptureManager / ContentCaptureSession) and the app providing
@@ -26,5 +27,6 @@
   * @hide
   */
 oneway interface IContentCaptureDirectManager {
-    void sendEvents(in ParceledListSlice events);
+    // reason and options are used only for metrics logging.
+    void sendEvents(in ParceledListSlice events, int reason, in ContentCaptureOptions options);
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 7241664..c5a5f73 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -498,7 +498,7 @@
             }
 
             final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
-            mDirectServiceInterface.sendEvents(events);
+            mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
         } catch (RemoteException e) {
             Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
                     + ": " + e);
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index 48a568a..20cc944 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -80,12 +80,16 @@
                 .addTaggedData(INDEX, event.getEventIndex())
                 .addTaggedData(WIDGET_TYPE, event.getWidgetType())
                 .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
-                .addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getResultId()))
                 .addTaggedData(ENTITY_TYPE, event.getEntityType())
-                .addTaggedData(SMART_START, event.getSmartStart())
-                .addTaggedData(SMART_END, event.getSmartEnd())
                 .addTaggedData(EVENT_START, event.getStart())
                 .addTaggedData(EVENT_END, event.getEnd());
+        if (isPlatformLocalTextClassifierSmartSelection(event.getResultId())) {
+            // Ensure result id and smart indices are only set for events with smart selection from
+            // the platform's textclassifier.
+            log.addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getResultId()))
+                    .addTaggedData(SMART_START, event.getSmartStart())
+                    .addTaggedData(SMART_END, event.getSmartEnd());
+        }
         if (event.getSessionId() != null) {
             log.addTaggedData(SESSION_ID, event.getSessionId().flattenToString());
         }
@@ -193,6 +197,11 @@
         }
     }
 
+    static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
+        return SelectionSessionLogger.CLASSIFIER_ID.equals(
+                SelectionSessionLogger.SignatureParser.getClassifierId(signature));
+    }
+
     private static void debugLog(LogMaker log) {
         if (!Log.ENABLE_FULL_LOGGING) {
             return;
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index ba1287f..15f54ef 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -158,11 +158,11 @@
                     mStartEvent = event;
                     break;
                 case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
-                case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                case SelectionEvent.EVENT_SMART_SELECTION_MULTI:   // fall through
+                case SelectionEvent.EVENT_AUTO_SELECTION:
                     mSmartEvent = event;
                     break;
-                case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
-                case SelectionEvent.EVENT_AUTO_SELECTION:
+                case SelectionEvent.EVENT_SELECTION_MODIFIED:
                     if (mPrevEvent != null
                             && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
                             && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
@@ -215,7 +215,8 @@
                 case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
                 case SelectionEvent.EVENT_SMART_SELECTION_MULTI:  // fall through
                 case SelectionEvent.EVENT_AUTO_SELECTION:
-                    if (isPlatformLocalTextClassifierSmartSelection(event.getResultId())) {
+                    if (SelectionSessionLogger.isPlatformLocalTextClassifierSmartSelection(
+                            event.getResultId())) {
                         if (event.getAbsoluteEnd() - event.getAbsoluteStart() > 1) {
                             event.setEventType(SelectionEvent.EVENT_SMART_SELECTION_MULTI);
                         } else {
@@ -229,10 +230,5 @@
                     return;
             }
         }
-
-        private static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
-            return SelectionSessionLogger.CLASSIFIER_ID.equals(
-                    SelectionSessionLogger.SignatureParser.getClassifierId(signature));
-        }
     }
 }
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 616c4b5..b7dd88b 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -175,6 +175,9 @@
             if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
                 final String timeZone = intent.getStringExtra("time-zone");
                 createTime(timeZone);
+            } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
+                    || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
+                return;
             }
             onTimeChanged();
         }
@@ -642,12 +645,9 @@
      */
     @UnsupportedAppUsage
     private void onTimeChanged() {
-        // mShouldRunTicker always equals the last value passed into onVisibilityAggregated
-        if (mShouldRunTicker) {
-            mTime.setTimeInMillis(System.currentTimeMillis());
-            setText(DateFormat.format(mFormat, mTime));
-            setContentDescription(DateFormat.format(mDescFormat, mTime));
-        }
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        setText(DateFormat.format(mFormat, mTime));
+        setContentDescription(DateFormat.format(mDescFormat, mTime));
     }
 
     /** @hide */
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cf7d09d..cdbec29 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11266,13 +11266,23 @@
 
     @Nullable
     final TextServicesManager getTextServicesManagerForUser() {
+        return getServiceManagerForUser("android", TextServicesManager.class);
+    }
+
+    @Nullable
+    final ClipboardManager getClipboardManagerForUser() {
+        return getServiceManagerForUser(getContext().getPackageName(), ClipboardManager.class);
+    }
+
+    @Nullable
+    final <T> T getServiceManagerForUser(String packageName, Class<T> managerClazz) {
         if (mTextOperationUser == null) {
-            return getContext().getSystemService(TextServicesManager.class);
+            return getContext().getSystemService(managerClazz);
         }
         try {
-            return getContext().createPackageContextAsUser(
-                    "android", 0 /* flags */, mTextOperationUser)
-                    .getSystemService(TextServicesManager.class);
+            Context context = getContext().createPackageContextAsUser(
+                    packageName, 0 /* flags */, mTextOperationUser);
+            return context.getSystemService(managerClazz);
         } catch (PackageManager.NameNotFoundException e) {
             return null;
         }
@@ -12569,8 +12579,7 @@
                 && mEditor != null && mEditor.mKeyListener != null
                 && getSelectionStart() >= 0
                 && getSelectionEnd() >= 0
-                && ((ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE))
-                        .hasPrimaryClip());
+                && getClipboardManagerForUser().hasPrimaryClip());
     }
 
     boolean canPasteAsPlainText() {
@@ -12578,9 +12587,7 @@
             return false;
         }
 
-        final ClipData clipData =
-                ((ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE))
-                        .getPrimaryClip();
+        final ClipData clipData = getClipboardManagerForUser().getPrimaryClip();
         final ClipDescription description = clipData.getDescription();
         final boolean isPlainType = description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
         final CharSequence text = clipData.getItemAt(0).getText();
@@ -12623,8 +12630,7 @@
      * Paste clipboard content between min and max positions.
      */
     private void paste(int min, int max, boolean withFormatting) {
-        ClipboardManager clipboard =
-                (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipboardManager clipboard = getClipboardManagerForUser();
         ClipData clip = clipboard.getPrimaryClip();
         if (clip != null) {
             boolean didFirst = false;
@@ -12667,8 +12673,7 @@
 
     @CheckResult
     private boolean setPrimaryClip(ClipData clip) {
-        ClipboardManager clipboard =
-                (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+        ClipboardManager clipboard = getClipboardManagerForUser();
         try {
             clipboard.setPrimaryClip(clip);
         } catch (Throwable t) {
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index 3fddfc8..04ad7e9 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -99,9 +99,13 @@
         }
         List<AppTarget> appTargets = new ArrayList<>();
         for (ResolvedComponentInfo target : targets) {
-            appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString()))
-                    .setTarget(target.name.getPackageName(), mUser)
-                    .setClassName(target.name.getClassName()).build());
+            appTargets.add(
+                    new AppTarget.Builder(
+                        new AppTargetId(target.name.flattenToString()),
+                            target.name.getPackageName(),
+                            mUser)
+                    .setClassName(target.name.getClassName())
+                    .build());
         }
         mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(),
                 sortedAppTargets -> {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5924fb15..a88c51a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -32,7 +32,6 @@
 import android.app.prediction.AppPredictor;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.ComponentName;
@@ -125,7 +124,9 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -162,6 +163,7 @@
     public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
     private AppPredictor mAppPredictor;
     private AppPredictor.Callback mAppPredictorCallback;
+    private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
 
     /**
      * If set to true, use ShortcutManager to retrieve the matching direct share targets, instead of
@@ -235,7 +237,6 @@
 
     private boolean mListViewDataChanged = false;
 
-
     @Retention(SOURCE)
     @IntDef({CONTENT_PREVIEW_FILE, CONTENT_PREVIEW_IMAGE, CONTENT_PREVIEW_TEXT})
     private @interface ContentPreviewType {
@@ -454,6 +455,7 @@
 
         AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
         if (appPredictor != null) {
+            mDirectShareAppTargetCache = new HashMap<>();
             mAppPredictorCallback = resultList -> {
                 if (isFinishing() || isDestroyed()) {
                     return;
@@ -480,8 +482,7 @@
                             new ComponentName(
                                 appTarget.getPackageName(), appTarget.getClassName())));
                 }
-                sendShareShortcutInfoList(shareShortcutInfos, driList);
-                sendShortcutManagerShareTargetResultCompleted();
+                sendShareShortcutInfoList(shareShortcutInfos, driList, resultList);
             };
             appPredictor
                 .registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback);
@@ -1318,13 +1319,14 @@
         AsyncTask.execute(() -> {
             ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
             List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter);
-            sendShareShortcutInfoList(resultList, driList);
+            sendShareShortcutInfoList(resultList, driList, null);
         });
     }
 
     private void sendShareShortcutInfoList(
                 List<ShortcutManager.ShareShortcutInfo> resultList,
-                List<DisplayResolveInfo> driList) {
+                List<DisplayResolveInfo> driList,
+                @Nullable List<AppTarget> appTargets) {
         // Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
         // for direct share targets. After ShareSheet is refactored we should use the
         // ShareShortcutInfos directly.
@@ -1334,7 +1336,13 @@
             for (int j = 0; j < resultList.size(); j++) {
                 if (driList.get(i).getResolvedComponentName().equals(
                             resultList.get(j).getTargetComponent())) {
-                    chooserTargets.add(convertToChooserTarget(resultList.get(j)));
+                    ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j);
+                    ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo);
+                    chooserTargets.add(chooserTarget);
+                    if (mDirectShareAppTargetCache != null && appTargets != null) {
+                        // Note that appTargets.size() == resultList.size() is always true.
+                        mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j));
+                    }
                 }
             }
             if (chooserTargets.isEmpty()) {
@@ -1442,33 +1450,25 @@
     }
 
     private void sendClickToAppPredictor(TargetInfo targetInfo) {
-        AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
-        if (appPredictor == null) {
+        AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled();
+        if (directShareAppPredictor == null) {
             return;
         }
         if (!(targetInfo instanceof ChooserTargetInfo)) {
             return;
         }
         ChooserTarget chooserTarget = ((ChooserTargetInfo) targetInfo).getChooserTarget();
-        ComponentName componentName = chooserTarget.getComponentName();
-        Bundle extras = chooserTarget.getIntentExtras();
-        if (extras == null) {
-            return;
+        AppTarget appTarget = null;
+        if (mDirectShareAppTargetCache != null) {
+            appTarget = mDirectShareAppTargetCache.get(chooserTarget);
         }
-        String shortcutId = extras.getString(Intent.EXTRA_SHORTCUT_ID);
-        if (shortcutId == null) {
-            return;
+        // This is a direct share click that was provided by the APS
+        if (appTarget != null) {
+            directShareAppPredictor.notifyAppTargetEvent(
+                    new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+                        .setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE)
+                        .build());
         }
-        appPredictor.notifyAppTargetEvent(
-                new AppTargetEvent.Builder(
-                    // TODO(b/124404997) Send full shortcut info, not just Id with AppTargetId.
-                    new AppTarget.Builder(new AppTargetId(shortcutId),
-                            componentName.getPackageName(), getUser())
-                        .setClassName(componentName.getClassName())
-                        .build(),
-                    AppTargetEvent.ACTION_LAUNCH)
-                    .setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE)
-                    .build());
     }
 
     @Nullable
@@ -2035,7 +2035,8 @@
             return;
         }
 
-        if (mChooserRowAdapter.calculateChooserTargetWidth(right - left)
+        int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight();
+        if (mChooserRowAdapter.calculateChooserTargetWidth(availableWidth)
                 || mAdapterView.getAdapter() == null) {
             mAdapterView.setAdapter(mChooserRowAdapter);
 
@@ -2044,7 +2045,9 @@
                     return;
                 }
 
-                int offset = 0;
+                final int bottomInset = mSystemWindowInsets != null
+                                            ? mSystemWindowInsets.bottom : 0;
+                int offset = bottomInset;
                 int rowsToShow = mChooserRowAdapter.getContentPreviewRowCount()
                         + mChooserRowAdapter.getProfileRowCount()
                         + mChooserRowAdapter.getServiceTargetRowCount()
@@ -2059,7 +2062,7 @@
                 // still zero? then use a default height and leave, which
                 // can happen when there are no targets to show
                 if (rowsToShow == 0) {
-                    offset = getResources().getDimensionPixelSize(
+                    offset += getResources().getDimensionPixelSize(
                             R.dimen.chooser_max_collapsed_height);
                     mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
                     return;
@@ -2084,8 +2087,9 @@
                     // make sure to leave room for direct share 4->8 expansion
                     int requiredExpansionHeight =
                             (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
+                    int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0;
                     int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
-                                        - requiredExpansionHeight;
+                                        - requiredExpansionHeight - topInset - bottomInset;
 
                     offset = Math.min(offset, minHeight);
                 }
@@ -2655,7 +2659,7 @@
         @Override
         public boolean isEnabled(int position) {
             int viewType = getItemViewType(position);
-            if (viewType == VIEW_TYPE_CONTENT_PREVIEW) {
+            if (viewType == VIEW_TYPE_CONTENT_PREVIEW || viewType == VIEW_TYPE_AZ_LABEL) {
                 return false;
             }
             return true;
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 114d31f..d60d543 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -71,8 +71,8 @@
 
     void noteSyncStart(String name, int uid);
     void noteSyncFinish(String name, int uid);
-    void noteJobStart(String name, int uid);
-    void noteJobFinish(String name, int uid, int stopReason);
+    void noteJobStart(String name, int uid, int standbyBucket, int jobid);
+    void noteJobFinish(String name, int uid, int stopReason, int standbyBucket, int jobid);
 
     void noteStartWakelock(int uid, int pid, String name, String historyName,
             int type, boolean unimportantForLogging);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3ea746d..a5daa0a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -46,6 +46,7 @@
 import android.graphics.Bitmap;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Insets;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -67,12 +68,15 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowInsets;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.ListView;
+import android.widget.Space;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -136,6 +140,9 @@
 
     private ColorMatrixColorFilter mSuspendedMatrixColorFilter;
 
+    protected Insets mSystemWindowInsets = null;
+    private Space mFooterSpacer = null;
+
     /** See {@link #setRetainInOnStop}. */
     private boolean mRetainInOnStop;
 
@@ -329,6 +336,11 @@
             if (isVoiceInteraction()) {
                 rdl.setCollapsed(false);
             }
+
+            rdl.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+            rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
+
             mResolverDrawerLayout = rdl;
         }
 
@@ -364,10 +376,38 @@
         finish();
     }
 
+    protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+        mSystemWindowInsets = insets.getSystemWindowInsets();
+
+        mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
+                mSystemWindowInsets.right, 0);
+
+        View emptyView = findViewById(R.id.empty);
+        emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+                + getResources().getDimensionPixelSize(
+                        R.dimen.chooser_edge_margin_normal) * 2);
+
+        if (mFooterSpacer == null) {
+            mFooterSpacer = new Space(getApplicationContext());
+        } else {
+            ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+        }
+        mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+                mSystemWindowInsets.bottom));
+        ((ListView) mAdapterView).addFooterView(mFooterSpacer);
+
+        return insets.consumeSystemWindowInsets();
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         mAdapter.handlePackagesChanged();
+
+        if (mSystemWindowInsets != null) {
+            mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
+                    mSystemWindowInsets.right, 0);
+        }
     }
 
     private void initSuspendedColorMatrix() {
@@ -1277,6 +1317,10 @@
         final ViewGroup buttonLayout = findViewById(R.id.button_bar);
         if (buttonLayout != null) {
             buttonLayout.setVisibility(View.VISIBLE);
+            int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+            buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+                    buttonLayout.getPaddingRight(), buttonLayout.getPaddingBottom() + inset);
+
             mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
             mSettingsButton = (Button) buttonLayout.findViewById(R.id.button_app_settings);
             mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 6d0a864..14fe6ab 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -106,5 +106,12 @@
      */
     public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
 
+    // Flag related to Privacy Indicators
+
+    /**
+     * Whether the Permissions Hub is showing.
+     */
+    public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
+
     private SystemUiDeviceConfigFlags() { }
 }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 9a10210..1a16150 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -126,7 +126,7 @@
     // The height of a window which has not in DIP.
     private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
 
-    private static final int SCRIM_LIGHT = 0x99ffffff; // 60% white
+    private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white
 
     public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
             new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 7e501d2..c715577 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -167,4 +167,9 @@
      * Notifies System UI that the display is ready to show system decorations.
      */
     void onDisplayReady(int displayId);
+
+    /**
+     * Notifies System UI whether the recents animation is running or not.
+     */
+    void onRecentsAnimationStateChanged(boolean running);
 }
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index 19b68e5..4014c45 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -188,7 +188,8 @@
         final int y = (int) e.getY();
         final boolean fromMouse = e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE;
         final boolean primaryButton = (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0;
-        switch (e.getActionMasked()) {
+        final int actionMasked = e.getActionMasked();
+        switch (actionMasked) {
             case MotionEvent.ACTION_DOWN:
                 if (!mShow) {
                     // When there is no caption we should not react to anything.
@@ -220,6 +221,12 @@
                     break;
                 }
                 // Abort the ongoing dragging.
+                if (actionMasked == MotionEvent.ACTION_UP) {
+                    // If it receives ACTION_UP event, the dragging is already finished and also
+                    // the system can not end drag on ACTION_UP event. So request to finish
+                    // dragging.
+                    finishMovingTask();
+                }
                 mDragging = false;
                 return !mCheckForDragging;
         }
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 3adb36f..c73de8f 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -854,12 +854,11 @@
 
         final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
         final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
-        final int widthPadding = getPaddingLeft() + getPaddingRight();
 
         // Currently we allot more height than is really needed so that the entirety of the
         // sheet may be pulled up.
         // TODO: Restrict the height here to be the right value.
-        int heightUsed = getPaddingTop() + getPaddingBottom();
+        int heightUsed = 0;
 
         // Measure always-show children first.
         final int childCount = getChildCount();
@@ -869,11 +868,11 @@
             if (lp.alwaysShow && child.getVisibility() != GONE) {
                 if (lp.maxHeight != -1) {
                     final int remainingHeight = heightSize - heightUsed;
-                    measureChildWithMargins(child, widthSpec, widthPadding,
+                    measureChildWithMargins(child, widthSpec, 0,
                             MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
                             lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
                 } else {
-                    measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                    measureChildWithMargins(child, widthSpec, 0, heightSpec, heightUsed);
                 }
                 heightUsed += child.getMeasuredHeight();
             }
@@ -889,11 +888,11 @@
             if (!lp.alwaysShow && child.getVisibility() != GONE) {
                 if (lp.maxHeight != -1) {
                     final int remainingHeight = heightSize - heightUsed;
-                    measureChildWithMargins(child, widthSpec, widthPadding,
+                    measureChildWithMargins(child, widthSpec, 0,
                             MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
                             lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
                 } else {
-                    measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                    measureChildWithMargins(child, widthSpec, 0, heightSpec, heightUsed);
                 }
                 heightUsed += child.getMeasuredHeight();
             }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 36eb4c4..4874c41 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2374,6 +2374,12 @@
     // Settings > Apps and notifications > Notifications > Gentle notifications
     GENTLE_NOTIFICATIONS_SCREEN = 1715;
 
+    // OPEN: Settings > Display > Dark Theme
+    // CATEGORY: SETTINGS
+    // OS: Q
+    // Note: Only shows up on first time toggle
+    DIALOG_DARK_UI_INFO = 1740;
+
     // OPEN: Settings > System > Gestures > Global Actions Panel
     // CATEGORY: SETTINGS
     // OS: Q
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 0df2c83..3f8ddff 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -276,6 +276,24 @@
         // The maximum number of jobs that should be allowed to run in the past
         // {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
         optional int32 max_job_count_per_allowed_time = 12;
+        // The maximum number of timing sessions an app can run within this particular standby
+        // bucket's window size.
+        optional int32 max_session_count_active = 13;
+        // The maximum number of timing sessions an app can run within this particular standby
+        // bucket's window size.
+        optional int32 max_session_count_working = 14;
+        // The maximum number of timing sessions an app can run within this particular standby
+        // bucket's window size.
+        optional int32 max_session_count_frequent = 15;
+        // The maximum number of timing sessions an app can run within this particular standby
+        // bucket's window size.
+        optional int32 max_session_count_rare = 16;
+        // The maximum number of timing sessions that should be allowed to run in the past
+        // {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
+        optional int32 max_session_count_per_allowed_time = 17;
+        // Treat two distinct {@link TimingSession}s as the same if they start and end within this
+        // amount of time of each other.
+        optional int64 timing_session_coalescing_duration_ms = 18;
     }
     optional QuotaController quota_controller = 24;
 
@@ -511,6 +529,12 @@
             optional int32 bg_job_count_in_max_period = 7;
 
             /**
+             * The number of {@link TimingSession}s within the bucket window size. This will include
+             * sessions that started before the window as long as they end within the window.
+             */
+            optional int32 session_count_in_window = 11;
+
+            /**
              * The time after which the sum of all the app's sessions plus
              * ConstantsProto.QuotaController.in_quota_buffer_ms equals the quota. This is only
              * valid if
@@ -535,6 +559,21 @@
              * ConstantsProto.QuotaController.allowed_time_per_period_ms.
              */
             optional int32 job_count_in_allowed_time = 10;
+
+            /**
+             * The time after which {@link #timingSessionCountInAllowedTime} should be considered
+             * invalid, in the elapsed realtime timebase.
+             */
+            optional int64 session_count_expiration_time_elapsed = 12;
+
+            /**
+             * The number of {@link TimingSession}s that ran in at least the last
+             * {@link #mAllowedTimePerPeriodMs}. It may contain a few stale entries since cleanup won't
+             * happen exactly every {@link #mAllowedTimePerPeriodMs}. This should only be considered
+             * valid before elapsed realtime has reached
+             * {@link #timingSessionCountExpirationTimeElapsed}.
+             */
+            optional int32 session_count_in_allowed_time = 13;
         }
 
         message Package {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b634bb2..1c1225b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -904,9 +904,9 @@
         android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
         android:protectionLevel="dangerous|instant" />
 
-    <!-- Allows an app to access location in the background.  If you
-         are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
-         Requesting this by itself is not sufficient to give you
+    <!-- Allows an app to access location in the background. If you're requesting this permission,
+         you must also request either {@link #ACCESS_COARSE_LOCATION} or
+         {@link #ACCESS_FINE_LOCATION}. Requesting this permission by itself doesn't give you
          location access.
          <p>Protection level: dangerous
     -->
@@ -4434,7 +4434,7 @@
     <!-- Allows an application to watch changes and/or active state of app ops.
          @hide <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WATCH_APPOPS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|privileged" />
 
     <!-- Allows an application to directly open the "Open by default" page inside a package's
          Details screen.
diff --git a/core/res/res/drawable/ic_battery_80_24dp.xml b/core/res/res/drawable/ic_battery_80_24dp.xml
new file mode 100644
index 0000000..2513d0d
--- /dev/null
+++ b/core/res/res/drawable/ic_battery_80_24dp.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M9.5,2v2H7.33C6.6,4 6,4.6 6,5.33V15v5.67C6,21.4 6.6,22 7.33,22h9.33C17.4,22 18,21.4 18,20.67V15V5.33C18,4.6 17.4,4 16.67,4H14.5V2H9.5zM8,20v-5V6h8v9v5H8L8,20z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M16.67,22H7.33C6.6,22 6,21.4 6,20.67V8h12v12.67C18,21.4 17.4,22 16.67,22z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/list_divider_material.xml b/core/res/res/drawable/list_divider_material.xml
index 658a59d..e4c12fb 100644
--- a/core/res/res/drawable/list_divider_material.xml
+++ b/core/res/res/drawable/list_divider_material.xml
@@ -15,8 +15,8 @@
 -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:tint="?attr/colorForeground">
-    <solid android:color="#1f000000" />
+       android:tint="?attr/colorListDivider">
+    <solid android:color="?attr/opacityListDivider" />
     <size
         android:height="1dp"
         android:width="1dp" />
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index b8bc17f..8ba2c60 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -58,8 +58,10 @@
         <item name="colorPrimary">@color/primary_dark_device_default_settings</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorBackground">@color/primary_dark_device_default_settings</item>
+        <item name="colorEdgeEffect">@color/edge_effect_device_default_dark</item>
 
-        <item name="listDivider">@color/list_divider_color_dark</item>
+        <item name="colorListDivider">@color/list_divider_color_dark</item>
+        <item name="opacityListDivider">@color/list_divider_opacity_device_default_dark</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.BaseDialog">
@@ -86,5 +88,4 @@
     <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
         <item name="windowLightNavigationBar">false</item>
     </style>
-
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dbde79b..81accdf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -89,6 +89,10 @@
         <attr name="backgroundDimEnabled" format="boolean" />
         <!-- Color of background imagery used for popup windows. -->
         <attr name="colorPopupBackground" format="color" />
+        <!-- Color used for list divider. -->
+        <attr name="colorListDivider" format="color" />
+        <!-- Opacity used for list divider. -->
+        <attr name="opacityListDivider" format="color" />
 
         <!-- =========== -->
         <!-- Text styles -->
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 112f98e..7a8f411 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -48,8 +48,14 @@
 
     <color name="list_divider_color_light">#ffdadce0</color>
     <color name="list_divider_color_dark">#85ffffff</color>
-    <color name="loading_gradient_background_color_dark">#2D3033</color>
-    <color name="loading_gradient_background_color_light">#DADCE0</color>
-    <color name="loading_gradient_highlight_color_dark">#3C4043</color>
+    <color name="list_divider_opacity_device_default_light">@android:color/white</color>
+    <color name="list_divider_opacity_device_default_dark">@android:color/white</color>
+
+    <color name="loading_gradient_background_color_dark">#44484C</color>
+    <color name="loading_gradient_background_color_light">#F8F9FA</color>
+    <color name="loading_gradient_highlight_color_dark">#4D5155</color>
     <color name="loading_gradient_highlight_color_light">#F1F3F4</color>
+
+    <color name="edge_effect_device_default_light">@android:color/black</color>
+    <color name="edge_effect_device_default_dark">@android:color/white</color>
 </resources>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 2966aff..d357f01 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -149,4 +149,7 @@
     <color name="autofill_background_material_dark">@color/material_blue_grey_900</color>
     <color name="autofill_background_material_light">@color/material_grey_50</color>
 
+    <!-- List divider opacity -->
+    <color name="list_divider_opacity_material">#1f000000</color>
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 37678dd..fe49a31 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1499,16 +1499,16 @@
     <string name="fingerprint_icon_content_description">Fingerprint icon</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=70] -->
-    <string name="permlab_manageFace">manage face authentication hardware</string>
+    <string name="permlab_manageFace">manage face unlock hardware</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
     <string name="permdesc_manageFace">Allows the app to invoke methods to add and delete facial templates for use.</string>
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=70] -->
-    <string name="permlab_useFaceAuthentication">use face authentication hardware</string>
+    <string name="permlab_useFaceAuthentication">use face unlock hardware</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
-    <string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
+    <string name="permdesc_useFaceAuthentication">Allows the app to use face unlock hardware for authentication</string>
 
     <!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
-    <string name="face_recalibrate_notification_name">Face Authentication</string>
+    <string name="face_recalibrate_notification_name">Face unlock</string>
     <!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
     <string name="face_recalibrate_notification_title">Re-enroll your face</string>
     <!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
@@ -1561,23 +1561,23 @@
     <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=69] -->
     <string name="face_error_hw_not_available">Can\u2019t verify face. Hardware not available.</string>
     <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
-    <string name="face_error_timeout">Try face authentication again.</string>
+    <string name="face_error_timeout">Try face unlock again.</string>
     <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=69] -->
     <string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
     <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
-    <string name="face_error_canceled">Face operation canceled</string>
-    <!-- Generic error message shown when the face authentication operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] -->
-    <string name="face_error_user_canceled">Face authentication canceled by user</string>
+    <string name="face_error_canceled">Face operation canceled.</string>
+    <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] -->
+    <string name="face_error_user_canceled">Face unlock canceled by user.</string>
     <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
     <string name="face_error_lockout">Too many attempts. Try again later.</string>
     <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=71] -->
-    <string name="face_error_lockout_permanent">Too many attempts. Face authentication disabled.</string>
+    <string name="face_error_lockout_permanent">Too many attempts. Face unlock disabled.</string>
     <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
     <string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
     <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=52] -->
-    <string name="face_error_not_enrolled">You haven\u2019t set up face authentication</string>
-    <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=61] -->
-    <string name="face_error_hw_not_present">Face authentication is not supported on this device</string>
+    <string name="face_error_not_enrolled">You haven\u2019t set up face unlock.</string>
+    <!-- Generic error message shown when the app requests face unlock on a device without a sensor. [CHAR LIMIT=61] -->
+    <string name="face_error_hw_not_present">Face unlock is not supported on this device.</string>
 
     <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
     <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
@@ -3614,7 +3614,7 @@
     <!-- Message of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
     <string name="usb_contaminant_detected_message">USB port is automatically disabled. Tap to learn more.</string>
     <!-- Title of notification shown when contaminant is no longer detected on the USB port. [CHAR LIMIT=NONE] -->
-    <string name="usb_contaminant_not_detected_title">Safe to use USB port</string>
+    <string name="usb_contaminant_not_detected_title">OK to use USB port</string>
     <!-- Message of notification shown when contaminant is no longer detected on the USB port. [CHAR LIMIT=NONE] -->
     <string name="usb_contaminant_not_detected_message">Phone no longer detects liquid or debris.</string>
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 8015a5d..6289262 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1471,7 +1471,7 @@
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorEdgeEffect">@android:color/black</item>
+        <item name="colorEdgeEffect">@color/edge_effect_device_default_light</item>
 
         <!-- Add white nav bar with divider that matches material -->
         <item name="navigationBarDividerColor">@color/navigation_bar_divider_device_default_settings</item>
@@ -1488,7 +1488,8 @@
         <!-- Progress bar attributes -->
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
-        <item name="listDivider">@color/list_divider_color_light</item>
+        <item name="colorListDivider">@color/list_divider_color_light</item>
+        <item name="opacityListDivider">@color/list_divider_opacity_device_default_light</item>
     </style>
 
     <!-- @hide DeviceDefault theme for a window that should use Settings theme colors
@@ -1677,8 +1678,11 @@
         <item name="colorControlActivated">?attr/colorControlHighlight</item>
         <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
         <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
-        <item name="navigationBarColor">?attr/colorBackgroundFloating</item>
-        <item name="navigationBarDividerColor">@color/chooser_row_divider</item>
+        <item name="navigationBarColor">@android:color/transparent</item>
+    </style>
+
+    <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
+        <item name="windowLightNavigationBar">true</item>
     </style>
 
     <style name="Animation.DeviceDefault.Activity.Resolver" parent="Animation.DeviceDefault.Activity">
@@ -1689,10 +1693,6 @@
         <item name="taskOpenExitAnimation">@anim/resolver_close_anim</item>
     </style>
 
-    <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
-        <item name="windowLightNavigationBar">true</item>
-    </style>
-
     <!-- @hide DeviceDefault themes for the autofill FillUi -->
     <style name="Theme.DeviceDefault.Autofill" />
     <style name="Theme.DeviceDefault.Light.Autofill" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 6337db1..ce29389 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -53,6 +53,8 @@
         <item name="backgroundDimAmount">0.6</item>
         <item name="colorError">@color/error_color_material_dark</item>
         <item name="colorPopupBackground">?attr/colorBackground</item>
+        <item name="colorListDivider">?attr/colorForeground</item>
+        <item name="opacityListDivider">@color/list_divider_opacity_material</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Material</item>
@@ -424,6 +426,8 @@
         <item name="backgroundDimAmount">0.6</item>
         <item name="colorError">@color/error_color_material_light</item>
         <item name="colorPopupBackground">?attr/colorBackground</item>
+        <item name="colorListDivider">?attr/colorForeground</item>
+        <item name="opacityListDivider">@color/list_divider_opacity_material</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Material</item>
diff --git a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
index 220f854..c72707db 100644
--- a/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/android/server/bugreports/BugreportManagerTest.java
@@ -88,7 +88,9 @@
     @Test
     public void normalFlow_wifi() throws Exception {
         BugreportCallbackImpl callback = new BugreportCallbackImpl();
-        mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
+        // wifi bugreport does not take screenshot
+        mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(),
+                mExecutor, callback);
         waitTillDoneOrTimeout(callback);
 
         assertThat(callback.isDone()).isTrue();
@@ -99,13 +101,15 @@
         // of mBugreportFd.
         assertThat(callback.getErrorCode()).isEqualTo(
                 BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
-        assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+        assertFdsAreClosed(mBugreportFd);
     }
 
     @Test
     public void normalFlow_interactive() throws Exception {
         BugreportCallbackImpl callback = new BugreportCallbackImpl();
-        mBrm.startBugreport(mBugreportFd, mScreenshotFd, interactive(), mExecutor, callback);
+        // interactive bugreport does not take screenshot
+        mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(),
+                mExecutor, callback);
 
         waitTillDoneOrTimeout(callback);
         assertThat(callback.isDone()).isTrue();
@@ -113,7 +117,7 @@
         assertThat(callback.hasReceivedProgress()).isTrue();
         assertThat(callback.getErrorCode()).isEqualTo(
                 BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
-        assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+        assertFdsAreClosed(mBugreportFd);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java
index 1948994..7a42731 100644
--- a/core/tests/coretests/src/android/text/EmojiTest.java
+++ b/core/tests/coretests/src/android/text/EmojiTest.java
@@ -40,7 +40,7 @@
 public class EmojiTest {
 
     @Test
-    public void testIsNewEmoji_Emoji5() {
+    public void testIsNewEmoji_Emoji() {
         // each row in the data is the range of emoji
         final int[][][] data = new int[][][]{
                 {       // EMOJI 5
@@ -66,6 +66,24 @@
                         {0x1F9B0, 0x1F9B9},
                         {0x1F9C1, 0x1F9C2},
                         {0x1F9E7, 0x1F9FF},
+                },
+                {       // EMOJI 12
+                        {0x1F6D5, 0x1F6D5},
+                        {0x1F6FA, 0x1F6FA},
+                        {0x1F93F, 0x1F93F},
+                        {0x1F971, 0x1F971},
+                        {0x1F97B, 0x1F97B},
+                        {0x1F7E0, 0x1F7EB},
+                        {0x1F90D, 0x1F90F},
+                        {0x1F9A5, 0x1F9AA},
+                        {0x1F9AE, 0x1F9AF},
+                        {0x1F9BA, 0x1F9BF},
+                        {0x1F9C3, 0x1F9CA},
+                        {0x1F9CD, 0x1F9CF},
+                        {0x1FA70, 0x1FA73},
+                        {0x1FA78, 0x1FA7A},
+                        {0x1FA80, 0x1FA82},
+                        {0x1FA90, 0x1FA95}
                 }
         };
 
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 5cfae11..e24f26c1 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -55,5 +55,6 @@
   <hidden-api-whitelisted-app package="com.android.storagemanager" />
   <hidden-api-whitelisted-app package="com.android.systemui.plugins" />
   <hidden-api-whitelisted-app package="com.android.terminal" />
+  <hidden-api-whitelisted-app package="com.android.wallpaper" />
   <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
 </config>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c54208b..a16d5a7 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -194,14 +194,12 @@
                       targetSdk="16">
         <new-permission name="android.permission.WRITE_CALL_LOG" />
     </split-permission>
-    <!-- STOPSHIP(b/118882117): change targetSdk to Q when SDK version finalised -->
     <split-permission name="android.permission.ACCESS_FINE_LOCATION"
-                      targetSdk="10000">
+                      targetSdk="29">
         <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     </split-permission>
-    <!-- STOPSHIP(b/118882117): change targetSdk to Q when SDK version finalised -->
     <split-permission name="android.permission.ACCESS_COARSE_LOCATION"
-                      targetSdk="10000">
+                      targetSdk="29">
         <new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     </split-permission>
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b96c59a..b3856d5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -195,6 +195,7 @@
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
+        <permission name="android.permission.WATCH_APPOPS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.telephony">
@@ -263,6 +264,8 @@
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
+        <!-- Needed for test only -->
+        <permission name="android.permission.INTERACT_ACROSS_PROFILES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_ACCESSIBILITY"/>
@@ -313,6 +316,8 @@
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+        <!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
+        <permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 297153d..32f2fc2 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1930,11 +1930,11 @@
         public float[] mTempPositions; // no need to copy
         @UnsupportedAppUsage
         public float[] mPositions;
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917)
+        @UnsupportedAppUsage(trackingBug = 124050917)
         public int mStrokeWidth = -1; // if >= 0 use stroking.
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917)
+        @UnsupportedAppUsage(trackingBug = 124050917)
         public float mStrokeDashWidth = 0.0f;
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917)
+        @UnsupportedAppUsage(trackingBug = 124050917)
         public float mStrokeDashGap = 0.0f;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917)
         public float mRadius = 0.0f; // use this if mRadiusArray is null
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 1b515ad..d20aeca 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -778,8 +778,6 @@
 }
 
 const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
-  ATRACE_NAME("AssetManager::GetBag");
-
   auto cached_iter = cached_bags_.find(resid);
   if (cached_iter != cached_bags_.end()) {
     return cached_iter->second.get();
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 60d0318..a6af475 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -19,6 +19,7 @@
 
 #include <cstdint>
 #include <string>
+#include <vector>
 
 #include <android/util/EncodedBuffer.h>
 
@@ -124,6 +125,7 @@
     sp<ProtoReader> data(); // Get the reader apis of the data.
     bool flush(int fd); // Flush data directly to a file descriptor.
     bool serializeToString(std::string* out); // Serializes the proto to a string.
+    bool serializeToVector(std::vector<uint8_t>* out); // Serializes the proto to a vector<uint8_t>.
 
     /**
      * Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again.
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 98a68c6..6cfa357 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -454,7 +454,6 @@
     if (out == nullptr) return false;
     if (!compact()) return false;
 
-
     sp<ProtoReader> reader = mBuffer->read();
     out->reserve(reader->size());
     while (reader->hasNext()) {
@@ -465,6 +464,23 @@
     return true;
 }
 
+bool
+ProtoOutputStream::serializeToVector(std::vector<uint8_t>* out)
+{
+    if (out == nullptr) return false;
+    if (!compact()) return false;
+
+    sp<ProtoReader> reader = mBuffer->read();
+    out->reserve(reader->size());
+    while (reader->hasNext()) {
+        const uint8_t* buf = reader->readBuffer();
+        size_t size = reader->currentToRead();
+        out->insert(out->end(), buf, buf + size);
+        reader->move(size);
+    }
+    return true;
+}
+
 sp<ProtoReader>
 ProtoOutputStream::data()
 {
diff --git a/libs/protoutil/tests/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp
index 398af60..f895154 100644
--- a/libs/protoutil/tests/EncodedBuffer_test.cpp
+++ b/libs/protoutil/tests/EncodedBuffer_test.cpp
@@ -29,101 +29,101 @@
 }
 
 TEST(EncodedBufferTest, WriteSimple) {
-    EncodedBuffer buffer(TEST_CHUNK_SIZE);
-    EXPECT_EQ(buffer.size(), 0UL);
-    expectPointer(buffer.wp(), 0);
-    EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_SIZE);
+    sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
+    EXPECT_EQ(buffer->size(), 0UL);
+    expectPointer(buffer->wp(), 0);
+    EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_SIZE);
     for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
-        buffer.writeRawByte(50 + i);
+        buffer->writeRawByte(50 + i);
     }
-    EXPECT_EQ(buffer.size(), TEST_CHUNK_HALF_SIZE);
-    expectPointer(buffer.wp(), TEST_CHUNK_HALF_SIZE);
-    EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+    EXPECT_EQ(buffer->size(), TEST_CHUNK_HALF_SIZE);
+    expectPointer(buffer->wp(), TEST_CHUNK_HALF_SIZE);
+    EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_HALF_SIZE);
     for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
-        buffer.writeRawByte(80 + i);
+        buffer->writeRawByte(80 + i);
     }
-    EXPECT_EQ(buffer.size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
-    expectPointer(buffer.wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
-    EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+    EXPECT_EQ(buffer->size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+    expectPointer(buffer->wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+    EXPECT_EQ(buffer->currentToWrite(), TEST_CHUNK_HALF_SIZE);
 
     // verifies the buffer's data
-    expectPointer(buffer.ep(), 0);
+    expectPointer(buffer->ep(), 0);
     for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
-        EXPECT_EQ(buffer.readRawByte(), 50 + i);
+        EXPECT_EQ(buffer->readRawByte(), 50 + i);
     }
     for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
-        EXPECT_EQ(buffer.readRawByte(), 80 + i);
+        EXPECT_EQ(buffer->readRawByte(), 80 + i);
     }
 
     // clears the buffer
-    buffer.clear();
-    EXPECT_EQ(buffer.size(), 0UL);
-    expectPointer(buffer.wp(), 0);
+    buffer->clear();
+    EXPECT_EQ(buffer->size(), 0UL);
+    expectPointer(buffer->wp(), 0);
 }
 
 TEST(EncodedBufferTest, WriteVarint) {
-    EncodedBuffer buffer(TEST_CHUNK_SIZE);
+    sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
     size_t expected_buffer_size = 0;
-    EXPECT_EQ(buffer.writeRawVarint32(13), 1);
+    EXPECT_EQ(buffer->writeRawVarint32(13), 1);
     expected_buffer_size += 1;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
-    EXPECT_EQ(buffer.writeRawVarint32(UINT32_C(-1)), 5);
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
+    EXPECT_EQ(buffer->writeRawVarint32(UINT32_C(-1)), 5);
     expected_buffer_size += 5;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
 
-    EXPECT_EQ(buffer.writeRawVarint64(200), 2);
+    EXPECT_EQ(buffer->writeRawVarint64(200), 2);
     expected_buffer_size += 2;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
-    EXPECT_EQ(buffer.writeRawVarint64(UINT64_C(-1)), 10);
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
+    EXPECT_EQ(buffer->writeRawVarint64(UINT64_C(-1)), 10);
     expected_buffer_size += 10;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
 
-    buffer.writeRawFixed32(UINT32_C(-1));
+    buffer->writeRawFixed32(UINT32_C(-1));
     expected_buffer_size += 4;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
-    buffer.writeRawFixed64(UINT64_C(-1));
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
+    buffer->writeRawFixed64(UINT64_C(-1));
     expected_buffer_size += 8;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
 
-    EXPECT_EQ(buffer.writeHeader(32, 2), 2);
+    EXPECT_EQ(buffer->writeHeader(32, 2), 2);
     expected_buffer_size += 2;
-    EXPECT_EQ(buffer.size(), expected_buffer_size);
+    EXPECT_EQ(buffer->size(), expected_buffer_size);
 
     // verify data are correctly written to the buffer.
-    expectPointer(buffer.ep(), 0);
-    EXPECT_EQ(buffer.readRawVarint(), UINT32_C(13));
-    EXPECT_EQ(buffer.readRawVarint(), UINT32_C(-1));
-    EXPECT_EQ(buffer.readRawVarint(), UINT64_C(200));
-    EXPECT_EQ(buffer.readRawVarint(), UINT64_C(-1));
-    EXPECT_EQ(buffer.readRawFixed32(), UINT32_C(-1));
-    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(-1));
-    EXPECT_EQ(buffer.readRawVarint(), UINT64_C((32 << 3) + 2));
-    expectPointer(buffer.ep(), expected_buffer_size);
+    expectPointer(buffer->ep(), 0);
+    EXPECT_EQ(buffer->readRawVarint(), UINT32_C(13));
+    EXPECT_EQ(buffer->readRawVarint(), UINT32_C(-1));
+    EXPECT_EQ(buffer->readRawVarint(), UINT64_C(200));
+    EXPECT_EQ(buffer->readRawVarint(), UINT64_C(-1));
+    EXPECT_EQ(buffer->readRawFixed32(), UINT32_C(-1));
+    EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(-1));
+    EXPECT_EQ(buffer->readRawVarint(), UINT64_C((32 << 3) + 2));
+    expectPointer(buffer->ep(), expected_buffer_size);
 }
 
 TEST(EncodedBufferTest, Edit) {
-    EncodedBuffer buffer(TEST_CHUNK_SIZE);
-    buffer.writeRawFixed64(0xdeadbeefdeadbeef);
-    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
+    sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
+    buffer->writeRawFixed64(0xdeadbeefdeadbeef);
+    EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
 
-    buffer.editRawFixed32(4, 0x12345678);
+    buffer->editRawFixed32(4, 0x12345678);
     // fixed 64 is little endian order.
-    buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
-    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678deadbeef));
+    buffer->ep()->rewind(); // rewind ep for readRawFixed64 from 0
+    EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0x12345678deadbeef));
 
-    buffer.wp()->rewind();
-    expectPointer(buffer.wp(), 0);
-    buffer.copy(4, 3);
-    buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
-    EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678de345678));
+    buffer->wp()->rewind();
+    expectPointer(buffer->wp(), 0);
+    buffer->copy(4, 3);
+    buffer->ep()->rewind(); // rewind ep for readRawFixed64 from 0
+    EXPECT_EQ(buffer->readRawFixed64(), UINT64_C(0x12345678de345678));
 }
 
 TEST(EncodedBufferTest, ReadSimple) {
-    EncodedBuffer buffer(TEST_CHUNK_SIZE);
+    sp<EncodedBuffer> buffer = new EncodedBuffer(TEST_CHUNK_SIZE);
     for (size_t i = 0; i < TEST_CHUNK_3X_SIZE; i++) {
-        buffer.writeRawByte(i);
+        buffer->writeRawByte(i);
     }
-    sp<ProtoReader> reader1 = buffer.read();
+    sp<ProtoReader> reader1 = buffer->read();
     EXPECT_EQ(reader1->size(), TEST_CHUNK_3X_SIZE);
     EXPECT_EQ(reader1->bytesRead(), 0);
 
@@ -132,7 +132,7 @@
     }
     EXPECT_EQ(reader1->bytesRead(), TEST_CHUNK_3X_SIZE);
 
-    sp<ProtoReader> reader2 = buffer.read();
+    sp<ProtoReader> reader2 = buffer->read();
     uint8_t val = 0;
     while (reader2->hasNext()) {
         EXPECT_EQ(reader2->next(), val);
@@ -143,10 +143,10 @@
 }
 
 TEST(EncodedBufferTest, ReadVarint) {
-    EncodedBuffer buffer;
+    sp<EncodedBuffer> buffer = new EncodedBuffer();
     uint64_t val = UINT64_C(1522865904593);
-    size_t len = buffer.writeRawVarint64(val);
-    sp<ProtoReader> reader = buffer.read();
+    size_t len = buffer->writeRawVarint64(val);
+    sp<ProtoReader> reader = buffer->read();
     EXPECT_EQ(reader->size(), len);
     EXPECT_EQ(reader->readRawVarint(), val);
 }
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
index 6282fd5..9b768b7 100644
--- a/libs/protoutil/tests/ProtoOutputStream_test.cpp
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -132,6 +132,53 @@
     EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
 }
 
+TEST(ProtoOutputStreamTest, SerializeToVectorPrimitives) {
+    std::string s = "hello";
+    const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
+
+    ProtoOutputStream proto;
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
+    EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
+
+    PrimitiveProto primitives;
+    std::vector<uint8_t> vec;
+    ASSERT_TRUE(proto.serializeToVector(&vec));
+
+    std::string serialized(vec.data(), vec.data() + vec.size());
+    ASSERT_TRUE(primitives.ParseFromString(serialized));
+
+    EXPECT_EQ(primitives.val_int32(), 123);
+    EXPECT_EQ(primitives.val_int64(), -1);
+    EXPECT_EQ(primitives.val_float(), -23.5f);
+    EXPECT_EQ(primitives.val_double(), 324.5f);
+    EXPECT_EQ(primitives.val_uint32(), 3424);
+    EXPECT_EQ(primitives.val_uint64(), 57);
+    EXPECT_EQ(primitives.val_fixed32(), -20);
+    EXPECT_EQ(primitives.val_fixed64(), -37);
+    EXPECT_EQ(primitives.val_bool(), true);
+    EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
+    EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
+    EXPECT_EQ(primitives.val_sfixed32(), 63);
+    EXPECT_EQ(primitives.val_sfixed64(), -54);
+    EXPECT_EQ(primitives.val_sint32(), -533);
+    EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
+    EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
+}
+
 TEST(ProtoOutputStreamTest, Complex) {
     std::string name1 = "cat";
     std::string name2 = "dog";
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 04e7bab..e890514 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -360,7 +360,7 @@
     /**
      * Posts a notification in the status bar using the contents in {@code notif} object.
      */
-    public synchronized void setNiNotification(GpsNiNotification notif) {
+    private synchronized void setNiNotification(GpsNiNotification notif) {
         NotificationManager notificationManager = (NotificationManager) mContext
                 .getSystemService(Context.NOTIFICATION_SERVICE);
         if (notificationManager == null) {
@@ -541,35 +541,26 @@
      */
     static private String decodeString(String original, boolean isHex, int coding)
     {
-        if (coding == GPS_ENC_NONE) {
+        if (coding == GPS_ENC_NONE || coding == GPS_ENC_UNKNOWN) {
             return original;
         }
 
-        String decoded = original;
         byte[] input = stringToByteArray(original, isHex);
 
         switch (coding) {
-        case GPS_ENC_SUPL_GSM_DEFAULT:
-            decoded = decodeGSMPackedString(input);
-            break;
+            case GPS_ENC_SUPL_GSM_DEFAULT:
+                return decodeGSMPackedString(input);
 
-        case GPS_ENC_SUPL_UTF8:
-            decoded = decodeUTF8String(input);
-            break;
+            case GPS_ENC_SUPL_UTF8:
+                return decodeUTF8String(input);
 
-        case GPS_ENC_SUPL_UCS2:
-            decoded = decodeUCS2String(input);
-            break;
+            case GPS_ENC_SUPL_UCS2:
+                return decodeUCS2String(input);
 
-        case GPS_ENC_UNKNOWN:
-            decoded = original;
-            break;
-
-        default:
-            Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
-            break;
+            default:
+                Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
+                return original;
         }
-        return decoded;
     }
 
     // change this to configure notification display
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a8f313b..dc3041f 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -400,7 +400,7 @@
     /**
      * Indicates that the audio may be captured by any app.
      *
-     * For privacy, the following usages can not be recorded: VOICE_COMMUNICATION*,
+     * For privacy, the following usages cannot be recorded: VOICE_COMMUNICATION*,
      * USAGE_NOTIFICATION*, USAGE_ASSISTANCE* and USAGE_ASSISTANT.
      *
      * On {@link android.os.Build.VERSION_CODES#Q}, this means only {@link #USAGE_UNKNOWN},
@@ -413,11 +413,11 @@
     /**
      * Indicates that the audio may only be captured by system apps.
      *
-     * System apps can capture for many purposes like accessibility, user guidance...
+     * System apps can capture for many purposes like accessibility, live captions, user guidance...
      * but abide to the following restrictions:
-     *  - the audio can not leave the device
-     *  - the audio can not be passed to a third party app
-     *  - the audio can not be recorded at a higher quality then 16kHz 16bit mono
+     *  - the audio cannot leave the device
+     *  - the audio cannot be passed to a third party app
+     *  - the audio cannot be recorded at a higher quality than 16kHz 16bit mono
      *
      * See {@link Builder#setAllowedCapturePolicy}.
      */
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 31a6b81..d83cd96 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -82,7 +82,7 @@
  */
 public class ExifInterface {
     private static final String TAG = "ExifInterface";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // The Exif tag names. See Tiff 6.0 Section 3 and Section 8.
     /** Type is String. */
@@ -1605,12 +1605,15 @@
                         || exifTag.primaryFormat == IFD_FORMAT_STRING) {
                     dataFormat = exifTag.primaryFormat;
                 } else {
-                    Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
-                            + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
-                            + (exifTag.secondaryFormat == -1 ? "" : ", "
-                            + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
-                            + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
-                            + IFD_FORMAT_NAMES[guess.second]) + ")");
+                    if (DEBUG) {
+                        Log.d(TAG, "Given tag (" + tag
+                                + ") value didn't match with one of expected "
+                                + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
+                                + (exifTag.secondaryFormat == -1 ? "" : ", "
+                                + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
+                                + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
+                                + IFD_FORMAT_NAMES[guess.second]) + ")");
+                    }
                     continue;
                 }
                 switch (dataFormat) {
@@ -1688,7 +1691,9 @@
                         break;
                     }
                     default:
-                        Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
+                        if (DEBUG) {
+                            Log.d(TAG, "Data format isn't one of expected formats: " + dataFormat);
+                        }
                         continue;
                 }
             }
@@ -1790,7 +1795,7 @@
             // ExifInterface.
             mIsSupportedFile = false;
             if (DEBUG) {
-                Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
+                Log.d(TAG, "Invalid image: ExifInterface got an unsupported image format file"
                         + "(ExifInterface supports JPEG and some RAW image formats only) "
                         + "or a corrupted JPEG file to ExifInterface.", e);
             }
@@ -3183,14 +3188,20 @@
             long byteCount = 0;
             boolean valid = false;
             if (tag == null) {
-                Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
+                if (DEBUG) {
+                    Log.d(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
+                }
             } else if (dataFormat <= 0 || dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
-                Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
+                if (DEBUG) {
+                    Log.d(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
+                }
             } else {
                 byteCount = (long) numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
                 if (byteCount < 0 || byteCount > Integer.MAX_VALUE) {
-                    Log.w(TAG, "Skip the tag entry since the number of components is invalid: "
-                            + numberOfComponents);
+                    if (DEBUG) {
+                        Log.d(TAG, "Skip the tag entry since the number of components is invalid: "
+                                + numberOfComponents);
+                    }
                 } else {
                     valid = true;
                 }
@@ -3239,7 +3250,9 @@
                     dataInputStream.seek(offset);
                 } else {
                     // Skip if invalid data offset.
-                    Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
+                    if (DEBUG) {
+                        Log.d(TAG, "Skip the tag entry since data offset is invalid: " + offset);
+                    }
                     dataInputStream.seek(nextEntryOffset);
                     continue;
                 }
@@ -3289,11 +3302,15 @@
                         dataInputStream.seek(offset);
                         readImageFileDirectory(dataInputStream, nextIfdType);
                     } else {
-                        Log.w(TAG, "Skip jump into the IFD since it has already been read: "
-                                + "IfdType " + nextIfdType + " (at " + offset + ")");
+                        if (DEBUG) {
+                            Log.d(TAG, "Skip jump into the IFD since it has already been read: "
+                                    + "IfdType " + nextIfdType + " (at " + offset + ")");
+                        }
                     }
                 } else {
-                    Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
+                    if (DEBUG) {
+                        Log.d(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
+                    }
                 }
 
                 dataInputStream.seek(nextEntryOffset);
@@ -3348,12 +3365,16 @@
                         readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
                     }
                 } else {
-                    Log.w(TAG, "Stop reading file since re-reading an IFD may cause an "
-                            + "infinite loop: " + nextIfdOffset);
+                    if (DEBUG) {
+                        Log.d(TAG, "Stop reading file since re-reading an IFD may cause an "
+                                + "infinite loop: " + nextIfdOffset);
+                    }
                 }
             } else {
-                Log.w(TAG, "Stop reading file since a wrong offset may cause an infinite loop: "
-                        + nextIfdOffset);
+                if (DEBUG) {
+                    Log.d(TAG, "Stop reading file since a wrong offset may cause an infinite loop: "
+                            + nextIfdOffset);
+                }
             }
         }
     }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 68c0a22..435d8d7 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -509,35 +509,16 @@
      * @return The position of the {@link Uri}, or -1 if it cannot be found.
      */
     public int getRingtonePosition(Uri ringtoneUri) {
-        
         if (ringtoneUri == null) return -1;
+        final long ringtoneId = ContentUris.parseId(ringtoneUri);
         
         final Cursor cursor = getCursor();
-        final int cursorCount = cursor.getCount();
-        
-        if (!cursor.moveToFirst()) {
-            return -1;
-        }
-        
-        // Only create Uri objects when the actual URI changes
-        Uri currentUri = null;
-        String previousUriString = null;
-        for (int i = 0; i < cursorCount; i++) {
-            String uriString = cursor.getString(URI_COLUMN_INDEX);
-            if (currentUri == null || !uriString.equals(previousUriString)) {
-                currentUri = Uri.parse(uriString);
+        cursor.moveToPosition(-1);
+        while (cursor.moveToNext()) {
+            if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
+                return cursor.getPosition();
             }
-            
-            if (ringtoneUri.equals(ContentUris.withAppendedId(currentUri, cursor
-                    .getLong(ID_COLUMN_INDEX)))) {
-                return i;
-            }
-            
-            cursor.move(1);
-            
-            previousUriString = uriString;
         }
-        
         return -1;
     }
 
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index b3c2bb7..534d63b 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -265,13 +265,10 @@
             }
         }
 
-        boolean isHeifFile = false;
-
         if (mimeType.equals("image/heif")
                 || mimeType.equals("image/heif-sequence")
                 || mimeType.equals("image/heic")
                 || mimeType.equals("image/heic-sequence")) {
-            isHeifFile = true;
             try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
                 retriever.setDataSource(file.getAbsolutePath());
                 bitmap = retriever.getThumbnailImageAtIndex(-1,
@@ -298,11 +295,8 @@
 
         if (bitmap == null) {
             bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer);
-            // Use ImageDecoder to do full image decode of heif format file
-            // will have right orientation. Don't rotate the bitmap again.
-            if (isHeifFile) {
-                return bitmap;
-            }
+            // Use ImageDecoder to do full file decoding, we don't need to handle the orientation
+            return bitmap;
         }
 
         // Transform the bitmap if the orientation of the image is not 0.
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 2f03d26..a82c78f 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -91,6 +91,20 @@
      */
     public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1;
 
+    /**
+     * An audio mix behavior where the targeted audio is played unaffected but a copy is
+     * accessible for capture through {@link AudioRecord}.
+     *
+     * Only capture of playback is supported, not capture of capture.
+     * Use concurrent capture instead to capture what is captured by other apps.
+     *
+     * The captured audio is an approximation of the played audio.
+     * Effects and volume are not applied, and track are mixed with different delay then in the HAL.
+     * As a result, this API is not suitable for echo cancelling.
+     * @hide
+     */
+    public static final int ROUTE_FLAG_LOOP_BACK_RENDER = ROUTE_FLAG_LOOP_BACK | ROUTE_FLAG_RENDER;
+
     private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK;
 
     // MIX_TYPE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
@@ -125,6 +139,15 @@
      */
     public static final int MIX_STATE_MIXING = 1;
 
+    /** Maximum sampling rate for privileged playback capture*/
+    private static final int PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE = 16000;
+
+    /** Maximum channel number for privileged playback capture*/
+    private static final int PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER = 1;
+
+    /** Maximum channel number for privileged playback capture*/
+    private static final int PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE = 2;
+
     /**
      * The current mixing state.
      * @return one of {@link #MIX_STATE_DISABLED}, {@link #MIX_STATE_IDLE},
@@ -140,7 +163,8 @@
         return mRouteFlags;
     }
 
-    AudioFormat getFormat() {
+    /** @hide */
+    public AudioFormat getFormat() {
         return mFormat;
     }
 
@@ -182,6 +206,31 @@
         return true;
     }
 
+    /** @return an error string if the format would not allow Privileged playbackCapture
+     *          null otherwise
+     * @hide */
+    public static String canBeUsedForPrivilegedCapture(AudioFormat format) {
+        int sampleRate = format.getSampleRate();
+        if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) {
+            return "Privileged audio capture sample rate " + sampleRate
+                   + " can not be over " + PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE + "kHz";
+        }
+        int channelCount = format.getChannelCount();
+        if (channelCount > PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER || channelCount <= 0) {
+            return "Privileged audio capture channel count " + channelCount + " can not be over "
+                   + PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER;
+        }
+        int encoding = format.getEncoding();
+        if (!format.isPublicEncoding(encoding) || !format.isEncodingLinearPcm(encoding)) {
+            return "Privileged audio capture encoding " + encoding + "is not linear";
+        }
+        if (format.getBytesPerSample(encoding) > PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE) {
+            return "Privileged audio capture encoding " + encoding + " can not be over "
+                   + PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE + " bytes per sample";
+        }
+        return null;
+    }
+
     /** @hide */
     @Override
     public boolean equals(Object o) {
@@ -390,6 +439,12 @@
                     }
                 }
             }
+            if (mRule.allowPrivilegedPlaybackCapture()) {
+                String error = AudioMix.canBeUsedForPrivilegedCapture(mFormat);
+                if (error != null) {
+                    throw new IllegalArgumentException(error);
+                }
+            }
             return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType,
                     mDeviceAddress);
         }
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index ed2fdae..c4afd95 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -365,6 +365,10 @@
         /**
          * Set if the audio of app that opted out of audio playback capture should be captured.
          *
+         * Caller of this method with <code>true</code>, MUST abide to the restriction listed in
+         * {@link ALLOW_CAPTURE_BY_SYSTEM}, including but not limited to the captured audio
+         * can not leave the capturing app, and the quality is limited to 16k mono.
+         *
          * The permission {@link CAPTURE_AUDIO_OUTPUT} or {@link CAPTURE_MEDIA_OUTPUT} is needed
          * to ignore the opt-out.
          *
diff --git a/packages/CarSystemUI/res/drawable/notification_handle_bar.xml b/packages/CarSystemUI/res/drawable/notification_handle_bar.xml
new file mode 100644
index 0000000..5ed7499
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/notification_handle_bar.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<ripple
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@android:color/white">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/clear_all_button_radius"/>
+            <solid android:color="@android:color/white"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml b/packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml
new file mode 100644
index 0000000..918abd9
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_ongoing_privacy_chip.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<com.android.systemui.statusbar.car.privacy.OngoingPrivacyChip
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/car_privacy_chip"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:layout_margin="@dimen/ongoing_appops_chip_margin"
+    android:layout_toStartOf="@+id/clock_container"
+    android:focusable="true"
+    android:gravity="center_vertical|end"
+    android:orientation="horizontal"
+    android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
+    android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
+    android:visibility="visible">
+
+    <LinearLayout
+        android:id="@+id/icons_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical|start"/>
+</com.android.systemui.statusbar.car.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 925ccb4..cae89c1 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -65,6 +65,8 @@
             />
         </FrameLayout>
 
+        <include layout="@layout/car_ongoing_privacy_chip"/>
+
         <FrameLayout
             android:id="@+id/clock_container"
             android:layout_width="wrap_content"
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 5c915b8..55b0d87 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -23,24 +23,27 @@
     android:background="@color/notification_shade_background_color">
 
     <View
-         android:id="@+id/glass_pane"
-         android:layout_width="match_parent"
-         android:layout_height="match_parent"
-         app:layout_constraintTop_toTopOf="parent"
-         app:layout_constraintBottom_toBottomOf="parent"
-         app:layout_constraintStart_toStartOf="parent"
-         app:layout_constraintEnd_toEndOf="parent"
-         android:translationZ="2dp"
-        />
+        android:id="@+id/glass_pane"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:translationZ="2dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+    />
 
     <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/notifications"
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:orientation="vertical"
-        app:layout_constraintTop_toTopOf="parent"
+        android:paddingStart="@dimen/notification_shade_list_padding_bottom"
         app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"/>
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <include layout="@layout/notification_handle_bar"/>
 
 </com.android.car.notification.CarNotificationView>
diff --git a/packages/CarSystemUI/res/layout/notification_handle_bar.xml b/packages/CarSystemUI/res/layout/notification_handle_bar.xml
new file mode 100644
index 0000000..99c3a02
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/notification_handle_bar.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <View
+        android:id="@+id/handle_bar"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/notification_shade_handle_bar_height"
+        android:layout_marginBottom="@dimen/notification_shade_handle_bar_margin_bottom"
+        android:layout_marginEnd="@dimen/notification_shade_handle_bar_margin_start"
+        android:layout_marginStart="@dimen/notification_shade_handle_bar_margin_end"
+        android:layout_marginTop="@dimen/notification_shade_handle_bar_margin_top"
+        android:background="@drawable/notification_handle_bar"/>
+</merge>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml b/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
index 2793e56..531e577 100644
--- a/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
+++ b/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
@@ -58,8 +58,8 @@
             android:gravity="center_vertical">
             <com.android.systemui.statusbar.AlphaOptimizedImageView
                 android:id="@+id/wifi_signal"
-                android:layout_width="@dimen/status_bar_icon_size"
-                android:layout_height="@dimen/status_bar_icon_size"
+                android:layout_width="@*android:dimen/status_bar_system_icon_size"
+                android:layout_height="@*android:dimen/status_bar_system_icon_size"
                 android:theme="?attr/lightIconTheme"/>
         </FrameLayout>
 
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 83ec351..0d69fbb 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -34,7 +34,7 @@
     <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
 
     <!-- The background color of the notification shade -->
-    <color name="notification_shade_background_color">#99000000</color>
+    <color name="notification_shade_background_color">#DD000000</color>
 
     <!-- The color of the dividing line between grouped notifications. -->
     <color name="notification_divider_color">@*android:color/notification_action_list</color>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 8789c8a..7027ce3 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -59,6 +59,16 @@
     <dimen name="car_keyline_1">24dp</dimen>
     <dimen name="car_keyline_2">96dp</dimen>
     <dimen name="car_keyline_3">128dp</dimen>
+    <dimen name="privacy_chip_icon_height">36dp</dimen>
+    <dimen name="privacy_chip_icon_padding_left">0dp</dimen>
+    <dimen name="privacy_chip_icon_padding_right">0dp</dimen>
+    <dimen name="privacy_chip_icon_padding_top">0dp</dimen>
+    <dimen name="privacy_chip_icon_padding_bottom">0dp</dimen>
+
+    <dimen name="privacy_chip_text_padding_left">0dp</dimen>
+    <dimen name="privacy_chip_text_padding_right">0dp</dimen>
+    <dimen name="privacy_chip_text_padding_top">0dp</dimen>
+    <dimen name="privacy_chip_text_padding_bottom">0dp</dimen>
 
     <dimen name="privacy_chip_icon_max_height">100dp</dimen>
 
@@ -76,6 +86,16 @@
     <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
     <!-- Radius of Ongoing App Ops chip corners -->
     <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+    <!-- Start padding for the app icon displayed in the dialog -->
+    <dimen name="privacy_dialog_app_icon_padding_start">40dp</dimen>
+    <!-- End padding for the app opps icon displayed in the dialog -->
+    <dimen name="privacy_dialog_app_ops_icon_padding_end">40dp</dimen>
+    <!-- Top padding for the list of application displayed in the dialog -->
+    <dimen name="privacy_dialog_app_list_padding_top">20dp</dimen>
+    <!-- Top padding for the dialog container-->
+    <dimen name="privacy_dialog_container_padding_top">10dp</dimen>
+    <!-- Top padding for the dialog title-->
+    <dimen name="privacy_dialog_title_padding_start">10dp</dimen>
 
     <!-- Car volume dimens. -->
     <dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
@@ -88,4 +108,14 @@
     <dimen name="car_volume_item_divider_width">1dp</dimen>
     <dimen name="car_volume_item_divider_margin_end">@*android:dimen/car_padding_4</dimen>
     <dimen name="car_volume_item_corner_radius">@*android:dimen/car_radius_3</dimen>
+
+    <!-- Car notification shade-->
+    <dimen name="notification_shade_handle_bar_height">10dp</dimen>
+    <dimen name="notification_shade_handle_bar_radius">20dp</dimen>
+    <dimen name="notification_shade_handle_bar_margin_start">500dp</dimen>
+    <dimen name="notification_shade_handle_bar_margin_end">500dp</dimen>
+    <dimen name="notification_shade_handle_bar_margin_top">20dp</dimen>
+    <dimen name="notification_shade_handle_bar_margin_bottom">10dp</dimen>
+    <dimen name="notification_shade_list_padding_bottom">0dp</dimen>
+
 </resources>
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 9b6ab06..54e468ee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -131,6 +131,8 @@
     // The container for the notifications.
     private CarNotificationView mNotificationView;
     private RecyclerView mNotificationList;
+    // The handler bar view at the bottom of notification shade.
+    private View mHandleBar;
     // The controller for the notification view.
     private NotificationViewController mNotificationViewController;
     // The state of if the notification list is currently showing the bottom.
@@ -464,6 +466,7 @@
 
         mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
         View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
+        mHandleBar = mStatusBarWindow.findViewById(R.id.handle_bar);
         mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
         mNotificationView.setNotificationDataManager(mNotificationDataManager);
 
@@ -521,7 +524,7 @@
 
                 boolean handled = closeGestureDetector.onTouchEvent(event);
                 boolean isTracking = mIsTracking;
-                Rect rect = mNotificationList.getClipBounds();
+                Rect rect = mNotificationView.getClipBounds();
                 float clippedHeight = 0;
                 if (rect != null) {
                     clippedHeight = rect.bottom;
@@ -609,7 +612,7 @@
             to = mNotificationView.getHeight();
         }
 
-        Rect rect = mNotificationList.getClipBounds();
+        Rect rect = mNotificationView.getClipBounds();
         if (rect != null) {
             float from = rect.bottom;
             animate(from, to, velocity, isClosing);
@@ -653,7 +656,7 @@
                 if (isClosing) {
                     mStatusBarWindowController.setPanelVisible(false);
                     mNotificationView.setVisibility(View.INVISIBLE);
-                    mNotificationList.setClipBounds(null);
+                    mNotificationView.setClipBounds(null);
                     mNotificationViewController.setIsInForeground(false);
                     // let the status bar know that the panel is closed
                     setPanelExpanded(false);
@@ -1012,8 +1015,12 @@
         Rect clipBounds = new Rect();
         clipBounds.set(0, 0, mNotificationView.getWidth(), height);
         // Sets the clip region on the notification list view.
-        mNotificationList.setClipBounds(clipBounds);
-
+        mNotificationView.setClipBounds(clipBounds);
+        if (mHandleBar != null) {
+            ViewGroup.MarginLayoutParams lp =
+                    (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
+            mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
+        }
         if (mNotificationView.getHeight() > 0) {
             // Calculates the alpha value for the background based on how much of the notification
             // shade is visible to the user. When the notification shade is completely open then
@@ -1095,9 +1102,6 @@
         @Override
         public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
                 float distanceY) {
-            if (!mNotificationListAtBottomAtTimeOfTouch && !mNotificationListAtBottom) {
-                return false;
-            }
             // should not clip while scroll to the bottom of the list.
             if (!mNotificationListAtBottomAtTimeOfTouch) {
                 return false;
@@ -1134,7 +1138,8 @@
         @Override
         public boolean onFling(MotionEvent event1, MotionEvent event2,
                 float velocityX, float velocityY) {
-            if (!mNotificationListAtBottomAtTimeOfTouch && !mNotificationListAtBottom) {
+            // should not fling if the touch does not start when view is at the bottom of the list.
+            if (!mNotificationListAtBottomAtTimeOfTouch) {
                 return false;
             }
             if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
new file mode 100644
index 0000000..ead1de2
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
@@ -0,0 +1,228 @@
+/*
+ * 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.car.privacy;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.appops.AppOpItem;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.plugins.ActivityStarter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Layout defining the privacy chip that will be displayed in CarStatusRar with the information for
+ * which applications are using AppOpps permission fpr camera, mic and location.
+ */
+public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
+
+    private Context mContext;
+
+    private LinearLayout mIconsContainer;
+    private List<PrivacyItem> mPrivacyItems;
+    private static AppOpsController sAppOpsController;
+    private UserManager mUserManager;
+    private int mCurrentUser;
+    private List<Integer> mCurrentUserIds;
+    private boolean mListening = false;
+    PrivacyDialogBuilder mPrivacyDialogBuilder;
+    private LinearLayout mPrivacyChip;
+    private ActivityStarter mActivityStarter;
+
+    protected static final int[] OPS = new int[]{
+            AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_RECORD_AUDIO,
+            AppOpsManager.OP_COARSE_LOCATION,
+            AppOpsManager.OP_FINE_LOCATION
+    };
+
+    public OngoingPrivacyChip(Context context) {
+        super(context, null);
+        init(context);
+    }
+
+    public OngoingPrivacyChip(Context context, AttributeSet attr) {
+        super(context, attr);
+        init(context);
+    }
+
+    public OngoingPrivacyChip(Context context, AttributeSet attr, int defStyle) {
+        super(context, attr, defStyle);
+        init(context);
+    }
+
+    public OngoingPrivacyChip(Context context, AttributeSet attr, int defStyle, int a) {
+        super(context, attr, defStyle, a);
+        init(context);
+    }
+
+    private void init(Context context) {
+        mContext = context;
+        mPrivacyItems = new ArrayList<>();
+        sAppOpsController = Dependency.get(AppOpsController.class);
+        mUserManager = mContext.getSystemService(UserManager.class);
+        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mCurrentUser = ActivityManager.getCurrentUser();
+        mCurrentUserIds = mUserManager.getProfiles(mCurrentUser).stream().map(
+                userInfo -> userInfo.id).collect(Collectors.toList());
+
+        mPrivacyDialogBuilder = new PrivacyDialogBuilder(context, mPrivacyItems);
+    }
+
+    private AppOpsController.Callback mCallback = new AppOpsController.Callback() {
+
+        @Override
+        public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
+            int userId = UserHandle.getUserId(uid);
+            if (mCurrentUserIds.contains(userId)) {
+                updatePrivacyList();
+            }
+        }
+    };
+
+    @Override
+    public void onFinishInflate() {
+        mIconsContainer = findViewById(R.id.icons_container);
+        mPrivacyChip = (LinearLayout) findViewById(R.id.car_privacy_chip);
+        if (mPrivacyChip != null) {
+            mPrivacyChip.setOnClickListener(this);
+            setListening(true);
+        }
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        if (mPrivacyChip != null) {
+            setListening(false);
+        }
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    public void onClick(View v) {
+        updatePrivacyList();
+        Handler mUiHandler = new Handler(Looper.getMainLooper());
+        mUiHandler.post(() -> {
+            mActivityStarter.postStartActivityDismissingKeyguard(
+                    new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
+        });
+    }
+
+    private void setListening(boolean listen) {
+        if (mListening == listen) {
+            return;
+        }
+        mListening = listen;
+        if (mListening) {
+            sAppOpsController.addCallback(OPS, mCallback);
+            updatePrivacyList();
+        } else {
+            sAppOpsController.removeCallback(OPS, mCallback);
+        }
+    }
+
+    private void updatePrivacyList() {
+        mPrivacyItems = mCurrentUserIds.stream()
+                .flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
+                .filter(Objects::nonNull)
+                .map(item -> toPrivacyItem(item))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
+
+        Handler refresh = new Handler(Looper.getMainLooper());
+        refresh.post(new Runnable() {
+            @Override
+            public void run() {
+                updateView();
+            }
+        });
+    }
+
+    private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
+        PrivacyType type;
+        switch (appOpItem.getCode()) {
+            case AppOpsManager.OP_CAMERA:
+                type = PrivacyType.TYPE_CAMERA;
+                break;
+            case AppOpsManager.OP_COARSE_LOCATION:
+                type = PrivacyType.TYPE_LOCATION;
+                break;
+            case AppOpsManager.OP_FINE_LOCATION:
+                type = PrivacyType.TYPE_LOCATION;
+                break;
+            case AppOpsManager.OP_RECORD_AUDIO:
+                type = PrivacyType.TYPE_MICROPHONE;
+                break;
+            default:
+                return null;
+        }
+        PrivacyApplication app = new PrivacyApplication(appOpItem.getPackageName(), mContext);
+        return new PrivacyItem(type, app, appOpItem.getTimeStarted());
+    }
+
+    // Should only be called if the mPrivacyDialogBuilder icons or app changed
+    private void updateView() {
+        if (mPrivacyItems.isEmpty()) {
+            mPrivacyChip.setVisibility(GONE);
+            return;
+        }
+        mPrivacyChip.setVisibility(VISIBLE);
+        setIcons(mPrivacyDialogBuilder);
+
+        requestLayout();
+    }
+
+    private void setIcons(PrivacyDialogBuilder dialogBuilder) {
+        mIconsContainer.removeAllViews();
+        dialogBuilder.generateIcons().forEach(item -> {
+            int size = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.privacy_chip_icon_height);
+            ImageView image = new ImageView(mContext);
+            image.setImageDrawable(item);
+            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(size, size);
+
+            int leftPadding = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.privacy_chip_icon_padding_left);
+            int topPadding = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.privacy_chip_icon_padding_top);
+            int rightPadding = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.privacy_chip_icon_padding_right);
+            int bottomPadding = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.privacy_chip_icon_padding_bottom);
+            image.setLayoutParams(layoutParams);
+            image.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
+            mIconsContainer.addView(image);
+        });
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
new file mode 100644
index 0000000..5ec7a77
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
@@ -0,0 +1,62 @@
+/*
+ * 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.car.privacy;
+
+import android.car.userlib.CarUserManagerHelper;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+/**
+ * Class to hold the data for the applications that are using the AppOps permissions.
+ */
+public class PrivacyApplication {
+    private static final String TAG = "PrivacyApplication";
+
+    private Drawable mIcon;
+    private String mApplicationName;
+
+    public PrivacyApplication(String packageName, Context context) {
+        try {
+            CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context);
+            ApplicationInfo app = context.getPackageManager()
+                    .getApplicationInfoAsUser(packageName, 0,
+                            carUserManagerHelper.getCurrentForegroundUserId());
+            mIcon = context.getPackageManager().getApplicationIcon(app);
+            mApplicationName = context.getPackageManager().getApplicationLabel(app).toString();
+        } catch (PackageManager.NameNotFoundException e) {
+            mApplicationName = packageName;
+            Log.e(TAG, "Failed to to find package name", e);
+        }
+    }
+
+    /**
+     * Gets the application name.
+     */
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Gets the application name.
+     */
+    public String getApplicationName() {
+        return mApplicationName;
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java
new file mode 100644
index 0000000..3b83e7c
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyDialogBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.car.privacy;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Helper class to build the {@link OngoingPrivacyDialog}
+ */
+public class PrivacyDialogBuilder {
+
+    private Map<PrivacyType, List<PrivacyItem>> mItemsByType;
+    private PrivacyApplication mApplication;
+    private Context mContext;
+
+    public PrivacyDialogBuilder(Context context, List<PrivacyItem> itemsList) {
+        mContext = context;
+        mItemsByType = itemsList.stream().filter(Objects::nonNull).collect(
+                Collectors.groupingBy(PrivacyItem::getPrivacyType));
+        List<PrivacyApplication> apps = itemsList.stream().filter(Objects::nonNull).map(
+                PrivacyItem::getPrivacyApplication).distinct().collect(Collectors.toList());
+        mApplication = apps.size() == 1 ? apps.get(0) : null;
+    }
+
+    /**
+     * Gets the icon id for all the {@link PrivacyItem} in the same order as of itemList.
+     */
+    public List<Drawable> generateIcons() {
+        return mItemsByType.keySet().stream().map(item -> item.getIconId(mContext)).collect(
+                Collectors.toList());
+    }
+
+    /**
+     * Gets the application object.
+     */
+    public PrivacyApplication getApplication() {
+        return mApplication;
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
new file mode 100644
index 0000000..fca1373
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
@@ -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 com.android.systemui.statusbar.car.privacy;
+
+/**
+ * Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
+ */
+public class PrivacyItem {
+
+    private PrivacyType mPrivacyType;
+    private PrivacyApplication mPrivacyApplication;
+
+    public PrivacyItem(PrivacyType privacyType, PrivacyApplication privacyApplication,
+            long timeStarted) {
+        this.mPrivacyType = privacyType;
+        this.mPrivacyApplication = privacyApplication;
+    }
+
+    /**
+     * Gets the application object.
+     */
+    public PrivacyApplication getPrivacyApplication() {
+        return mPrivacyApplication;
+    }
+
+    /**
+     * Gets the privacy type for the application.
+     */
+    public PrivacyType getPrivacyType() {
+        return mPrivacyType;
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java
new file mode 100644
index 0000000..8955c87
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyType.java
@@ -0,0 +1,53 @@
+/*
+ * 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.car.privacy;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import com.android.systemui.R;
+
+/**
+ * Enum for storing data for camera, mic and location.
+ */
+public enum PrivacyType {
+    TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
+    TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
+    TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_white);
+
+    private int mNameId;
+    private int mIconId;
+
+    PrivacyType(int nameId, int iconId) {
+        mNameId = nameId;
+        mIconId = iconId;
+    }
+
+    /**
+     * Get the icon Id.
+     */
+    public Drawable getIconId(Context context) {
+        return context.getResources().getDrawable(mIconId, null);
+    }
+
+    /**
+     * Get the name Id.
+     */
+    public String getNameId(Context context) {
+        return context.getResources().getString(mNameId);
+    }
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
index ea7b378..06c5294 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
@@ -22,8 +22,8 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.image.DynamicSystemClient;
+import android.os.image.DynamicSystemManager;
 import android.util.FeatureFlagUtils;
-import android.util.Log;
 
 
 /**
@@ -37,21 +37,26 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!featureFlagEnabled()) {
+        String action = intent.getAction();
+
+        if (!Intent.ACTION_BOOT_COMPLETED.equals(action)) {
             return;
         }
 
-        String action = intent.getAction();
+        DynamicSystemManager dynSystem =
+                (DynamicSystemManager) context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
 
-        Log.d(TAG, "Broadcast received: " + action);
+        boolean isInUse = (dynSystem != null) && dynSystem.isInUse();
 
-        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-            Intent startServiceIntent = new Intent(
-                    context, DynamicSystemInstallationService.class);
-
-            startServiceIntent.setAction(DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE);
-            context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
+        if (!isInUse && !featureFlagEnabled()) {
+            return;
         }
+
+        Intent startServiceIntent = new Intent(
+                context, DynamicSystemInstallationService.class);
+
+        startServiceIntent.setAction(DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE);
+        context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
     }
 
     private boolean featureFlagEnabled() {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index b0e28a0..077f7ec 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -37,7 +37,7 @@
 
     private static final String TAG = "InstallationAsyncTask";
 
-    private static final int READ_BUFFER_SIZE = 1 << 19;
+    private static final int READ_BUFFER_SIZE = 1 << 13;
 
     private class InvalidImageUrlException extends RuntimeException {
         private InvalidImageUrlException(String message) {
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 73c7895..d01878a 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -30,11 +30,9 @@
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.IPackageManager;
-import android.ext.services.notification.AgingHelper.Callback;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.service.notification.Adjustment;
@@ -76,8 +74,6 @@
 public class Assistant extends NotificationAssistantService {
     private static final String TAG = "ExtAssistant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    public static final boolean AGE_NOTIFICATIONS = SystemProperties.getBoolean(
-            "debug.age_notifs", false);
 
     private static final String TAG_ASSISTANT = "assistant";
     private static final String TAG_IMPRESSION = "impression-set";
@@ -94,7 +90,6 @@
 
     private SmartActionsHelper mSmartActionsHelper;
     private NotificationCategorizer mNotificationCategorizer;
-    private AgingHelper mAgingHelper;
 
     // key : impressions tracker
     // TODO: prune deleted channels and apps
@@ -125,9 +120,6 @@
                 getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
         mSmartActionsHelper = new SmartActionsHelper(getContext(), mSettings);
         mNotificationCategorizer = new NotificationCategorizer();
-        mAgingHelper = new AgingHelper(getContext(),
-                mNotificationCategorizer,
-                new AgingCallback());
         mSmsHelper = new SmsHelper(this);
         mSmsHelper.initialize();
     }
@@ -301,15 +293,18 @@
                         sbn, ranking.getChannel(), mSmsHelper);
                 String key = getKey(
                         sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
-                ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
-                        createChannelImpressionsWithThresholds());
-                if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) {
+                boolean shouldTriggerBlock;
+                synchronized (mkeyToImpressions) {
+                    ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
+                            createChannelImpressionsWithThresholds());
+                    mkeyToImpressions.put(key, ci);
+                    shouldTriggerBlock = ci.shouldTriggerBlock();
+                }
+                if (ranking.getImportance() > IMPORTANCE_MIN && shouldTriggerBlock) {
                     adjustNotification(createNegativeAdjustment(
                             sbn.getPackageName(), sbn.getKey(), sbn.getUserId()));
                 }
-                mkeyToImpressions.put(key, ci);
                 mLiveNotifications.put(sbn.getKey(), entry);
-                mAgingHelper.onNotificationPosted(entry);
             }
         } catch (Throwable e) {
             Log.e(TAG, "Error occurred processing post", e);
@@ -324,8 +319,6 @@
                 return;
             }
 
-            mAgingHelper.onNotificationRemoved(sbn.getKey());
-
             boolean updatedImpressions = false;
             String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
             String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
@@ -370,22 +363,6 @@
 
     @Override
     public void onNotificationsSeen(List<String> keys) {
-        try {
-            if (keys == null) {
-                return;
-            }
-
-            for (String key : keys) {
-                NotificationEntry entry = mLiveNotifications.get(key);
-
-                if (entry != null) {
-                    entry.setSeen();
-                    mAgingHelper.onNotificationSeen(entry);
-                }
-            }
-        } catch (Throwable e) {
-            Slog.e(TAG, "Error occurred processing seen", e);
-        }
     }
 
     @Override
@@ -452,9 +429,6 @@
 
     @Override
     public void onListenerDisconnected() {
-        if (mAgingHelper != null) {
-            mAgingHelper.onDestroy();
-        }
     }
 
     private boolean isForCurrentUser(StatusBarNotification sbn) {
@@ -537,21 +511,4 @@
             }
         }
     }
-
-    protected final class AgingCallback implements Callback {
-        @Override
-        public void sendAdjustment(String key, int newImportance) {
-            if (AGE_NOTIFICATIONS) {
-                NotificationEntry entry = mLiveNotifications.get(key);
-                if (entry != null) {
-                    Bundle bundle = new Bundle();
-                    bundle.putInt(KEY_IMPORTANCE, newImportance);
-                    Adjustment adjustment = new Adjustment(entry.getSbn().getPackageName(), key,
-                            bundle, "aging", entry.getSbn().getUserId());
-                    adjustNotification(adjustment);
-                }
-            }
-        }
-    }
-
 }
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
index 765e9f9..670b419 100644
--- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
@@ -39,9 +39,9 @@
     // TODO: Add build dependency on NetworkStack stable AIDL so we can stop hard coding class name
     private static final String NETWORK_STACK_CONNECTOR_CLASS =
             "android.net.INetworkStackConnector";
-    private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
+    public static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
             "watchdog_request_timeout_millis";
-    private static final long DEFAULT_REQUEST_TIMEOUT_MILLIS =
+    public static final long DEFAULT_REQUEST_TIMEOUT_MILLIS =
             TimeUnit.HOURS.toMillis(1);
     // Modified only #onCreate, using concurrent collection to ensure thread visibility
     private final Map<String, ExplicitHealthChecker> mSupportedCheckers = new ConcurrentHashMap<>();
diff --git a/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java
new file mode 100644
index 0000000..a9cb63e
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.ext.services.watchdog;
+
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES;
+import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES;
+import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteCallback;
+import android.service.watchdog.ExplicitHealthCheckService;
+import android.service.watchdog.IExplicitHealthCheckService;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ServiceTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Contains the base tests that does not rely on the specific algorithm implementation.
+ */
+public class ExplicitHealthCheckServiceImplTest {
+    private static final String NETWORK_STACK_CONNECTOR_CLASS =
+            "android.net.INetworkStackConnector";
+
+    private final Context mContext = InstrumentationRegistry.getContext();
+    private IExplicitHealthCheckService mService;
+    private String mNetworkStackPackageName;
+
+    @Rule
+    public ServiceTestRule mServiceTestRule;
+
+    @Before
+    public void setUp() throws Exception {
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity(
+                        Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE);
+
+        mServiceTestRule = new ServiceTestRule();
+        mService = IExplicitHealthCheckService.Stub.asInterface(
+                mServiceTestRule.bindService(getExtServiceIntent()));
+        mNetworkStackPackageName = getNetworkStackPackage();
+        assumeFalse(mNetworkStackPackageName == null);
+    }
+
+    @After
+    public void tearDown() {
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testHealthCheckSupportedPackage() throws Exception {
+        List<PackageConfig> supportedPackages = new ArrayList<>();
+        CountDownLatch latch = new CountDownLatch(1);
+
+        mService.getSupportedPackages(new RemoteCallback(result -> {
+            supportedPackages.addAll(result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES));
+            latch.countDown();
+        }));
+        latch.await();
+
+        // TODO: Support DeviceConfig changes for the health check timeout
+        assertThat(supportedPackages).hasSize(1);
+        assertThat(supportedPackages.get(0).getPackageName())
+                .isEqualTo(mNetworkStackPackageName);
+        assertThat(supportedPackages.get(0).getHealthCheckTimeoutMillis())
+                .isEqualTo(ExplicitHealthCheckServiceImpl.DEFAULT_REQUEST_TIMEOUT_MILLIS);
+    }
+
+    @Test
+    public void testHealthCheckRequests() throws Exception {
+        List<String> requestedPackages = new ArrayList<>();
+        CountDownLatch latch1 = new CountDownLatch(1);
+        CountDownLatch latch2 = new CountDownLatch(1);
+        CountDownLatch latch3 = new CountDownLatch(1);
+
+        // Initially, no health checks requested
+        mService.getRequestedPackages(new RemoteCallback(result -> {
+            requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
+            latch1.countDown();
+        }));
+
+        // Verify that no health checks requested
+        latch1.await();
+        assertThat(requestedPackages).isEmpty();
+
+        // Then request health check
+        mService.request(mNetworkStackPackageName);
+
+        // Verify that health check is requested for network stack
+        mService.getRequestedPackages(new RemoteCallback(result -> {
+            requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
+            latch2.countDown();
+        }));
+        latch2.await();
+        assertThat(requestedPackages).hasSize(1);
+        assertThat(requestedPackages.get(0)).isEqualTo(mNetworkStackPackageName);
+
+        // Then cancel health check
+        requestedPackages.clear();
+        mService.cancel(mNetworkStackPackageName);
+
+        // Verify that health check is cancelled for network stack
+        mService.getRequestedPackages(new RemoteCallback(result -> {
+            requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
+            latch3.countDown();
+        }));
+        latch3.await();
+        assertThat(requestedPackages).isEmpty();
+    }
+
+    private String getNetworkStackPackage() {
+        Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        if (comp != null) {
+            return comp.getPackageName();
+        } else {
+            // On Go devices, or any device that does not ship the network stack module.
+            // The network stack will live in system_server process, so no need to monitor.
+            return null;
+        }
+    }
+
+    private Intent getExtServiceIntent() {
+        ComponentName component = getExtServiceComponentNameLocked();
+        if (component == null) {
+            fail("Health check service not found");
+        }
+        Intent intent = new Intent();
+        intent.setComponent(component);
+        return intent;
+    }
+
+    private ComponentName getExtServiceComponentNameLocked() {
+        ServiceInfo serviceInfo = getExtServiceInfoLocked();
+        if (serviceInfo == null) {
+            return null;
+        }
+
+        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE
+                .equals(serviceInfo.permission)) {
+            return null;
+        }
+        return name;
+    }
+
+    private ServiceInfo getExtServiceInfoLocked() {
+        final String packageName =
+                mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
+        if (packageName == null) {
+            return null;
+        }
+
+        final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE);
+        intent.setPackage(packageName);
+        final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            return null;
+        }
+        return resolveInfo.serviceInfo;
+    }
+}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index e15526a..aefa882 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -109,7 +109,9 @@
     // InProcessNetworkStack is a replacement for NetworkStack
     overrides: ["NetworkStack"],
     // The permission configuration *must* be included to ensure security of the device
-    required: ["PlatformNetworkPermissionConfig"],
+    // The InProcessNetworkStack goes together with the PlatformCaptivePortalLogin, which replaces
+    // the default CaptivePortalLogin.
+    required: ["PlatformNetworkPermissionConfig", "PlatformCaptivePortalLogin"],
 }
 
 // Updatable network stack packaged as an application
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index bfcd6c1..4c44484 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -41,7 +41,9 @@
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <!-- Signature permission defined in NetworkStackStub -->
     <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
-    <application android:extractNativeLibs="false">
+    <application
+        android:extractNativeLibs="false"
+        android:persistent="true">
         <service android:name="com.android.server.NetworkStackService">
             <intent-filter>
                 <action android:name="android.net.INetworkStackConnector"/>
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
index 3da566f..d00a551 100644
--- a/packages/NetworkStack/AndroidManifestBase.xml
+++ b/packages/NetworkStack/AndroidManifestBase.xml
@@ -24,7 +24,6 @@
         android:label="NetworkStack"
         android:defaultToDeviceProtectedStorage="true"
         android:directBootAware="true"
-        android:persistent="true"
         android:usesCleartextTraffic="true">
 
         <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index a6d7484..c394d4c 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -62,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Android service used to start the network stack when bound to via an intent.
@@ -117,6 +118,15 @@
         @GuardedBy("mValidationLogs")
         private final ArrayDeque<SharedLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
 
+        private static final int VERSION_UNKNOWN = 0;
+        private static final String DUMPSYS_ARG_VERSION = "version";
+
+        /** Version of the AIDL interfaces observed on the system */
+        private final AtomicInteger mSystemAidlVersion = new AtomicInteger(VERSION_UNKNOWN);
+
+        /** Whether different versions have been observed on interfaces provided by the system */
+        private volatile boolean mConflictingSystemAidlVersions = false;
+
         private SharedLog addValidationLogs(Network network, String name) {
             final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name);
             synchronized (mValidationLogs) {
@@ -143,6 +153,13 @@
             }
         }
 
+        private void updateSystemAidlVersion(final int version) {
+            final int previousVersion = mSystemAidlVersion.getAndSet(version);
+            if (previousVersion != VERSION_UNKNOWN && previousVersion != version) {
+                mConflictingSystemAidlVersions = true;
+            }
+        }
+
         @NonNull
         private final SharedLog mLog = new SharedLog(TAG);
 
@@ -150,6 +167,7 @@
         public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
                 @NonNull IDhcpServerCallbacks cb) throws RemoteException {
             checkNetworkStackCallingPermission();
+            updateSystemAidlVersion(cb.getInterfaceVersion());
             final DhcpServer server;
             try {
                 server = new DhcpServer(
@@ -171,6 +189,7 @@
         @Override
         public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb)
                 throws RemoteException {
+            updateSystemAidlVersion(cb.getInterfaceVersion());
             final SharedLog log = addValidationLogs(network, name);
             final NetworkMonitor nm = new NetworkMonitor(mContext, cb, network, log);
             cb.onNetworkMonitorCreated(new NetworkMonitorImpl(nm));
@@ -178,6 +197,7 @@
 
         @Override
         public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException {
+            updateSystemAidlVersion(cb.getInterfaceVersion());
             final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry, this);
 
             synchronized (mIpClients) {
@@ -202,6 +222,7 @@
         @Override
         public void fetchIpMemoryStore(@NonNull final IIpMemoryStoreCallbacks cb)
                 throws RemoteException {
+            updateSystemAidlVersion(cb.getInterfaceVersion());
             cb.onIpMemoryStoreFetched(mIpMemoryStoreService);
         }
 
@@ -209,6 +230,11 @@
         protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
                 @Nullable String[] args) {
             checkDumpPermission();
+            if (args != null && args.length >= 1 && DUMPSYS_ARG_VERSION.equals(args[0])) {
+                dumpVersion(fout);
+                return;
+            }
+
             final IndentingPrintWriter pw = new IndentingPrintWriter(fout, "  ");
             pw.println("NetworkStack logs:");
             mLog.dump(fd, pw, args);
@@ -252,6 +278,15 @@
             }
         }
 
+        /**
+         * Dump version information of the module and detected system version.
+         */
+        private void dumpVersion(@NonNull PrintWriter fout) {
+            fout.println("NetworkStackConnector: " + this.VERSION);
+            fout.println("SystemServer: " + mSystemAidlVersion);
+            fout.println("SystemServerConflicts: " + mConflictingSystemAidlVersions);
+        }
+
         @Override
         public int getInterfaceVersion() {
             return this.VERSION;
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
index 6a6bf83..8312dfe 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
@@ -410,6 +410,10 @@
         });
     }
 
+    @Override
+    public void factoryReset() {
+    }
+
     /** Get db size threshold. */
     @VisibleForTesting
     protected int getDbSizeThreshold() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 42afb69..c4ff719 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -132,4 +132,12 @@
             .isHiddenModule(packageName);
     }
 
+    /**
+     * Returns a boolean indicating whether a given package is a system module.
+     */
+    public static boolean isSystemModule(Context context, String packageName) {
+        return ApplicationsState.getInstance((Application) context.getApplicationContext())
+                .isSystemModule(packageName);
+    }
+
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 5eaa163..f9df5a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -139,7 +139,8 @@
     String mCurComputingSizePkg;
     int mCurComputingSizeUserId;
     boolean mSessionsChanged;
-    final HashSet<String> mHiddenModules = new HashSet<>();
+    // Maps all installed modules on the system to whether they're hidden or not.
+    final HashMap<String, Boolean> mSystemModules = new HashMap<>();
 
     // Temporary for dispatching session callbacks.  Only touched by main thread.
     final ArrayList<WeakReference<Session>> mActiveSessions = new ArrayList<>();
@@ -212,9 +213,7 @@
 
         final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */);
         for (ModuleInfo info : moduleInfos) {
-            if (info.isHidden()) {
-                mHiddenModules.add(info.getPackageName());
-            }
+            mSystemModules.put(info.getPackageName(), info.isHidden());
         }
 
         /**
@@ -426,7 +425,16 @@
     }
 
     boolean isHiddenModule(String packageName) {
-        return mHiddenModules.contains(packageName);
+        Boolean isHidden = mSystemModules.get(packageName);
+        if (isHidden == null) {
+            return false;
+        }
+
+        return isHidden;
+    }
+
+    boolean isSystemModule(String packageName) {
+        return mSystemModules.containsKey(packageName);
     }
 
     void doPauseIfNeededLocked() {
@@ -688,7 +696,10 @@
             Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
         }
         if (entry == null) {
-            if (mHiddenModules.contains(info.packageName)) {
+            if (isHiddenModule(info.packageName)) {
+                if (DEBUG) {
+                    Log.i(TAG, "No AppEntry for " + info.packageName + " (hidden module)");
+                }
                 return null;
             }
             if (DEBUG) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 1d351a5..9a95288 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -826,11 +826,22 @@
     }
 
     /**
-     * @return resource for string that discribes the connection state of this device.
-     * case 1: idle or playing media, show "Active" on the only one A2DP active device.
-     * case 2: in phone call, show "Active" on the only one HFP active device
+     * Return full summary that describes connection state of this device
+     *
+     * @see #getConnectionSummary(boolean shortSummary)
      */
     public String getConnectionSummary() {
+        return getConnectionSummary(false /* shortSummary */);
+    }
+
+    /**
+     * Return summary that describes connection state of this device. Summary depends on:
+     * 1. Whether device has battery info
+     * 2. Whether device is in active usage(or in phone call)
+     *
+     * @param shortSummary {@code true} if need to return short version summary
+     */
+    public String getConnectionSummary(boolean shortSummary) {
         boolean profileConnected = false;    // Updated as long as BluetoothProfile is connected
         boolean a2dpConnected = true;        // A2DP is connected
         boolean hfpConnected = true;         // HFP is connected
@@ -909,9 +920,9 @@
                 if ((mIsActiveDeviceHearingAid)
                         || (mIsActiveDeviceHeadset && isOnCall)
                         || (mIsActiveDeviceA2dp && !isOnCall)) {
-                    if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+                    if (isTwsBatteryAvailable(leftBattery, rightBattery) && !shortSummary) {
                         stringRes = R.string.bluetooth_active_battery_level_untethered;
-                    } else if (batteryLevelPercentageString != null) {
+                    } else if (batteryLevelPercentageString != null && !shortSummary) {
                         stringRes = R.string.bluetooth_active_battery_level;
                     } else {
                         stringRes = R.string.bluetooth_active_no_battery_level;
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
index 786139f..b2e75ea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
@@ -77,5 +77,17 @@
             Settings.Global.putLong(resolver, Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME,
                     System.currentTimeMillis())
         }
+
+        /**
+         * Returns when the estimate was last updated as an Instant
+         */
+        @JvmStatic
+        fun getLastCacheUpdateTime(context: Context): Instant {
+            return Instant.ofEpochMilli(
+                    Settings.Global.getLong(
+                            context.contentResolver,
+                            Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME,
+                            -1))
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
index ec8bb80..4941f7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -117,7 +117,8 @@
                 if (info == null || !info.enabled
                         || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
                     mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier(),
-                            0 /*installFlags*/, PackageManager.INSTALL_REASON_UNKNOWN);
+                            PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                            PackageManager.INSTALL_REASON_UNKNOWN, null);
                     if (DEBUG) {
                         Log.d(TAG, "Installing " + packageName);
                     }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index e840a4b..b216a2a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -16,15 +16,14 @@
 
 package com.android.settingslib.users;
 
-import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
@@ -43,6 +42,7 @@
 import android.os.UserManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.inputmethod.InputMethodInfo;
+
 import com.android.settingslib.BaseTest;
 
 import org.mockito.ArgumentMatcher;
@@ -146,7 +146,8 @@
         mHelper.applyUserAppsStates(mockListener);
 
         verify(mIpm, times(1)).installExistingPackageAsUser("app1", testUserId,
-                0 /*installFlags*/, PackageManager.INSTALL_REASON_UNKNOWN);
+                PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                PackageManager.INSTALL_REASON_UNKNOWN, null);
         verify(mIpm, times(1)).setApplicationHiddenSettingAsUser("app2", false, testUserId);
         verify(mockListener).onDisableUiForPackage("app2");
         verify(mPm, times(1)).deletePackageAsUser(eq("app3"),
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index c0a1f11..93dcbfe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -192,6 +192,27 @@
     }
 
     @Test
+    public void getConnectionSummary_shortSummary_returnShortSummary() {
+        // Test without battery level
+        // Set A2DP profile to be connected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isNull();
+
+        // Set device as Active for A2DP and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isEqualTo("Active");
+
+        // Test with battery level
+        mBatteryLevel = 10;
+        assertThat(mCachedDevice.getConnectionSummary(true /* shortSummary */)).isEqualTo(
+                "Active");
+
+        // Set A2DP profile to be disconnected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testA2dpBatteryInactive_returnBattery() {
         // Arrange:
         //   1. Profile:       {A2DP, CONNECTED, Inactive}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index dd72d57..05246a4 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -235,4 +235,7 @@
 
     <!-- Default for Settings.Secure.SILENCE_GESTURE -->
     <bool name="def_silence_gesture">false</bool>
+
+    <!-- Default for Settings.Secure.AWARE_LOCK_ENABLED -->
+    <bool name="def_aware_lock_enabled">false</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6558c87a..f7132e3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3237,7 +3237,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 180;
+            private static final int SETTINGS_VERSION = 181;
 
             private final int mUserId;
 
@@ -4401,6 +4401,25 @@
                     currentVersion = 180;
                 }
 
+                if (currentVersion == 180) {
+                    // Version 180: Set the default value for Secure Settings: AWARE_LOCK_ENABLED
+
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+
+                    final Setting awareLockEnabled = secureSettings.getSettingLocked(
+                            Secure.AWARE_LOCK_ENABLED);
+
+                    if (awareLockEnabled.isNull()) {
+                        final boolean defAwareLockEnabled = getContext().getResources().getBoolean(
+                                R.bool.def_aware_lock_enabled);
+                        secureSettings.insertSettingLocked(
+                                Secure.AWARE_LOCK_ENABLED, defAwareLockEnabled ? "1" : "0",
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    currentVersion = 181;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 16fce89..e00f204 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -170,7 +170,6 @@
     <!-- Permission needed to enable/disable Bluetooth/Wifi -->
     <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
     <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
-    <uses-permission android:name="android.permission.WATCH_APPOPS" />
     <!-- Permission needed to invoke DynamicSystem (AOT) -->
     <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
 
@@ -199,6 +198,12 @@
     <!-- Permission required to test ContentResolver caching. -->
     <uses-permission android:name="android.permission.CACHE_CONTENT" />
 
+    <!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
+    <uses-permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE" />
+    
+    <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+
     <application android:label="@string/app_label"
                  android:defaultToDeviceProtectedStorage="true"
                  android:directBootAware="true">
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b4912c..7db8969 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -222,9 +222,6 @@
     <!-- to change themes - light or dark -->
     <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
 
-    <!-- Listen app op changes -->
-    <uses-permission android:name="android.permission.WATCH_APPOPS" />
-
     <!-- to read and change hvac values in a car -->
     <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index a84ddaf..4db2280 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -34,7 +34,6 @@
               android:layout_height="wrap_content"
               android:paddingStart="44dp"
               android:paddingEnd="44dp"
-              android:letterSpacing="0.02"
               android:visibility="gone"
               android:textColor="?attr/wallpaperTextColor"
               android:theme="@style/TextAppearance.Keyguard"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 137c30a..1c8e141 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -115,7 +115,6 @@
         <item name="android:layout_height">wrap_content</item>
         <item name="android:lines">1</item>
         <item name="android:textSize">@dimen/widget_label_font_size</item>
-        <item name="android:letterSpacing">0.02</item>
     </style>
 
     <style name="TextAppearance.Keyguard.BottomArea">
diff --git a/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml
deleted file mode 100644
index c471b38..0000000
--- a/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="40"
-        android:viewportHeight="40">
-    <path
-        android:fillColor="#9AA0A6"
-        android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7266L16.2734 24.6666L20 20.94L23.7267 24.6666L24.6667 23.7266L20.94 20L24.6667 16.2733Z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/corner_gesture_hint.xml b/packages/SystemUI/res/drawable/corner_gesture_hint.xml
new file mode 100644
index 0000000..3f4abb0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/corner_gesture_hint.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2019 The Android Open Source Project
+
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="12dp"
+    android:width="12dp"
+    android:viewportWidth="12"
+    android:viewportHeight="12">
+
+    <path android:fillColor="#00000000"
+          android:pathData="M 1.18 10.65 C 1.18 5.58 5.41 1.18 10.65 1.18"
+          android:strokeColor="#000"
+          android:strokeLineCap="round"
+          android:strokeWidth="1.3" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
index 15f14d8..8b34080 100644
--- a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
+++ b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
@@ -20,6 +20,6 @@
     android:viewportWidth="40"
     android:viewportHeight="40">
     <path
-        android:fillColor="#5F6368"
+        android:fillColor="@color/notification_section_clear_all_btn_color"
         android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7267L16.2734 24.6667L20 20.94L23.7267 24.6667L24.6667 23.7267L20.94 20L24.6667 16.2733Z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index 5b93edd..db40c4f 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -30,11 +30,12 @@
     <com.android.systemui.statusbar.AlphaOptimizedButton
         style="@android:style/Widget.Material.Button.Borderless"
         android:id="@+id/settings_button"
-        android:layout_gravity="end"
+        android:layout_gravity="start"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:focusable="true"
         android:text="@string/manage_bubbles_text"
-        android:textColor="?attr/wallpaperTextColor"/>
+        android:textColor="?attr/wallpaperTextColor"
+    />
 
 </com.android.systemui.bubbles.BubbleExpandedView>
diff --git a/packages/SystemUI/res/layout/contaminant_dialog.xml b/packages/SystemUI/res/layout/contaminant_dialog.xml
new file mode 100644
index 0000000..ea6d900
--- /dev/null
+++ b/packages/SystemUI/res/layout/contaminant_dialog.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/alert_dialog.xml
+**
+** 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.
+*/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="18dp"
+        android:paddingBottom="18dp"
+        android:textAlignment="center"
+        android:fontFamily="google-sans-medium"
+        android:textSize="20sp"
+        android:textStyle="bold"
+        android:textColor="?android:attr/textColorPrimary"/>
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="24dp"
+        android:paddingRight="24dp"
+        android:paddingBottom="24dp"
+        android:textAlignment="center"
+        android:textSize="16sp"
+        android:fontFamily="roboto-regular"
+        android:textColor="?android:attr/textColorPrimary" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingBottom="8dp"
+        android:focusable="true">
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="2dp"
+            android:background="@android:drawable/divider_horizontal_bright" />
+
+        <TextView
+            android:id="@+id/learnMore"
+            style="@style/USBContaminant.UserAction" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="2dp"
+            android:background="@android:drawable/divider_horizontal_bright" />
+
+        <TextView
+            android:id="@+id/enableUsb"
+            style="@style/USBContaminant.UserAction" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="2dp"
+            android:background="@android:drawable/divider_horizontal_bright" />
+
+        <TextView
+          android:id="@+id/gotIt"
+          style="@style/USBContaminant.UserAction" />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 087e0bd..4b672ee 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -228,7 +228,7 @@
                 android:focusable="true">
                 <ImageView
                     android:id="@+id/alert_icon"
-                    android:src="@drawable/ic_notification_interruptive"
+                    android:src="@drawable/ic_notifications_alert"
                     android:background="@android:color/transparent"
                     android:layout_gravity="center"
                     android:layout_width="wrap_content"
@@ -249,6 +249,7 @@
                     android:text="@string/notification_alert_title"/>
                 <TextView
                     android:id="@+id/alert_summary"
+                    android:visibility="gone"
                     android:paddingTop="@dimen/notification_importance_button_padding"
                     android:text="@string/notification_channel_summary_default"
                     android:layout_width="match_parent"
@@ -271,7 +272,7 @@
                 android:focusable="true">
                 <ImageView
                     android:id="@+id/silence_icon"
-                    android:src="@drawable/ic_notification_gentle"
+                    android:src="@drawable/ic_notifications_silence"
                     android:background="@android:color/transparent"
                     android:layout_gravity="center"
                     android:layout_width="wrap_content"
@@ -292,6 +293,7 @@
                     android:text="@string/notification_silence_title"/>
                 <TextView
                     android:id="@+id/silence_summary"
+                    android:visibility="gone"
                     android:paddingTop="@dimen/notification_importance_button_padding"
                     android:text="@string/notification_channel_summary_default"
                     android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_customize_header.xml b/packages/SystemUI/res/layout/qs_customize_header.xml
index 58066a3..481561d 100644
--- a/packages/SystemUI/res/layout/qs_customize_header.xml
+++ b/packages/SystemUI/res/layout/qs_customize_header.xml
@@ -20,7 +20,6 @@
     android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginBottom="@dimen/qs_tile_margin_top_bottom_negative"
     android:gravity="center"
     android:minHeight="@dimen/qs_customize_header_min_height"
     android:textAppearance="@style/TextAppearance.QSEdit.Headers"
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index d0783a0..1e16e5d1 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/header_text_container"
     android:layout_width="match_parent"
     android:layout_height="@dimen/qs_header_tooltip_height"
@@ -23,25 +23,16 @@
     android:paddingEnd="@dimen/status_bar_padding_end"
     android:theme="@style/QSHeaderTheme">
 
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="start|center_vertical"
-        android:gravity="center_vertical">
-
-        <LinearLayout
+        <com.android.systemui.qs.QSHeaderInfoLayout
             android:id="@+id/status_container"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_gravity="start|center_vertical"
+            android:layout_width="match_parent"
             android:layout_weight="1"
-            android:gravity="center_vertical" >
+            android:layout_height="match_parent">
 
             <LinearLayout
                 android:id = "@+id/alarm_container"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:layout_gravity="center_vertical"
                 android:gravity="center_vertical"
                 android:focusable="true"
                 android:clickable="true">
@@ -76,7 +67,6 @@
                 android:id = "@+id/ringer_container"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:layout_gravity="center_vertical"
                 android:gravity="center_vertical"
                 android:focusable="true"
                 android:clickable="true">
@@ -98,21 +88,14 @@
                     android:textAppearance="@style/TextAppearance.QS.Status"
                     android:visibility="gone"/>
             </LinearLayout>
-        </LinearLayout>
-
-        <View
-            android:minWidth="@dimen/qs_status_separator"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
-
+        </com.android.systemui.qs.QSHeaderInfoLayout>
 
         <include layout="@layout/qs_carrier_group"
                  android:id="@+id/carrier_group"
                  android:layout_width="wrap_content"
                  android:layout_height="match_parent"
+                 android:layout_marginStart="@dimen/qs_status_separator"
                  android:layout_gravity="end|center_vertical"
                  android:focusable="false"/>
-    </LinearLayout>
 
-</FrameLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index 26cf792..02651a2 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -32,4 +32,22 @@
         android:tint="#ff000000"
         android:layout_gravity="right|bottom"
         android:src="@drawable/rounded" />
+    <ImageView
+        android:id="@+id/assist_hint_left"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:padding="6dp"
+        android:layout_gravity="left|top"
+        android:src="@drawable/corner_gesture_hint"
+        android:tint="#ffffffff"
+        android:visibility="gone" />
+    <ImageView
+        android:id="@+id/assist_hint_right"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:padding="6dp"
+        android:layout_gravity="right|bottom"
+        android:src="@drawable/corner_gesture_hint"
+        android:tint="#ffffffff"
+        android:visibility="gone" />
 </com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index 2b21006..0c3c597 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -43,7 +43,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:layout_marginLeft="@dimen/notification_section_header_padding_left"
+            android:layout_marginStart="@dimen/notification_section_header_padding_left"
             android:text="@string/notification_section_header_gentle"
             android:textSize="12sp"
             android:textColor="@color/notification_section_header_label_color"
@@ -52,9 +52,10 @@
             android:id="@+id/btn_clear_all"
             android:layout_width="@dimen/notification_section_header_height"
             android:layout_height="@dimen/notification_section_header_height"
-            android:layout_marginRight="4dp"
+            android:layout_marginEnd="4dp"
             android:src="@drawable/status_bar_notification_section_header_clear_btn"
             android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
+            android:scaleType="center"
             />
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index e25faa2..addc10a 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -50,6 +50,7 @@
     <color name="notification_guts_button_color">@color/GM2_blue_200</color>
 
     <color name="notification_section_header_label_color">@color/GM2_grey_200</color>
+    <color name="notification_section_clear_all_btn_color">@color/GM2_grey_500</color>
     <color name="notification_channel_dialog_separator">@color/GM2_grey_700</color>
 
     <!-- The color of the background in the top part of QSCustomizer -->
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index d4bf9cd..9b72e7a 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -151,5 +151,11 @@
         <attr name="optedOut" format="boolean" />
     </declare-styleable>
 
+    <!-- Theme attributes used to style the appearance of expanded Bubbles -->
+    <declare-styleable name="BubbleExpandedView">
+        <attr name="android:colorBackgroundFloating" />
+        <attr name="android:dialogCornerRadius" />
+    </declare-styleable>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 6d7e205..490473ce 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -98,6 +98,7 @@
     <color name="notification_guts_button_color">@color/GM2_blue_700</color>
 
     <color name="notification_section_header_label_color">@color/GM2_grey_900</color>
+    <color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
     <!-- The divider view for the notification channel editor half-shelf -->
     <color name="notification_channel_dialog_separator">@color/GM2_grey_200</color>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index aecae63..fb226c7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -41,10 +41,10 @@
 
     <!-- Size of the nav bar edge panels, should be greater to the
          edge sensitivity + the drag threshold -->
-    <dimen name="navigation_edge_panel_width">76dp</dimen>
+    <dimen name="navigation_edge_panel_width">70dp</dimen>
     <!-- Padding at the end of the navigation panel to allow the arrow not to be clipped off -->
-    <dimen name="navigation_edge_panel_padding">24dp</dimen>
-    <dimen name="navigation_edge_panel_height">84dp</dimen>
+    <dimen name="navigation_edge_panel_padding">8dp</dimen>
+    <dimen name="navigation_edge_panel_height">96dp</dimen>
     <!-- The threshold to drag to trigger the edge action -->
     <dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
     <!-- The minimum display position of the arrow on the screen -->
@@ -442,8 +442,11 @@
     <dimen name="qs_tile_margin_vertical">24dp</dimen>
     <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen>
-    <!-- The height of the qs customize header. Should be (28dp - qs_tile_margin_top_bottom). -->
-    <dimen name="qs_customize_header_min_height">40dp</dimen>
+    <!-- The height of the qs customize header. Should be
+         (qs_panel_padding_top (48dp) +  brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
+         (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
+    -->
+    <dimen name="qs_customize_header_min_height">46dp</dimen>
     <dimen name="qs_tile_margin_top">18dp</dimen>
     <dimen name="qs_tile_background_size">44dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
@@ -698,7 +701,7 @@
     <!-- The top padding of the clear all button -->
     <dimen name="clear_all_padding_top">12dp</dimen>
 
-    <dimen name="notification_section_header_height">40dp</dimen>
+    <dimen name="notification_section_header_height">48dp</dimen>
     <dimen name="notification_section_header_padding_left">16dp</dimen>
 
     <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e01e6a8..9831250 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -166,7 +166,7 @@
     <string name="usb_contaminant_title">USB port disabled</string>
 
     <!-- Message of USB contaminant presence dialog [CHAR LIMIT=NONE] -->
-    <string name="usb_contaminant_message">To protect your device from liquid or debris, the USB port is disabled and won\u2019t detect any accessories.\n\nYou\u2019ll be notified when it\u2019s safe to use the USB port again.</string>
+    <string name="usb_contaminant_message">To protect your device from liquid or debris, the USB port is disabled and won\u2019t detect any accessories.\n\nYou\u2019ll be notified when it\u2019s okay to use the USB port again.</string>
 
     <!-- Toast for enabling ports from USB contaminant dialog [CHAR LIMIT=NONE] -->
     <string name="usb_port_enabled">USB port enabled to detect chargers and accessories</string>
@@ -174,6 +174,9 @@
     <!-- Button text to disable contaminant detection [CHAR LIMIT=NONE] -->
     <string name="usb_disable_contaminant_detection">Enable USB</string>
 
+    <!-- Button text to disable contaminant detection [CHAR LIMIT=NONE] -->
+    <string name="learn_more">Learn more</string>
+
     <!-- Checkbox label for application compatibility mode ON (zooming app to look like it's running
          on a phone).  [CHAR LIMIT=25] -->
     <string name="compat_mode_on">Zoom to fill screen</string>
@@ -1100,7 +1103,10 @@
     <string name="battery_saver_notification_action_text">Turn off Battery Saver</string>
 
     <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] -->
-    <string name="media_projection_dialog_text">While recording or casting, <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> can capture any sensitive information, such as audio that you play and your passwords, payment info, photos, and messages.</string>
+    <string name="media_projection_dialog_text">While recording or casting, <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> can capture any sensitive information that is displayed on your screen or played from your device, including sensitive information such as audio, passwords, payment info, photos and messages.</string>
+
+    <!-- Media projection permission dialog warning text for system services. [CHAR LIMIT=NONE] -->
+    <string name="media_projection_dialog_service_text">While recording or casting, the service providing this function can capture any sensitive information that is displayed on your screen or played from your device, including sensitive information such as audio, passwords, payment info, photos and messages.</string>
 
     <!-- Media projection permission dialog warning title. [CHAR LIMIT=NONE] -->
     <string name="media_projection_dialog_title">Exposing sensitive info during casting/recording </string>
@@ -1115,10 +1121,10 @@
     <string name="manage_notifications_text">Manage</string>
 
     <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
-    <string name="notification_section_header_gentle">Gentle notifications</string>
+    <string name="notification_section_header_gentle">Silent notifications</string>
 
     <!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
-    <string name="accessibility_notification_section_header_gentle_clear_all">Clear all gentle notifications</string>
+    <string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
 
     <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
     <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
@@ -1653,13 +1659,13 @@
     <string name="inline_minimize_button">Minimize</string>
 
     <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
-    <string name="inline_silent_button_silent">Gentle</string>
+    <string name="inline_silent_button_silent">Silent</string>
 
     <!-- Notification inline controls: button to continue showing notifications silently [CHAR_LIMIT=35] -->
     <string name="inline_silent_button_stay_silent">Stay silent</string>
 
     <!-- Notification inline controls: button to make notifications alert the user [CHAR_LIMIT=35] -->
-    <string name="inline_silent_button_alert">Interruptive</string>
+    <string name="inline_silent_button_alert">Alerting</string>
 
     <!-- Notification inline controls: button to continue alerting the user when notifications arrive [CHAR_LIMIT=35] -->
     <string name="inline_silent_button_keep_alerting">Keep alerting</string>
@@ -2372,7 +2378,7 @@
     <string name="auto_saver_title">Tap to schedule Battery Saver</string>
 
     <!-- The content of the notification to suggest enabling automatic battery saver.  [CHAR LIMIT=NONE]-->
-    <string name="auto_saver_text">Turn on automatically when battery is at <xliff:g id="percentage">%d</xliff:g>%%</string>
+    <string name="auto_saver_text">Turn on when battery is likely to run out</string>
 
     <!-- An action on the notification to suggest enabling automatic battery saver: Do not turn on automatic battery saver.  [CHAR LIMIT=NONE]-->
     <string name="no_auto_saver_action">No thanks</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 59ed5ce..04cd30c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -572,4 +572,22 @@
         <item name="android:backgroundDimEnabled">true</item>
         <item name="android:windowCloseOnTouchOutside">true</item>
     </style>
+
+    <!-- USB Contaminant dialog -->
+    <style name ="USBContaminant" />
+
+    <style name ="USBContaminant.UserAction">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:fontFamily">roboto-regular</item>
+        <item name="android:paddingLeft">16dp</item>
+        <item name="android:paddingTop">16dp</item>
+        <item name="android:paddingRight">24dp</item>
+        <item name="android:paddingBottom">16dp</item>
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:clickable">true</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index b826b30..77bb514 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -73,6 +73,12 @@
     void onAssistantProgress(float progress) = 12;
 
     /**
+    * Proxies the assistant gesture fling velocity (in pixels per millisecond) upon completion.
+    * Velocity is 0 for drag gestures.
+    */
+    void onAssistantGestureCompletion(float velocity) = 18;
+
+    /**
      * Start the assistant.
      */
     void startAssistant(in Bundle bundle) = 13;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index b89b9ef..cc3a67c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -28,6 +28,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.StringJoiner;
 
 /**
  * Various shared constants between Launcher and SysUI as part of quickstep
@@ -35,7 +36,6 @@
 public class QuickStepContract {
 
     public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
-    public static final String KEY_EXTRA_INPUT_CHANNEL = "extra_input_channel";
     public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor";
     public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
     public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
@@ -47,12 +47,26 @@
     public static final String NAV_BAR_MODE_GESTURAL_OVERLAY =
             WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
+    // Overview is disabled, either because the device is in lock task mode, or because the device
+    // policy has disabled the feature
     public static final int SYSUI_STATE_SCREEN_PINNING = 1 << 0;
+    // The navigation bar is hidden due to immersive mode
     public static final int SYSUI_STATE_NAV_BAR_HIDDEN = 1 << 1;
+    // The notification panel is expanded and interactive (either locked or unlocked), and the
+    // quick settings is not expanded
     public static final int SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED = 1 << 2;
+    // The keyguard bouncer is showing
     public static final int SYSUI_STATE_BOUNCER_SHOWING = 1 << 3;
+    // The navigation bar a11y button should be shown
     public static final int SYSUI_STATE_A11Y_BUTTON_CLICKABLE = 1 << 4;
+    // The navigation bar a11y button shortcut is available
     public static final int SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE = 1 << 5;
+    // The keyguard is showing
+    public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING = 1 << 6;
+    // The recents feature is disabled (either by SUW/SysUI/device policy)
+    public static final int SYSUI_STATE_OVERVIEW_DISABLED = 1 << 7;
+    // The home feature is disabled (either by SUW/SysUI/device policy)
+    public static final int SYSUI_STATE_HOME_DISABLED = 1 << 8;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -60,10 +74,27 @@
             SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
             SYSUI_STATE_BOUNCER_SHOWING,
             SYSUI_STATE_A11Y_BUTTON_CLICKABLE,
-            SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE
+            SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE,
+            SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+            SYSUI_STATE_OVERVIEW_DISABLED,
+            SYSUI_STATE_HOME_DISABLED
     })
     public @interface SystemUiStateFlags {}
 
+    public static String getSystemUiStateString(int flags) {
+        StringJoiner str = new StringJoiner("|");
+        str.add((flags & SYSUI_STATE_SCREEN_PINNING) != 0 ? "screen_pinned" : "");
+        str.add((flags & SYSUI_STATE_OVERVIEW_DISABLED) != 0 ? "overview_disabled" : "");
+        str.add((flags & SYSUI_STATE_HOME_DISABLED) != 0 ? "home_disabled" : "");
+        str.add((flags & SYSUI_STATE_NAV_BAR_HIDDEN) != 0 ? "navbar_hidden" : "");
+        str.add((flags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0 ? "notif_visible" : "");
+        str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) != 0 ? "keygrd_visible" : "");
+        str.add((flags & SYSUI_STATE_BOUNCER_SHOWING) != 0 ? "bouncer_visible" : "");
+        str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
+        str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
+        return str.toString();
+    }
+
     /**
      * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
      * home button press/long press over are ignored and will start to drag when exceeded and the
@@ -87,6 +118,37 @@
     }
 
     /**
+     * Returns whether the specified sysui state is such that the assistant gesture should be
+     * disabled.
+     */
+    public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
+        // Disable when in screen pinning, immersive, the bouncer is showing, or the notifications
+        // are interactive
+        int disableFlags = SYSUI_STATE_SCREEN_PINNING
+                | SYSUI_STATE_NAV_BAR_HIDDEN
+                | SYSUI_STATE_BOUNCER_SHOWING
+                | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+        return (sysuiStateFlags & disableFlags) != 0;
+    }
+
+    /**
+     * Returns whether the specified sysui state is such that the back gesture should be
+     * disabled.
+     */
+    public static boolean isBackGestureDisabled(int sysuiStateFlags) {
+        // Always allow when the bouncer is showing (even on top of the keyguard)
+        if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0) {
+            return false;
+        }
+        // Disable when in screen pinning, immersive, or the notifications are interactive
+        int disableFlags = SYSUI_STATE_SCREEN_PINNING
+                | SYSUI_STATE_NAV_BAR_HIDDEN
+                | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+                | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+        return (sysuiStateFlags & disableFlags) != 0;
+    }
+
+    /**
      * @return whether this nav bar mode is edge to edge
      */
     public static boolean isGesturalMode(int mode) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 21b3a00..bd2b19c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -64,6 +64,14 @@
         onActivityLaunchOnSecondaryDisplayRerouted();
     }
 
+    /**
+     * Called when contents are drawn for the first time on a display which can only contain one
+     * task.
+     *
+     * @param displayId the id of the display on which contents are drawn.
+     */
+    public void onSingleTaskDisplayDrawn(int displayId) { }
+
     public void onTaskProfileLocked(int taskId, int userId) { }
     public void onTaskCreated(int taskId, ComponentName componentName) { }
     public void onTaskRemoved(int taskId) { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 06ae399..c89f2ab 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -196,11 +196,18 @@
     }
 
     @Override
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
+    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
+            throws RemoteException {
         mHandler.obtainMessage(H.ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, 0 /* unused */,
                 activityToken).sendToTarget();
     }
 
+    @Override
+    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
+                0 /* unused */).sendToTarget();
+    }
+
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -220,6 +227,7 @@
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16;
         private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17;
         private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18;
+        private static final int ON_SINGLE_TASK_DISPLAY_DRAWN = 19;
 
 
         public H(Looper looper) {
@@ -356,6 +364,12 @@
                         }
                         break;
                     }
+                    case ON_SINGLE_TASK_DISPLAY_DRAWN: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onSingleTaskDisplayDrawn(msg.arg1);
+                        }
+                        break;
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 2ff7266..a4b6958 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -310,7 +310,9 @@
 
     @Override
     public void showMessage(CharSequence message, ColorStateList colorState) {
-        mSecurityMessageDisplay.setNextMessageColor(colorState);
+        if (colorState != null) {
+            mSecurityMessageDisplay.setNextMessageColor(colorState);
+        }
         mSecurityMessageDisplay.setMessage(message);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index d0b2c58..0bb9e744 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -3,6 +3,7 @@
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -412,15 +413,10 @@
         mClockTransition.setScale(smallFontSize / bigFontSize);
         mBoldClockTransition.setScale(bigFontSize / smallFontSize);
 
-        TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
-        mClockView.setVisibility(hasHeader ? View.INVISIBLE : View.VISIBLE);
-        mClockViewBold.setVisibility(hasHeader ? View.VISIBLE : View.INVISIBLE);
-        int paddingBottom = mContext.getResources().getDimensionPixelSize(hasHeader
-                ? R.dimen.widget_vertical_padding_clock : R.dimen.title_clock_padding);
-        mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
-                mClockView.getPaddingRight(), paddingBottom);
-        mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(),
-                mClockViewBold.getPaddingRight(), paddingBottom);
+        // End any current transitions before starting a new transition so that the new transition
+        // starts from a good state instead of a potentially bad intermediate state arrived at
+        // during a transition animation.
+        TransitionManager.endTransitions((ViewGroup) mClockView.getParent());
 
         if (hasHeader) {
             // After the transition, make the default clock GONE so that it doesn't make the
@@ -439,6 +435,16 @@
                 }
             });
         }
+
+        TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
+        mClockView.setVisibility(hasHeader ? View.INVISIBLE : View.VISIBLE);
+        mClockViewBold.setVisibility(hasHeader ? View.VISIBLE : View.INVISIBLE);
+        int paddingBottom = mContext.getResources().getDimensionPixelSize(hasHeader
+                ? R.dimen.widget_vertical_padding_clock : R.dimen.title_clock_padding);
+        mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
+                mClockView.getPaddingRight(), paddingBottom);
+        mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(),
+                mClockViewBold.getPaddingRight(), paddingBottom);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
@@ -553,7 +559,6 @@
         private Animator createAnimator(View view, float cutoff, int startVisibility,
                 int endVisibility, float startScale, float endScale) {
             view.setPivotY(view.getHeight() - view.getPaddingBottom());
-            view.setVisibility(startVisibility);
             ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
             animator.addUpdateListener(animation -> {
                 final float fraction = animation.getAnimatedFraction();
@@ -564,6 +569,14 @@
                 view.setScaleX(scale);
                 view.setScaleY(scale);
             });
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    super.onAnimationStart(animation);
+                    view.setVisibility(startVisibility);
+                    animation.removeListener(this);
+                }
+            });
             addListener(new TransitionListenerAdapter() {
                 @Override
                 public void onTransitionEnd(Transition transition) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 0e91e41..362ead3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -64,6 +64,9 @@
     // How much we scale up the duration of the disappear animation when the current user is locked
     public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
 
+    // Extra padding, in pixels, that should eat touch events.
+    private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40;
+
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final AppearAnimationUtils mAppearAnimationUtils;
     private final DisappearAnimationUtils mDisappearAnimationUtils;
@@ -204,9 +207,10 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
         mLockPatternView.getLocationOnScreen(mTmpPosition);
-        mLockPatternScreenBounds.set(mTmpPosition[0], mTmpPosition[1],
-                mTmpPosition[0] + mLockPatternView.getWidth(),
-                mTmpPosition[1] + mLockPatternView.getHeight());
+        mLockPatternScreenBounds.set(mTmpPosition[0] - PATTERNS_TOUCH_AREA_EXTENSION,
+                mTmpPosition[1] - PATTERNS_TOUCH_AREA_EXTENSION,
+                mTmpPosition[0] + mLockPatternView.getWidth() + PATTERNS_TOUCH_AREA_EXTENSION,
+                mTmpPosition[1] + mLockPatternView.getHeight() + PATTERNS_TOUCH_AREA_EXTENSION);
     }
 
     @Override
@@ -438,7 +442,9 @@
 
     @Override
     public void showMessage(CharSequence message, ColorStateList colorState) {
-        mSecurityMessageDisplay.setNextMessageColor(colorState);
+        if (colorState != null) {
+            mSecurityMessageDisplay.setNextMessageColor(colorState);
+        }
         mSecurityMessageDisplay.setMessage(message);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 0914fb8..906ace7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -71,6 +71,8 @@
     private static final float TOUCH_Y_MULTIPLIER = 0.25f;
     // How much you need to drag the bouncer to trigger an auth retry (in dps.)
     private static final float MIN_DRAG_SIZE = 10;
+    // How much to scale the default slop by, to avoid accidental drags.
+    private static final float SLOP_SCALE = 2f;
 
     private KeyguardSecurityModel mSecurityModel;
     private LockPatternUtils mLockPatternUtils;
@@ -179,7 +181,7 @@
                     return false;
                 }
                 int index = event.findPointerIndex(mActivePointerId);
-                int touchSlop = mViewConfiguration.getScaledTouchSlop();
+                float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE;
                 if (mCurrentSecurityView != null && index != -1
                         && mStartTouchY - event.getY(index) > touchSlop) {
                     mIsDragging = true;
@@ -229,8 +231,10 @@
         }
         if (action == MotionEvent.ACTION_UP) {
             if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    MIN_DRAG_SIZE, getResources().getDisplayMetrics())) {
+                    MIN_DRAG_SIZE, getResources().getDisplayMetrics())
+                    && !mUpdateMonitor.isFaceDetectionRunning()) {
                 mUpdateMonitor.requestFaceAuth();
+                showMessage(null, null);
             }
         }
         return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ea8565e..873874f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -168,6 +168,9 @@
      */
     private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
 
+    private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
+    public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
+
     private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
 
     private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
@@ -570,7 +573,8 @@
                 cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
             }
         }
-        handleFingerprintHelp(-1, mContext.getString(R.string.kg_fingerprint_not_recognized));
+        handleFingerprintHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                mContext.getString(R.string.kg_fingerprint_not_recognized));
     }
 
     private void handleFingerprintAcquired(int acquireInfo) {
@@ -722,7 +726,8 @@
                 cb.onBiometricAuthFailed(BiometricSourceType.FACE);
             }
         }
-        handleFaceHelp(-1, mContext.getString(R.string.kg_face_not_recognized));
+        handleFaceHelp(BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+                mContext.getString(R.string.kg_face_not_recognized));
     }
 
     private void handleFaceAcquired(int acquireInfo) {
@@ -803,6 +808,11 @@
                     getCurrentUser());
         }
 
+        // The face timeout message is not very actionable, let's ask the user to
+        // manually retry.
+        if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+            errString = mContext.getString(R.string.keyguard_unlock);
+        }
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1187,7 +1197,7 @@
                 throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
             }
             String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
-            int slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, 0);
+            int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
             int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
             if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
@@ -1994,7 +2004,8 @@
         boolean becameAbsent = false;
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.w(TAG, "invalid subId in handleSimStateChange()");
-            /* Only handle No SIM(ABSENT) due to handleServiceStateChange() handle other case */
+            /* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to
+             * handleServiceStateChange() handle other case */
             if (state == State.ABSENT) {
                 updateTelephonyCapable(true);
                 // Even though the subscription is not valid anymore, we need to notify that the
@@ -2007,6 +2018,8 @@
                         data.simState = State.ABSENT;
                     }
                 }
+            } else if (state == State.CARD_IO_ERROR) {
+                updateTelephonyCapable(true);
             } else {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index ed2a6b5..2be5743 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,13 +16,22 @@
 
 package com.android.systemui;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Rect;
-import android.opengl.GLSurfaceView;
 import android.service.wallpaper.WallpaperService;
+import android.util.Log;
+import android.util.Size;
 import android.view.SurfaceHolder;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.glwallpaper.EglHelper;
+import com.android.systemui.glwallpaper.GLWallpaperRenderer;
 import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.DozeParameters;
 
 /**
  * Default built-in wallpaper that simply shows a static image.
@@ -30,112 +39,183 @@
 @SuppressWarnings({"UnusedDeclaration"})
 public class ImageWallpaper extends WallpaperService {
     private static final String TAG = ImageWallpaper.class.getSimpleName();
+    // We delayed destroy render context that subsequent render requests have chance to cancel it.
+    // This is to avoid destroying then recreating render context in a very short time.
+    private static final int DELAY_FINISH_RENDERING = 1000;
 
     @Override
     public Engine onCreateEngine() {
         return new GLEngine(this);
     }
 
-    class GLEngine extends Engine {
-        private GLWallpaperSurfaceView mWallpaperSurfaceView;
+    class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
+        // Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
+        // set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
+        @VisibleForTesting
+        static final int MIN_SURFACE_WIDTH = 64;
+        @VisibleForTesting
+        static final int MIN_SURFACE_HEIGHT = 64;
+
+        private GLWallpaperRenderer mRenderer;
+        private EglHelper mEglHelper;
+        private StatusBarStateController mController;
+        private final Runnable mFinishRenderingTask = this::finishRendering;
+        private final boolean mNeedTransition;
+        private boolean mNeedRedraw;
 
         GLEngine(Context context) {
-            mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
-            mWallpaperSurfaceView.setRenderer(
-                    new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
-            mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
-            setOffsetNotificationsEnabled(true);
+            mNeedTransition = ActivityManager.isHighEndGfx()
+                    && !DozeParameters.getInstance(context).getDisplayNeedsBlanking();
+
+            // We will preserve EGL context when we are in lock screen or aod
+            // to avoid janking in following transition, we need to release when back to home.
+            mController = Dependency.get(StatusBarStateController.class);
+            if (mController != null) {
+                mController.addCallback(this /* StateListener */);
+            }
+            mEglHelper = new EglHelper();
+            mRenderer = new ImageWallpaperRenderer(context, this /* SurfaceProxy */);
+        }
+
+        @Override
+        public void onCreate(SurfaceHolder surfaceHolder) {
+            setFixedSizeAllowed(true);
+            setOffsetNotificationsEnabled(false);
+            updateSurfaceSize();
+        }
+
+        private void updateSurfaceSize() {
+            SurfaceHolder holder = getSurfaceHolder();
+            Size frameSize = mRenderer.reportSurfaceSize();
+            int width = Math.max(MIN_SURFACE_WIDTH, frameSize.getWidth());
+            int height = Math.max(MIN_SURFACE_HEIGHT, frameSize.getHeight());
+            holder.setFixedSize(width, height);
         }
 
         @Override
         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
-            if (mWallpaperSurfaceView != null) {
-                mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration);
-            }
-        }
-
-        @Override
-        public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
-                float yOffsetStep, int xPixelOffset, int yPixelOffset) {
-            if (mWallpaperSurfaceView != null) {
-                mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
-            }
+            mRenderer.updateAmbientMode(inAmbientMode,
+                    (mNeedTransition || animationDuration != 0) ? animationDuration : 0);
         }
 
         @Override
         public void onDestroy() {
-            if (mWallpaperSurfaceView != null) {
-                mWallpaperSurfaceView.onPause();
+            if (mController != null) {
+                mController.removeCallback(this /* StateListener */);
             }
+            mController = null;
+            mRenderer.finish();
+            mRenderer = null;
+            mEglHelper.finish();
+            mEglHelper = null;
+            getSurfaceHolder().getSurface().hwuiDestroy();
         }
 
-        private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
-            private WallpaperStatusListener mWallpaperStatusListener;
+        @Override
+        public void onSurfaceCreated(SurfaceHolder holder) {
+            mEglHelper.init(holder);
+            mRenderer.onSurfaceCreated();
+        }
 
-            GLWallpaperSurfaceView(Context context) {
-                super(context);
-                setEGLContextClientVersion(2);
-            }
+        @Override
+        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            mRenderer.onSurfaceChanged(width, height);
+            mNeedRedraw = true;
+        }
 
-            @Override
-            public SurfaceHolder getHolder() {
-                return getSurfaceHolder();
-            }
-
-            @Override
-            public void setRenderer(Renderer renderer) {
-                super.setRenderer(renderer);
-                mWallpaperStatusListener = (WallpaperStatusListener) renderer;
-            }
-
-            private void notifyAmbientModeChanged(boolean inAmbient, long duration) {
-                if (mWallpaperStatusListener != null) {
-                    mWallpaperStatusListener.onAmbientModeChanged(inAmbient, duration);
-                }
-            }
-
-            private void notifyOffsetsChanged(float xOffset, float yOffset) {
-                if (mWallpaperStatusListener != null) {
-                    mWallpaperStatusListener.onOffsetsChanged(
-                            xOffset, yOffset, getHolder().getSurfaceFrame());
-                }
-            }
-
-            @Override
-            public void render() {
+        @Override
+        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
+            if (mNeedRedraw) {
+                preRender();
                 requestRender();
+                postRender();
+                mNeedRedraw = false;
             }
         }
-    }
 
-    /**
-     * A listener to trace status of image wallpaper.
-     */
-    public interface WallpaperStatusListener {
+        @Override
+        public SurfaceHolder getHolder() {
+            return getSurfaceHolder();
+        }
 
-        /**
-         * Called back while ambient mode changes.
-         * @param inAmbientMode true if is in ambient mode, false otherwise.
-         * @param duration the duration of animation.
-         */
-        void onAmbientModeChanged(boolean inAmbientMode, long duration);
+        @Override
+        public void onStatePostChange() {
+            // When back to home, we try to release EGL, which is preserved in lock screen or aod.
+            if (mController.getState() == StatusBarState.SHADE) {
+                scheduleFinishRendering();
+            }
+        }
 
-        /**
-         * Called back while wallpaper offsets.
-         * @param xOffset The offset portion along x.
-         * @param yOffset The offset portion along y.
-         */
-        void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
-    }
+        @Override
+        public void preRender() {
+            boolean contextRecreated = false;
+            Rect frame = getSurfaceHolder().getSurfaceFrame();
+            getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
 
-    /**
-     * An abstraction for view of GLRenderer.
-     */
-    public interface ImageGLView {
+            // Check if we need to recreate egl context.
+            if (!mEglHelper.hasEglContext()) {
+                mEglHelper.destroyEglSurface();
+                if (!mEglHelper.createEglContext()) {
+                    Log.w(TAG, "recreate egl context failed!");
+                } else {
+                    contextRecreated = true;
+                }
+            }
 
-        /**
-         * Ask the view to render.
-         */
-        void render();
+            // Check if we need to recreate egl surface.
+            if (mEglHelper.hasEglContext() && !mEglHelper.hasEglSurface()) {
+                if (!mEglHelper.createEglSurface(getSurfaceHolder())) {
+                    Log.w(TAG, "recreate egl surface failed!");
+                }
+            }
+
+            // If we recreate egl context, notify renderer to setup again.
+            if (mEglHelper.hasEglContext() && mEglHelper.hasEglSurface() && contextRecreated) {
+                mRenderer.onSurfaceCreated();
+                mRenderer.onSurfaceChanged(frame.width(), frame.height());
+            }
+        }
+
+        @Override
+        public void requestRender() {
+            Rect frame = getSurfaceHolder().getSurfaceFrame();
+            boolean readyToRender = mEglHelper.hasEglContext() && mEglHelper.hasEglSurface()
+                    && frame.width() > 0 && frame.height() > 0;
+
+            if (readyToRender) {
+                mRenderer.onDrawFrame();
+                if (!mEglHelper.swapBuffer()) {
+                    Log.e(TAG, "drawFrame failed!");
+                }
+            } else {
+                Log.e(TAG, "requestRender: not ready, has context=" + mEglHelper.hasEglContext()
+                        + ", has surface=" + mEglHelper.hasEglSurface()
+                        + ", frame=" + frame);
+            }
+        }
+
+        @Override
+        public void postRender() {
+            scheduleFinishRendering();
+        }
+
+        private void scheduleFinishRendering() {
+            getMainThreadHandler().removeCallbacks(mFinishRenderingTask);
+            getMainThreadHandler().postDelayed(mFinishRenderingTask, DELAY_FINISH_RENDERING);
+        }
+
+        private void finishRendering() {
+            if (mEglHelper != null) {
+                mEglHelper.destroyEglSurface();
+                if (!needPreserveEglContext()) {
+                    mEglHelper.destroyEglContext();
+                }
+            }
+        }
+
+        private boolean needPreserveEglContext() {
+            return mNeedTransition && mController != null
+                    && mController.getState() == StatusBarState.KEYGUARD;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index c0f03a6..69d3af8 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -105,6 +105,7 @@
     private float mDensity;
     private WindowManager mWindowManager;
     private int mRotation;
+    private boolean mAssistHintVisible;
     private DisplayCutoutView mCutoutTop;
     private DisplayCutoutView mCutoutBottom;
     private SecureSetting mColorInversionSetting;
@@ -133,6 +134,55 @@
         mHandler = startHandlerThread();
         mHandler.post(this::startOnScreenDecorationsThread);
         setupStatusBarPaddingIfNeeded();
+        putComponent(ScreenDecorations.class, this);
+    }
+
+    private void fade(View view, boolean fadeIn) {
+        if (fadeIn) {
+            view.animate().cancel();
+            view.setAlpha(0f);
+            view.setVisibility(View.VISIBLE);
+            view.animate().alpha(1f);
+        } else {
+            view.animate().cancel();
+            view.animate().alpha(0f).withEndAction(() -> view.setVisibility(View.INVISIBLE));
+        }
+
+    }
+
+    /**
+     * Controls the visibility of the assist gesture handles.
+     *
+     * @param visible whether the handles should be shown
+     */
+    public void setAssistHintVisible(boolean visible) {
+        if (mAssistHintVisible != visible) {
+            mAssistHintVisible = visible;
+
+            View assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
+            View assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
+            View assistHintBottomLeft = mBottomOverlay.findViewById(R.id.assist_hint_left);
+            View assistHintBottomRight = mBottomOverlay.findViewById(R.id.assist_hint_right);
+
+            switch (mRotation) {
+                case RotationUtils.ROTATION_NONE:
+                    fade(assistHintBottomLeft, mAssistHintVisible);
+                    fade(assistHintBottomRight, mAssistHintVisible);
+                    break;
+                case RotationUtils.ROTATION_LANDSCAPE:
+                    fade(assistHintTopRight, mAssistHintVisible);
+                    fade(assistHintBottomRight, mAssistHintVisible);
+                    break;
+                case RotationUtils.ROTATION_SEASCAPE:
+                    fade(assistHintTopLeft, mAssistHintVisible);
+                    fade(assistHintBottomLeft, mAssistHintVisible);
+                    break;
+                case RotationUtils.ROTATION_UPSIDE_DOWN:
+                    fade(assistHintTopLeft, mAssistHintVisible);
+                    fade(assistHintTopRight, mAssistHintVisible);
+                    break;
+            }
+        }
     }
 
     @VisibleForTesting
@@ -375,12 +425,52 @@
             updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0);
         }
 
+        updateAssistantHandleViews();
         mCutoutTop.setRotation(mRotation);
         mCutoutBottom.setRotation(mRotation);
 
         updateWindowVisibilities();
     }
 
+    private void updateAssistantHandleViews() {
+        View assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
+        View assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
+        View assistHintBottomLeft = mBottomOverlay.findViewById(R.id.assist_hint_left);
+        View assistHintBottomRight = mBottomOverlay.findViewById(R.id.assist_hint_right);
+
+        final int assistHintVisibility = mAssistHintVisible ? View.VISIBLE : View.INVISIBLE;
+
+        if (mRotation == RotationUtils.ROTATION_NONE) {
+            assistHintTopLeft.setVisibility(View.GONE);
+            assistHintTopRight.setVisibility(View.GONE);
+            assistHintBottomLeft.setVisibility(assistHintVisibility);
+            assistHintBottomRight.setVisibility(assistHintVisibility);
+            updateView(assistHintBottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(assistHintBottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+        } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
+            assistHintTopLeft.setVisibility(View.GONE);
+            assistHintTopRight.setVisibility(assistHintVisibility);
+            assistHintBottomLeft.setVisibility(View.GONE);
+            assistHintBottomRight.setVisibility(assistHintVisibility);
+            updateView(assistHintTopRight, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(assistHintBottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+        } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
+            assistHintTopLeft.setVisibility(assistHintVisibility);
+            assistHintTopRight.setVisibility(assistHintVisibility);
+            assistHintBottomLeft.setVisibility(View.GONE);
+            assistHintBottomRight.setVisibility(View.GONE);
+            updateView(assistHintTopLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+            updateView(assistHintTopRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
+        } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) {
+            assistHintTopLeft.setVisibility(assistHintVisibility);
+            assistHintTopRight.setVisibility(View.GONE);
+            assistHintBottomLeft.setVisibility(assistHintVisibility);
+            assistHintBottomRight.setVisibility(View.GONE);
+            updateView(assistHintTopLeft, Gravity.BOTTOM | Gravity.RIGHT, 180);
+            updateView(assistHintBottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
+        }
+    }
+
     private void updateView(View v, int gravity, int rotation) {
         ((FrameLayout.LayoutParams)v.getLayoutParams()).gravity = gravity;
         v.setRotation(rotation);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 68cf15d..51bd9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -14,7 +14,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -59,6 +58,7 @@
     private static final String ASSIST_ICON_METADATA_NAME =
             "com.android.systemui.action_assist_icon";
     private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms";
+    private static final String INVOCATION_PHONE_STATE_KEY = "invocation_phone_state";
     public static final String INVOCATION_TYPE_KEY = "invocation_type";
 
     public static final int INVOCATION_TYPE_GESTURE = 1;
@@ -74,6 +74,7 @@
     private final WindowManager mWindowManager;
     private final AssistDisclosure mAssistDisclosure;
     private final InterestingConfigChanges mInterestingConfigChanges;
+    private final PhoneStateMonitor mPhoneStateMonitor;
 
     private AssistOrbContainer mView;
     private final DeviceProvisionedController mDeviceProvisionedController;
@@ -108,6 +109,7 @@
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mAssistUtils = new AssistUtils(context);
         mAssistDisclosure = new AssistDisclosure(context, new Handler());
+        mPhoneStateMonitor = new PhoneStateMonitor(context);
 
         registerVoiceInteractionSessionListener();
         mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
@@ -187,6 +189,7 @@
         if (args == null) {
             args = new Bundle();
         }
+        args.putInt(INVOCATION_PHONE_STATE_KEY, mPhoneStateMonitor.getPhoneState());
         args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.uptimeMillis());
         // Logs assistant start with invocation type.
         MetricsLogger.action(
@@ -195,31 +198,19 @@
         startAssistInternal(args, assistComponent, isService);
     }
 
-    /**
-     * Returns a {@code Rect} containing system UI presented on behalf of the assistant that
-     * consumes touches.
-     */
-    @Nullable
-    public Rect getTouchableRegion() {
-        // intentional no-op, vendor's AssistManager implementation should override if needed.
-        return null;
-    }
-
-    /** Registers a listener for changes to system UI presented on behalf of the assistant. */
-    public void setAssistSysUiChangeListener(AssistSysUiChangeListener listener) {
-        // intentional no-op, vendor's AssistManager implementation should override if needed.
-    }
-
-    /** Returns {@code true} if the system UI is showing UI for the assistant. */
-    public boolean hasAssistUi() {
-        return false;
-    }
-
     /** Called when the user is performing an assistant invocation action (e.g. Active Edge) */
     public void onInvocationProgress(int type, float progress) {
         // intentional no-op, vendor's AssistManager implementation should override if needed.
     }
 
+    /** Called when the user has invoked the assistant with the incoming velocity, in pixels per
+     * millisecond. For invocations without a velocity (e.g. slow drag), the velocity is set to
+     * zero.
+     */
+    public void onAssistantGestureCompletion(float velocity) {
+        // intentional no-op, vendor's AssistManager implementation should override if needed.
+    }
+
     public void hideAssist() {
         mAssistUtils.hideCurrentSession();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistSysUiChangeListener.java b/packages/SystemUI/src/com/android/systemui/assist/AssistSysUiChangeListener.java
deleted file mode 100644
index d03afb6..0000000
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistSysUiChangeListener.java
+++ /dev/null
@@ -1,27 +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.assist;
-
-/**
- * Used to notify when system UI is showing UI for the assistant.
- */
-public interface AssistSysUiChangeListener {
-
-    /** Called when the visibility of system UI for the assistant has changed. */
-    void onChange(boolean isVisible);
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
new file mode 100644
index 0000000..ea08c33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist;
+
+import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
+
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.ArrayList;
+
+/** Class to monitor and report the state of the phone. */
+final class PhoneStateMonitor {
+
+    private static final int PHONE_STATE_AOD1 = 1;
+    private static final int PHONE_STATE_AOD2 = 2;
+    private static final int PHONE_STATE_BOUNCER = 3;
+    private static final int PHONE_STATE_UNLOCKED_LOCKSCREEN = 4;
+    private static final int PHONE_STATE_HOME = 5;
+    private static final int PHONE_STATE_OVERVIEW = 6;
+    private static final int PHONE_STATE_ALL_APPS = 7;
+    private static final int PHONE_STATE_APP_DEFAULT = 8;
+    private static final int PHONE_STATE_APP_IMMERSIVE = 9;
+    private static final int PHONE_STATE_APP_FULLSCREEN = 10;
+
+    private final Context mContext;
+    private final StatusBarStateController mStatusBarStateController;
+
+    private boolean mLauncherShowing;
+    @Nullable private ComponentName mDefaultHome;
+
+    PhoneStateMonitor(Context context) {
+        mContext = context;
+        mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+
+        ActivityManagerWrapper activityManagerWrapper = ActivityManagerWrapper.getInstance();
+        mDefaultHome = PackageManagerWrapper.getInstance().getHomeActivities(new ArrayList<>());
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mDefaultHome =
+                        PackageManagerWrapper.getInstance().getHomeActivities(new ArrayList<>());
+            }
+        }, new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
+        mLauncherShowing = isLauncherShowing(activityManagerWrapper.getRunningTask());
+        activityManagerWrapper.registerTaskStackListener(new TaskStackChangeListener() {
+            @Override
+            public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+                mLauncherShowing = isLauncherShowing(taskInfo);
+            }
+        });
+    }
+
+    int getPhoneState() {
+        int phoneState;
+        if (isShadeFullscreen()) {
+            phoneState = getPhoneLockscreenState();
+        } else if (mLauncherShowing) {
+            phoneState = getPhoneLauncherState();
+        } else {
+            phoneState = getPhoneAppState();
+        }
+        return phoneState;
+    }
+
+    private int getPhoneLockscreenState() {
+        if (isDozing()) {
+            return PHONE_STATE_AOD1;
+        } else if (isBouncerShowing()) {
+            return PHONE_STATE_BOUNCER;
+        } else if (isKeyguardLocked()) {
+            return PHONE_STATE_AOD2;
+        } else {
+            return PHONE_STATE_UNLOCKED_LOCKSCREEN;
+        }
+    }
+
+    private int getPhoneLauncherState() {
+        if (isLauncherInOverview()) {
+            return PHONE_STATE_OVERVIEW;
+        } else if (isLauncherInAllApps()) {
+            return PHONE_STATE_ALL_APPS;
+        } else {
+            return PHONE_STATE_HOME;
+        }
+    }
+
+    private int getPhoneAppState() {
+        if (isAppImmersive()) {
+            return PHONE_STATE_APP_IMMERSIVE;
+        } else if (isAppFullscreen()) {
+            return PHONE_STATE_APP_FULLSCREEN;
+        } else {
+            return PHONE_STATE_APP_DEFAULT;
+        }
+    }
+
+    private boolean isShadeFullscreen() {
+        int statusBarState = mStatusBarStateController.getState();
+        return statusBarState == StatusBarState.KEYGUARD
+                || statusBarState == StatusBarState.SHADE_LOCKED;
+    }
+
+    private boolean isDozing() {
+        return mStatusBarStateController.isDozing();
+    }
+
+    private boolean isLauncherShowing(ActivityManager.RunningTaskInfo runningTaskInfo) {
+        if (runningTaskInfo == null) {
+            return false;
+        } else {
+            return runningTaskInfo.topActivity.equals(mDefaultHome);
+        }
+    }
+
+    private boolean isAppImmersive() {
+        return SysUiServiceProvider.getComponent(mContext, StatusBar.class).inImmersiveMode();
+    }
+
+    private boolean isAppFullscreen() {
+        return SysUiServiceProvider.getComponent(mContext, StatusBar.class).inFullscreenMode();
+    }
+
+    private boolean isBouncerShowing() {
+        StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+        return statusBar != null && statusBar.isBouncerShowing();
+    }
+
+    private boolean isKeyguardLocked() {
+        // TODO: Move binder call off of critical path
+        KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
+        return keyguardManager != null && keyguardManager.isKeyguardLocked();
+    }
+
+    private boolean isLauncherInOverview() {
+        // TODO
+        return false;
+    }
+
+    private boolean isLauncherInAllApps() {
+        // TODO
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index f60e95e..5c6c397 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,6 +16,8 @@
 package com.android.systemui.bubbles;
 
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
 import android.content.Context;
@@ -129,6 +131,20 @@
         mInflated = true;
     }
 
+    /**
+     * Set visibility of bubble in the expanded state.
+     *
+     * @param visibility {@code true} if the expanded bubble should be visible on the screen.
+     *
+     * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
+     * and setting {@code false} actually means rendering the expanded view in transparent.
+     */
+    void setContentVisibility(boolean visibility) {
+        if (expandedView != null) {
+            expandedView.setContentVisibility(visibility);
+        }
+    }
+
     void setDismissed() {
         entry.setBubbleDismissed(true);
         // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker
@@ -168,6 +184,13 @@
     }
 
     /**
+     * @return the display id of the virtual display on which bubble contents is drawn.
+     */
+    int getDisplayId() {
+        return expandedView != null ? expandedView.getVirtualDisplayId() : INVALID_DISPLAY;
+    }
+
+    /**
      * Should be invoked whenever a Bubble is accessed (selected while expanded).
      */
     void markAsAccessedAt(long lastAccessedMillis) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index aed117b..551488a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles;
 
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -40,7 +41,10 @@
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -111,7 +115,7 @@
     public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
 
     // Enables some subset of notifs to automatically become bubbles
-    private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
+    public static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
 
     /** Flag to enable or disable the entire feature */
     private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
@@ -381,6 +385,10 @@
      * @param notif the notification associated with this bubble.
      */
     void updateBubble(NotificationEntry notif) {
+        // If this is an interruptive notif, mark that it's interrupted
+        if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) {
+            notif.setInterruption();
+        }
         mBubbleData.notificationEntryUpdated(notif);
     }
 
@@ -445,7 +453,8 @@
             if (!areBubblesEnabled(mContext)) {
                 return;
             }
-            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
+            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+                    && canLaunchInActivityView(mContext, entry)) {
                 updateShowInShadeForSuppressNotification(entry);
             }
         }
@@ -455,7 +464,8 @@
             if (!areBubblesEnabled(mContext)) {
                 return;
             }
-            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
+            if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+                    && canLaunchInActivityView(mContext, entry)) {
                 updateBubble(entry);
             }
         }
@@ -465,7 +475,8 @@
             if (!areBubblesEnabled(mContext)) {
                 return;
             }
-            boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry);
+            boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+                    && canLaunchInActivityView(mContext, entry);
             if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
                 // It was previously a bubble but no longer a bubble -- lets remove it
                 removeBubble(entry.key, DISMISS_NO_LONGER_BUBBLE);
@@ -598,17 +609,23 @@
      * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
      */
     public int getExpandedDisplayId(Context context) {
+        final Bubble bubble = getExpandedBubble(context);
+        return bubble != null ? bubble.getDisplayId() : INVALID_DISPLAY;
+    }
+
+    @Nullable
+    private Bubble getExpandedBubble(Context context) {
         if (mStackView == null) {
-            return INVALID_DISPLAY;
+            return null;
         }
-        boolean defaultDisplay = context.getDisplay() != null
+        final boolean defaultDisplay = context.getDisplay() != null
                 && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
-        Bubble b = mStackView.getExpandedBubble();
-        if (defaultDisplay && b != null && isStackExpanded()
+        final Bubble expandedBubble = mStackView.getExpandedBubble();
+        if (defaultDisplay && expandedBubble != null && isStackExpanded()
                 && !mStatusBarWindowController.getPanelExpanded()) {
-            return b.expandedView.getVirtualDisplayId();
+            return expandedBubble;
         }
-        return INVALID_DISPLAY;
+        return null;
     }
 
     @VisibleForTesting
@@ -652,12 +669,6 @@
                 || autoBubbleAll;
     }
 
-    private boolean shouldAutoExpand(NotificationEntry entry) {
-        Notification.BubbleMetadata metadata = entry.getBubbleMetadata();
-        return metadata != null && metadata.getAutoExpandBubble()
-                && isForegroundApp(mContext, entry.notification.getPackageName());
-    }
-
     private void updateShowInShadeForSuppressNotification(NotificationEntry entry) {
         boolean suppressNotification = entry.getBubbleMetadata() != null
                 && entry.getBubbleMetadata().isNotificationSuppressed()
@@ -725,6 +736,14 @@
                 mBubbleData.setExpanded(false);
             }
         }
+
+        @Override
+        public void onSingleTaskDisplayDrawn(int displayId) {
+            final Bubble expandedBubble = getExpandedBubble(mContext);
+            if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
+                expandedBubble.setContentVisibility(true);
+            }
+        }
     }
 
     private static boolean shouldAutoBubbleMessages(Context context) {
@@ -766,6 +785,48 @@
                 (int) (defaultBounciness * 100)) / 100f;
     }
 
+    /**
+     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+     *
+     * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically
+     * that should filter out any invalid bubbles, but should protect SysUI side just in case.
+     *
+     * @param context the context to use.
+     * @param entry the entry to bubble.
+     */
+    static boolean canLaunchInActivityView(Context context, NotificationEntry entry) {
+        PendingIntent intent = entry.getBubbleMetadata() != null
+                ? entry.getBubbleMetadata().getIntent()
+                : null;
+        if (intent == null) {
+            Log.w(TAG, "Unable to create bubble -- no intent");
+            return false;
+        }
+        ActivityInfo info =
+                intent.getIntent().resolveActivityInfo(context.getPackageManager(), 0);
+        if (info == null) {
+            Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+                    + intent);
+            return false;
+        }
+        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+            Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+                    + intent);
+            return false;
+        }
+        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+            Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
+                    + "for intent: " + intent);
+            return false;
+        }
+        if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+            Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
+                    + intent);
+            return false;
+        }
+        return true;
+    }
+
     /** PinnedStackListener that dispatches IME visibility updates to the stack. */
     private class BubblesImeListener extends IPinnedStackListener.Stub {
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index fa137a1..70738c0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,12 +16,10 @@
 
 package com.android.systemui.bubbles;
 
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.systemui.bubbles.BubbleController.DEBUG_ENABLE_AUTO_BUBBLE;
+
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.ActivityView;
@@ -30,7 +28,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -52,6 +49,7 @@
 import android.view.WindowInsets;
 import android.widget.LinearLayout;
 
+import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
@@ -87,6 +85,7 @@
     private int mBubbleHeight;
     private int mPointerWidth;
     private int mPointerHeight;
+    private ShapeDrawable mPointerDrawable;
 
     private NotificationEntry mEntry;
     private PackageManager mPm;
@@ -170,16 +169,11 @@
         mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
 
-        TypedArray ta = getContext().obtainStyledAttributes(
-                new int[] {android.R.attr.colorBackgroundFloating});
-        int bgColor = ta.getColor(0, Color.WHITE);
-        ta.recycle();
 
-        ShapeDrawable triangleDrawable = new ShapeDrawable(TriangleShape.create(
-                mPointerWidth, mPointerHeight, false /* pointUp */));
-
-        triangleDrawable.setTint(bgColor);
-        mPointerView.setBackground(triangleDrawable);
+        mPointerDrawable = new ShapeDrawable(TriangleShape.create(
+                mPointerWidth, mPointerHeight, true /* pointUp */));
+        mPointerView.setBackground(mPointerDrawable);
+        mPointerView.setVisibility(GONE);
 
         mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_expanded_header_height);
@@ -188,10 +182,20 @@
 
         mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
                 true /* singleTaskInstance */);
+
+        setContentVisibility(false);
         addView(mActivityView);
 
-        // Make sure pointer is below activity view
-        bringChildToFront(mPointerView);
+        // Expanded stack layout, top to bottom:
+        // Expanded view container
+        // ==> bubble row
+        // ==> expanded view
+        //   ==> activity view
+        //   ==> manage button
+        bringChildToFront(mActivityView);
+        bringChildToFront(mSettingsIcon);
+
+        applyThemeAttrs();
 
         setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
             // Keep track of IME displaying because we should not make any adjustments that might
@@ -206,6 +210,23 @@
         });
     }
 
+    void applyThemeAttrs() {
+        TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
+        int bgColor = ta.getColor(
+                R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
+        float cornerRadius = ta.getDimension(
+                R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
+        ta.recycle();
+
+        // Update triangle color.
+        mPointerDrawable.setTint(bgColor);
+
+        // Update ActivityView cornerRadius
+        if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
+            mActivityView.setCornerRadius(cornerRadius);
+        }
+    }
+
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
@@ -217,6 +238,22 @@
     }
 
     /**
+     * Set visibility of contents in the expanded state.
+     *
+     * @param visibility {@code true} if the contents should be visible on the screen.
+     *
+     * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
+     * and setting {@code false} actually means rendering the contents in transparent.
+     */
+    void setContentVisibility(boolean visibility) {
+        final float alpha = visibility ? 1f : 0f;
+        mPointerView.setAlpha(alpha);
+        if (mActivityView != null) {
+            mActivityView.setAlpha(alpha);
+        }
+    }
+
+    /**
      * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
      * This should be done post-move and post-animation.
      */
@@ -266,7 +303,7 @@
         if (mAppIcon == null) {
             mAppIcon = mPm.getDefaultActivityIcon();
         }
-        updateTheme();
+        applyThemeAttrs();
         showSettingsIcon();
         updateExpandedView();
     }
@@ -288,6 +325,7 @@
                 parent.removeView(mNotifRow);
             }
             addView(mNotifRow, 1 /* index */);
+            mPointerView.setAlpha(1f);
         }
     }
 
@@ -306,21 +344,6 @@
         }
     }
 
-    void updateTheme() {
-        // Get new colors.
-        TypedArray ta = mContext.obtainStyledAttributes(
-                new int[]{android.R.attr.colorBackgroundFloating, android.R.attr.colorForeground});
-        int backgroundColor = ta.getColor(0, Color.WHITE /* default */);
-        int foregroundColor = ta.getColor(1, Color.BLACK /* default */);
-        ta.recycle();
-
-        // Update triangle color.
-        ShapeDrawable triangleDrawable = new ShapeDrawable(
-                TriangleShape.create(mPointerWidth, mPointerHeight, false /* pointUp */));
-        triangleDrawable.setTint(backgroundColor);
-        mPointerView.setBackground(triangleDrawable);
-    }
-
     private void updateExpandedView() {
         mBubbleIntent = getBubbleIntent(mEntry);
         if (mBubbleIntent != null) {
@@ -329,12 +352,12 @@
                 removeView(mNotifRow);
                 mNotifRow = null;
             }
+            setContentVisibility(false);
             mActivityView.setVisibility(VISIBLE);
-        } else {
+        } else if (DEBUG_ENABLE_AUTO_BUBBLE) {
             // Hide activity view if we had it previously
             mActivityView.setVisibility(GONE);
             mNotifRow = mEntry.getRow();
-
         }
         updateView();
     }
@@ -406,7 +429,7 @@
             Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
                     mEntry.notification.getUid());
             mStackView.collapseStack(() -> {
-                mContext.startActivity(intent);
+                mContext.startActivityAsUser(intent, mEntry.notification.getUser());
                 logBubbleClickEvent(mEntry,
                         StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
             });
@@ -433,6 +456,7 @@
             mActivityView.onLocationChanged();
         } else if (mNotifRow != null) {
             applyRowState(mNotifRow);
+            mPointerView.setAlpha(1f);
         }
         updateHeight();
     }
@@ -444,6 +468,7 @@
         // Adjust for the pointer size
         x -= (mPointerView.getWidth() / 2f);
         mPointerView.setTranslationX(x);
+        mPointerView.setVisibility(VISIBLE);
     }
 
     /**
@@ -524,59 +549,14 @@
     @Nullable
     private PendingIntent getBubbleIntent(NotificationEntry entry) {
         Notification notif = entry.notification.getNotification();
-        String packageName = entry.notification.getPackageName();
         Notification.BubbleMetadata data = notif.getBubbleMetadata();
-        if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */,
-                packageName)) {
+        if (BubbleController.canLaunchInActivityView(mContext, entry) && data != null) {
             return data.getIntent();
-        } else if (BubbleController.shouldUseContentIntent(mContext)
-                && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */,
-                packageName)) {
-            return notif.contentIntent;
         }
         return null;
     }
 
     /**
-     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
-     *
-     * @param intent the pending intent of the bubble.
-     * @param enableLogging whether bubble developer error should be logged.
-     * @param packageName the notification package name for this bubble.
-     * @return
-     */
-    private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
-            String packageName) {
-        if (intent == null) {
-            return false;
-        }
-        ActivityInfo info =
-                intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
-        if (info == null) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
-            }
-            return false;
-        }
-        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
-            }
-            return false;
-        }
-        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
-            }
-            return false;
-        }
-        return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
-    }
-
-    /**
      * Listener that is notified when a bubble is blocked.
      */
     public interface OnBubbleBlockedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 4ad3a33..14d8f37 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -173,8 +173,10 @@
 
     private int mBubbleSize;
     private int mBubblePadding;
+    private int mExpandedViewPadding;
     private int mExpandedAnimateXDistance;
     private int mExpandedAnimateYDistance;
+    private int mPointerHeight;
     private int mStatusBarHeight;
     private int mPipDismissHeight;
     private int mImeOffset;
@@ -302,6 +304,8 @@
                 res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
         mExpandedAnimateYDistance =
                 res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
+        mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
+
         mStatusBarHeight =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         mPipDismissHeight = mContext.getResources().getDimensionPixelSize(
@@ -314,11 +318,12 @@
 
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
 
-        int padding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
+        mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
         int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
 
         mStackAnimationController = new StackAnimationController();
-        mExpandedAnimationController = new ExpandedAnimationController(mDisplaySize);
+        mExpandedAnimationController = new ExpandedAnimationController(
+                mDisplaySize, mExpandedViewPadding);
         mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
 
         mBubbleContainer = new PhysicsAnimationLayout(context);
@@ -331,7 +336,8 @@
 
         mExpandedViewContainer = new FrameLayout(context);
         mExpandedViewContainer.setElevation(elevation);
-        mExpandedViewContainer.setPadding(padding, padding, padding, padding);
+        mExpandedViewContainer.setPadding(mExpandedViewPadding, mExpandedViewPadding,
+                mExpandedViewPadding, mExpandedViewPadding);
         mExpandedViewContainer.setClipChildren(false);
         addView(mExpandedViewContainer);
 
@@ -448,7 +454,7 @@
     public void onThemeChanged() {
         for (Bubble b: mBubbleData.getBubbles()) {
             b.iconView.updateViews();
-            b.expandedView.updateTheme();
+            b.expandedView.applyThemeAttrs();
         }
     }
 
@@ -734,12 +740,16 @@
         }
         final Bubble previouslySelected = mExpandedBubble;
         mExpandedBubble = bubbleToSelect;
+
         if (mIsExpanded) {
             // Make the container of the expanded view transparent before removing the expanded view
             // from it. Otherwise a punch hole created by {@link android.view.SurfaceView} in the
             // expanded view becomes visible on the screen. See b/126856255
             mExpandedViewContainer.setAlpha(0.0f);
             mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+                if (previouslySelected != null) {
+                    previouslySelected.setContentVisibility(false);
+                }
                 updateExpandedBubble();
                 updatePointerPosition();
                 requestUpdate();
@@ -768,6 +778,14 @@
         }
         if (wasExpanded) {
             // Collapse the stack
+            mExpandedViewContainer.setAlpha(0.0f);
+            // TODO: In order to prevent flicker, code below should be executed after the alpha
+            // value set on the mExpandedViewContainer is reflected on the screen. However, we
+            // cannot just postpone the execution like #setSelectedBubble(), since some of member
+            // variables referred by the code are overridden before the execution.
+            if (mExpandedBubble != null) {
+                mExpandedBubble.setContentVisibility(false);
+            }
             animateExpansion(false /* expand */);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         } else {
@@ -925,14 +943,10 @@
             if (shouldExpand) {
                 mExpandedViewContainer.setTranslationX(xStart);
                 mExpandedViewContainer.setTranslationY(yStart);
-                mExpandedViewContainer.setAlpha(0f);
             }
 
             mExpandedViewXAnim.animateToFinalPosition(shouldExpand ? 0f : xStart);
             mExpandedViewYAnim.animateToFinalPosition(shouldExpand ? yDest : yStart);
-            mExpandedViewContainer.animate()
-                    .setDuration(100)
-                    .alpha(shouldExpand ? 1f : 0f);
         }
     }
 
@@ -1279,15 +1293,16 @@
      */
     int getMaxExpandedHeight() {
         int expandedY = (int) mExpandedAnimationController.getExpandedY();
-        return expandedY - getStatusBarHeight();
+        // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset
+        int pipDismissHeight = mPipDismissHeight - getBottomInset();
+        return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight;
     }
 
     /**
      * Calculates the y position of the expanded view when it is expanded.
      */
     float getYPositionForExpandedView() {
-        return mExpandedAnimationController.getExpandedY()
-                - mExpandedBubble.expandedView.getExpandedSize() - mBubblePadding;
+        return getStatusBarHeight() + mBubbleSize + mBubblePadding + mPointerHeight;
     }
 
     /**
@@ -1466,8 +1481,14 @@
         Bubble expandedBubble = getExpandedBubble();
         if (expandedBubble != null) {
             BubbleView iconView = expandedBubble.iconView;
-            float pointerPosition = iconView.getTranslationX() + (iconView.getWidth() / 2f);
-            expandedBubble.expandedView.setPointerPosition((int) pointerPosition);
+            float bubbleLeft = iconView.getTranslationX();
+            float halfBubbleWidth = (iconView.getWidth() / 2f);
+
+            // Bubbles live in expanded view container (x includes expanded view padding).
+            // Pointer lives in expanded view, which has padding (x does not include padding).
+            // Remove padding when deriving pointer location from bubbles.
+            float pointerX = bubbleLeft - mExpandedViewPadding + halfBubbleWidth;
+            expandedBubble.expandedView.setPointerPosition((int) pointerX);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index a9ad464..ae8043f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -78,8 +78,11 @@
      */
     private boolean mSpringingBubbleToTouch = false;
 
-    public ExpandedAnimationController(Point displaySize) {
+    private int mExpandedViewPadding;
+
+    public ExpandedAnimationController(Point displaySize, int expandedViewPadding) {
         mDisplaySize = displaySize;
+        mExpandedViewPadding = expandedViewPadding;
     }
 
     /**
@@ -122,7 +125,7 @@
                 new ChildAnimationConfigurator() {
                     // How much to translate the next bubble, so that it is not overlapping the
                     // previous one.
-                    float mTranslateNextBubbleXBy = mBubblePaddingPx;
+                    float mTranslateNextBubbleXBy = mExpandedViewPadding + mBubblePaddingPx;
 
                     @Override
                     public void configureAnimationForChildAtIndex(
@@ -298,13 +301,11 @@
             return 0;
         }
         final WindowInsets insets = mLayout.getRootWindowInsets();
-        int keyboardHeight = insets.getSystemWindowInsetBottom()
-                - insets.getStableInsetBottom();
-        float bottomInset = keyboardHeight > 0
-                ? keyboardHeight
-                : (mPipDismissHeight - insets.getStableInsetBottom());
-        // Stable insets are excluded from display size, so we must subtract it
-        return mDisplaySize.y - mBubbleSizePx - mBubblePaddingPx - bottomInset;
+        return mBubblePaddingPx + Math.max(
+            mStatusBarHeight,
+            insets.getDisplayCutout() != null
+                ? insets.getDisplayCutout().getSafeInsetTop()
+                : 0);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 8529ed4..b9cdc844 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -86,7 +86,10 @@
      * we need to keep track of it separately from the first bubble's translation in case there are
      * no bubbles, or the first bubble was just added and being animated to its new position.
      */
-    private PointF mStackPosition = new PointF();
+    private PointF mStackPosition = new PointF(-1, -1);
+
+    /** Whether or not the stack's start position has been set. */
+    private boolean mStackMovedToStartPosition = false;
 
     /** The most recent position in which the stack was resting on the edge of the screen. */
     private PointF mRestingStackPosition;
@@ -193,9 +196,10 @@
 
     /** Whether the stack is on the left side of the screen. */
     public boolean isStackOnLeftSide() {
-        if (mLayout == null) {
+        if (mLayout == null || !isStackPositionSet()) {
             return false;
         }
+
         float stackCenter = mStackPosition.x + mIndividualBubbleSize / 2;
         float screenCenter = mLayout.getWidth() / 2;
         return stackCenter < screenCenter;
@@ -630,10 +634,9 @@
     @Override
     void onChildAdded(View child, int index) {
         if (mLayout.getChildCount() == 1) {
-            // If this is the first child added, position the stack in its starting position before
-            // animating in.
-            moveStackToStartPosition(() -> animateInBubble(child));
-        } else if (mLayout.indexOfChild(child) == 0) {
+            // If this is the first child added, position the stack in its starting position.
+            moveStackToStartPosition();
+        } else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) {
             // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
             // to the back of the stack, it'll be largely invisible so don't bother animating it in.
             animateInBubble(child);
@@ -657,16 +660,21 @@
     }
 
     /** Moves the stack, without any animation, to the starting position. */
-    private void moveStackToStartPosition(Runnable after) {
+    private void moveStackToStartPosition() {
         // Post to ensure that the layout's width and height have been calculated.
         mLayout.setVisibility(View.INVISIBLE);
         mLayout.post(() -> {
+            mStackMovedToStartPosition = true;
             setStackPosition(
                     mRestingStackPosition == null
                             ? getDefaultStartPosition()
                             : mRestingStackPosition);
             mLayout.setVisibility(View.VISIBLE);
-            after.run();
+
+            // Animate in the top bubble now that we're visible.
+            if (mLayout.getChildCount() > 0) {
+                animateInBubble(mLayout.getChildAt(0));
+            }
         });
     }
 
@@ -718,6 +726,10 @@
                 getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
     }
 
+    private boolean isStackPositionSet() {
+        return mStackMovedToStartPosition;
+    }
+
     /** Animates in the given bubble. */
     private void animateInBubble(View child) {
         child.setTranslationY(mStackPosition.y);
diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
index a7df6f1..6c1da47 100644
--- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.StrictMode;
 
 /**
  * When a target is chosen from the SystemUI Chooser activity, unpack its arguments and
@@ -40,7 +41,15 @@
         final boolean ignoreTargetSecurity =
                 thisIntent.getBooleanExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY, false);
         final int userId = thisIntent.getIntExtra(Intent.EXTRA_USER_ID, -1);
-        activity.startActivityAsCaller(
-                chosenIntent, options, permissionToken, ignoreTargetSecurity, userId);
+
+        // We're dispatching intents that might be coming from legacy apps, so
+        // (as in com.android.internal.app.ResolverActivity) exempt ourselves from death.
+        StrictMode.disableDeathOnFileUriExposure();
+        try {
+            activity.startActivityAsCaller(
+                    chosenIntent, options, permissionToken, ignoreTargetSecurity, userId);
+        } finally {
+            StrictMode.enableDeathOnFileUriExposure();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 30f6e1a..87d90ad 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -156,10 +156,7 @@
      */
     protected void onConfigurationChanged(Configuration newConfig) {
         if (mConfigChanges.applyNewConfig(mContext.getResources())) {
-            // Save the old state.
-            Parcelable p = destroyFragmentHost();
-            // Generate a new fragment host and restore its state.
-            createFragmentHost(p);
+            reloadFragments();
         } else {
             mFragments.dispatchConfigurationChanged(newConfig);
         }
@@ -217,6 +214,13 @@
         Dependency.get(FragmentService.class).removeAndDestroy(view);
     }
 
+    public void reloadFragments() {
+        // Save the old state.
+        Parcelable p = destroyFragmentHost();
+        // Generate a new fragment host and restore its state.
+        createFragmentHost(p);
+    }
+
     class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
         public HostCallbacks() {
             super(mContext, FragmentHostManager.this.mHandler, 0);
@@ -293,13 +297,6 @@
             reloadFragments();
         }
 
-        private void reloadFragments() {
-            // Save the old state.
-            Parcelable p = destroyFragmentHost();
-            // Generate a new fragment host and restore its state.
-            createFragmentHost(p);
-        }
-
         Fragment instantiate(Context context, String className, Bundle arguments) {
             Context extensionContext = mExtensionLookup.get(className);
             if (extensionContext != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index dcabb78..7b8a424 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1564,8 +1564,11 @@
                     }
 
                     // Unlock rotation, so user can choose to rotate to portrait to see the panel.
-                    RotationPolicy.setRotationLockAtAngle(
-                            mContext, false, RotationUtils.ROTATION_NONE);
+                    // This call is posted so that the rotation does not change until post-layout,
+                    // otherwise onConfigurationChanged() may not get invoked.
+                    mGlobalActionsLayout.post(() ->
+                            RotationPolicy.setRotationLockAtAngle(
+                                    mContext, false, RotationUtils.ROTATION_NONE));
                 }
             } else {
                 if (!rotationLocked) {
@@ -1575,8 +1578,11 @@
                     }
 
                     // Lock to portrait, so the user doesn't accidentally hide the panel.
-                    RotationPolicy.setRotationLockAtAngle(
-                            mContext, true, RotationUtils.ROTATION_NONE);
+                    // This call is posted so that the rotation does not change until post-layout,
+                    // otherwise onConfigurationChanged() may not get invoked.
+                    mGlobalActionsLayout.post(() ->
+                            RotationPolicy.setRotationLockAtAngle(
+                                    mContext, true, RotationUtils.ROTATION_NONE));
                 }
 
                 // Disable rotation suggestions, if enabled
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
new file mode 100644
index 0000000..b66cc4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -0,0 +1,230 @@
+/*
+ * 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.glwallpaper;
+
+import static android.opengl.EGL14.EGL_ALPHA_SIZE;
+import static android.opengl.EGL14.EGL_BLUE_SIZE;
+import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
+import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
+import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
+import static android.opengl.EGL14.EGL_DEPTH_SIZE;
+import static android.opengl.EGL14.EGL_GREEN_SIZE;
+import static android.opengl.EGL14.EGL_NONE;
+import static android.opengl.EGL14.EGL_NO_CONTEXT;
+import static android.opengl.EGL14.EGL_NO_DISPLAY;
+import static android.opengl.EGL14.EGL_NO_SURFACE;
+import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
+import static android.opengl.EGL14.EGL_RED_SIZE;
+import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
+import static android.opengl.EGL14.EGL_STENCIL_SIZE;
+import static android.opengl.EGL14.EGL_SUCCESS;
+import static android.opengl.EGL14.eglChooseConfig;
+import static android.opengl.EGL14.eglCreateContext;
+import static android.opengl.EGL14.eglCreateWindowSurface;
+import static android.opengl.EGL14.eglDestroyContext;
+import static android.opengl.EGL14.eglDestroySurface;
+import static android.opengl.EGL14.eglGetDisplay;
+import static android.opengl.EGL14.eglGetError;
+import static android.opengl.EGL14.eglInitialize;
+import static android.opengl.EGL14.eglMakeCurrent;
+import static android.opengl.EGL14.eglSwapBuffers;
+import static android.opengl.EGL14.eglTerminate;
+
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLSurface;
+import android.opengl.GLUtils;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * A helper class to handle EGL management.
+ */
+public class EglHelper {
+    private static final String TAG = EglHelper.class.getSimpleName();
+
+    private EGLDisplay mEglDisplay;
+    private EGLConfig mEglConfig;
+    private EGLContext mEglContext;
+    private EGLSurface mEglSurface;
+
+    /**
+     * Initialize EGL and prepare EglSurface.
+     * @param surfaceHolder surface holder.
+     * @return true if EglSurface is ready.
+     */
+    public boolean init(SurfaceHolder surfaceHolder) {
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (mEglDisplay == EGL_NO_DISPLAY) {
+            Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return false;
+        }
+
+        if (!eglInitialize(mEglDisplay, null, 0, null, 0)) {
+            Log.w(TAG, "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return false;
+        }
+
+        mEglConfig = chooseEglConfig();
+        if (mEglConfig == null) {
+            Log.w(TAG, "eglConfig not initialized!");
+            return false;
+        }
+
+        if (!createEglContext()) {
+            Log.w(TAG, "Can't create EGLContext!");
+            return false;
+        }
+
+        if (!createEglSurface(surfaceHolder)) {
+            Log.w(TAG, "Can't create EGLSurface!");
+            return false;
+        }
+
+        return true;
+    }
+
+    private EGLConfig chooseEglConfig() {
+        int[] configsCount = new int[1];
+        EGLConfig[] configs = new EGLConfig[1];
+        int[] configSpec = getConfig();
+        if (!eglChooseConfig(mEglDisplay, configSpec, 0, configs, 0, 1, configsCount, 0)) {
+            Log.w(TAG, "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return null;
+        } else {
+            if (configsCount[0] <= 0) {
+                Log.w(TAG, "eglChooseConfig failed, invalid configs count: " + configsCount[0]);
+                return null;
+            } else {
+                return configs[0];
+            }
+        }
+    }
+
+    private int[] getConfig() {
+        return new int[] {
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_DEPTH_SIZE, 0,
+            EGL_STENCIL_SIZE, 0,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_CONFIG_CAVEAT, EGL_NONE,
+            EGL_NONE
+        };
+    }
+
+    /**
+     * Prepare an EglSurface.
+     * @param surfaceHolder surface holder.
+     * @return true if EglSurface is ready.
+     */
+    public boolean createEglSurface(SurfaceHolder surfaceHolder) {
+        mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+        if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
+            Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return false;
+        }
+
+        if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            Log.w(TAG, "eglMakeCurrent failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Destroy EglSurface.
+     */
+    public void destroyEglSurface() {
+        if (hasEglSurface()) {
+            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+            eglDestroySurface(mEglDisplay, mEglSurface);
+            mEglSurface = null;
+        }
+    }
+
+    /**
+     * Check if we have a valid EglSurface.
+     * @return true if EglSurface is ready.
+     */
+    public boolean hasEglSurface() {
+        return mEglSurface != null && mEglSurface != EGL_NO_SURFACE;
+    }
+
+    /**
+     * Prepare EglContext.
+     * @return true if EglContext is ready.
+     */
+    public boolean createEglContext() {
+        int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+        mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+        if (mEglContext == EGL_NO_CONTEXT) {
+            Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Destroy EglContext.
+     */
+    public void destroyEglContext() {
+        if (hasEglContext()) {
+            eglDestroyContext(mEglDisplay, mEglContext);
+            mEglContext = null;
+        }
+    }
+
+    /**
+     * Check if we have EglContext.
+     * @return true if EglContext is ready.
+     */
+    public boolean hasEglContext() {
+        return mEglContext != null;
+    }
+
+    /**
+     * Swap buffer to display.
+     * @return true if swap successfully.
+     */
+    public boolean swapBuffer() {
+        boolean status = eglSwapBuffers(mEglDisplay, mEglSurface);
+        int error = eglGetError();
+        if (error != EGL_SUCCESS) {
+            Log.w(TAG, "eglSwapBuffers failed: " + GLUtils.getEGLErrorString(error));
+        }
+        return status;
+    }
+
+    /**
+     * Destroy EglSurface and EglContext, then terminate EGL.
+     */
+    public void finish() {
+        if (hasEglSurface()) {
+            destroyEglSurface();
+        }
+        if (hasEglContext()) {
+            destroyEglContext();
+        }
+        eglTerminate(mEglDisplay);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
new file mode 100644
index 0000000..8cc17b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
@@ -0,0 +1,87 @@
+/*
+ * 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.glwallpaper;
+
+import android.util.Size;
+import android.view.SurfaceHolder;
+
+/**
+ * A renderer which is responsible for making OpenGL calls to render a frame.
+ */
+public interface GLWallpaperRenderer {
+
+    /**
+     * Called when the surface is created or recreated.
+     */
+    void onSurfaceCreated();
+
+    /**
+     * Called when the surface changed size.
+     * @param width surface width.
+     * @param height surface height.
+     */
+    void onSurfaceChanged(int width, int height);
+
+    /**
+     * Called to draw the current frame.
+     */
+    void onDrawFrame();
+
+    /**
+     * Notify ambient mode is changed.
+     * @param inAmbientMode true if in ambient mode.
+     * @param duration duration of transition.
+     */
+    void updateAmbientMode(boolean inAmbientMode, long duration);
+
+    /**
+     * Ask renderer to report the surface size it needs.
+     */
+    Size reportSurfaceSize();
+
+    /**
+     * Called when no need to render any more.
+     */
+    void finish();
+
+    /**
+     * A proxy which owns surface holder.
+     */
+    interface SurfaceProxy {
+
+        /**
+         * Get surface holder.
+         * @return surface holder.
+         */
+        SurfaceHolder getHolder();
+
+        /**
+         * Ask proxy to start rendering frame to surface.
+         */
+        void requestRender();
+
+        /**
+         * Ask proxy to prepare render context.
+         */
+        void preRender();
+
+        /**
+         * Ask proxy to destroy render context.
+         */
+        void postRender();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
index d935466..4be7623 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -195,76 +195,4 @@
         glUniform1i(mUniTexture, 0);
     }
 
-    /**
-     * This method adjust s(x-axis), t(y-axis) texture coordinates
-     * to prevent the wallpaper from being stretched.
-     * The adjustment happens if either the width or height of the bitmap is larger than
-     * corresponding size of the surface.
-     * If both width and height are larger than corresponding size of the surface,
-     * the adjustment will happen at both s, t side.
-     *
-     * @param bitmapWidth The width of the bitmap.
-     * @param bitmapHeight The height of the bitmap.
-     * @param surfaceWidth The width of the surface.
-     * @param surfaceHeight The height of the surface.
-     * @param xOffset The offset amount along s axis.
-     * @param yOffset The offset amount along t axis.
-     */
-    void adjustTextureCoordinates(int bitmapWidth, int bitmapHeight,
-            int surfaceWidth, int surfaceHeight, float xOffset, float yOffset) {
-        float[] coordinates = TEXTURES.clone();
-
-        if (bitmapWidth > surfaceWidth) {
-            // Calculate the new s pos in pixels.
-            float pixelS = (float) Math.round((bitmapWidth - surfaceWidth) * xOffset);
-            // Calculate the s pos in texture coordinate.
-            float coordinateS = pixelS / bitmapWidth;
-            // Calculate the percentage occupied by the surface width in bitmap width.
-            float surfacePercentageW = (float) surfaceWidth / bitmapWidth;
-            // Need also consider the case if bitmap height is smaller than surface height.
-            if (bitmapHeight < surfaceHeight) {
-                // We will narrow the surface percentage to keep aspect ratio.
-                surfacePercentageW *= (float) bitmapHeight / surfaceHeight;
-            }
-            // Determine the final s pos, also limit the legal s pos to prevent from out of range.
-            float s = coordinateS + surfacePercentageW > 1f ? 1f - surfacePercentageW : coordinateS;
-            // Traverse the s pos in texture coordinates array and adjust the s pos accordingly.
-            for (int i = 0; i < coordinates.length; i += 2) {
-                // indices 2, 4 and 6 are the end of s coordinates.
-                if (i == 2 || i == 4 || i == 6) {
-                    coordinates[i] = Math.min(1f, s + surfacePercentageW);
-                } else {
-                    coordinates[i] = s;
-                }
-            }
-        }
-
-        if (bitmapHeight > surfaceHeight) {
-            // Calculate the new t pos in pixels.
-            float pixelT = (float) Math.round((bitmapHeight - surfaceHeight) * yOffset);
-            // Calculate the t pos in texture coordinate.
-            float coordinateT = pixelT / bitmapHeight;
-            // Calculate the percentage occupied by the surface height in bitmap height.
-            float surfacePercentageH = (float) surfaceHeight / bitmapHeight;
-            // Need also consider the case if bitmap width is smaller than surface width.
-            if (bitmapWidth < surfaceWidth) {
-                // We will narrow the surface percentage to keep aspect ratio.
-                surfacePercentageH *= (float) bitmapWidth / surfaceWidth;
-            }
-            // Determine the final t pos, also limit the legal t pos to prevent from out of range.
-            float t = coordinateT + surfacePercentageH > 1f ? 1f - surfacePercentageH : coordinateT;
-            // Traverse the t pos in texture coordinates array and adjust the t pos accordingly.
-            for (int i = 1; i < coordinates.length; i += 2) {
-                // indices 1, 3 and 11 are the end of t coordinates.
-                if (i == 1 || i == 3 || i == 11) {
-                    coordinates[i] = Math.min(1f, t + surfacePercentageH);
-                } else {
-                    coordinates[i] = t;
-                }
-            }
-        }
-
-        mTextureBuffer.put(coordinates);
-        mTextureBuffer.position(0);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index 5914236..6a1f24a 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -56,11 +56,18 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (!mIsCanceled) {
-                    mAwake = !mAwake;
+                if (!mIsCanceled && mRevealListener != null) {
+                    mRevealListener.onRevealEnd();
                 }
                 mIsCanceled = false;
             }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (mRevealListener != null) {
+                    mRevealListener.onRevealStart();
+                }
+            }
         });
     }
 
@@ -74,10 +81,6 @@
         return mReveal;
     }
 
-    public boolean isAwake() {
-        return mAwake;
-    }
-
     void updateAwake(boolean awake, long duration) {
         mAwake = awake;
         mAnimator.setDuration(duration);
@@ -93,5 +96,15 @@
          * Called back while reveal status changes.
          */
         void onRevealStateChanged();
+
+        /**
+         * Called back while reveal starts.
+         */
+        void onRevealStart();
+
+        /**
+         * Called back while reveal ends.
+         */
+        void onRevealEnd();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 5bbfe84..433e460 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -26,23 +26,17 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
-import android.opengl.GLSurfaceView;
-import android.os.Build;
 import android.util.Log;
 import android.util.MathUtils;
+import android.util.Size;
 
-import com.android.systemui.ImageWallpaper;
-import com.android.systemui.ImageWallpaper.ImageGLView;
 import com.android.systemui.R;
 
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
 /**
  * A GL renderer for image wallpaper.
  */
-public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
-        ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener {
+public class ImageWallpaperRenderer implements GLWallpaperRenderer,
+        ImageRevealHelper.RevealStateListener {
     private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
     private static final float SCALE_VIEWPORT_MIN = 0.98f;
     private static final float SCALE_VIEWPORT_MAX = 1f;
@@ -52,62 +46,61 @@
     private final ImageGLWallpaper mWallpaper;
     private final ImageProcessHelper mImageProcessHelper;
     private final ImageRevealHelper mImageRevealHelper;
-    private final ImageGLView mGLView;
-    private float mXOffset = 0f;
-    private float mYOffset = 0f;
-    private int mWidth = 0;
-    private int mHeight = 0;
 
+    private SurfaceProxy mProxy;
+    private Rect mSurfaceSize;
     private Bitmap mBitmap;
-    private int mBitmapWidth = 0;
-    private int mBitmapHeight = 0;
 
-    public ImageWallpaperRenderer(Context context, ImageGLView glView) {
+    public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
         mWallpaperManager = context.getSystemService(WallpaperManager.class);
         if (mWallpaperManager == null) {
             Log.w(TAG, "WallpaperManager not available");
         }
 
+        mProxy = proxy;
         mProgram = new ImageGLProgram(context);
         mWallpaper = new ImageGLWallpaper(mProgram);
         mImageProcessHelper = new ImageProcessHelper();
         mImageRevealHelper = new ImageRevealHelper(this);
-        mGLView = glView;
 
-        if (mWallpaperManager != null) {
-            mBitmap = mWallpaperManager.getBitmap();
-            mBitmapWidth = mBitmap.getWidth();
-            mBitmapHeight = mBitmap.getHeight();
+        if (loadBitmap()) {
             // Compute threshold of the image, this is an async work.
             mImageProcessHelper.start(mBitmap);
-            mWallpaperManager.forgetLoadedWallpaper();
         }
     }
 
     @Override
-    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+    public void onSurfaceCreated() {
         glClearColor(0f, 0f, 0f, 1.0f);
         mProgram.useGLProgram(
                 R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
+
+        if (!loadBitmap()) {
+            Log.w(TAG, "reload bitmap failed!");
+        }
+
         mWallpaper.setup(mBitmap);
         mBitmap = null;
     }
 
-    @Override
-    public void onSurfaceChanged(GL10 gl, int width, int height) {
-        glViewport(0, 0, width, height);
-        if (Build.IS_DEBUGGABLE) {
-            Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height
-                    + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
+    private boolean loadBitmap() {
+        if (mWallpaperManager != null && mBitmap == null) {
+            mBitmap = mWallpaperManager.getBitmap();
+            mWallpaperManager.forgetLoadedWallpaper();
+            if (mBitmap != null) {
+                mSurfaceSize = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+            }
         }
-        mWidth = width;
-        mHeight = height;
-        mWallpaper.adjustTextureCoordinates(
-                mBitmapWidth, mBitmapHeight, width, height, mXOffset, mYOffset);
+        return mBitmap != null;
     }
 
     @Override
-    public void onDrawFrame(GL10 gl) {
+    public void onSurfaceChanged(int width, int height) {
+        glViewport(0, 0, width, height);
+    }
+
+    @Override
+    public void onDrawFrame() {
         float threshold = mImageProcessHelper.getThreshold();
         float reveal = mImageRevealHelper.getReveal();
 
@@ -122,50 +115,45 @@
         mWallpaper.draw();
     }
 
+    @Override
+    public void updateAmbientMode(boolean inAmbientMode, long duration) {
+        mImageRevealHelper.updateAwake(!inAmbientMode, duration);
+    }
+
+    @Override
+    public Size reportSurfaceSize() {
+        return new Size(mSurfaceSize.width(), mSurfaceSize.height());
+    }
+
+    @Override
+    public void finish() {
+        mProxy = null;
+    }
+
     private void scaleViewport(float reveal) {
+        int width = mSurfaceSize.width();
+        int height = mSurfaceSize.height();
         // Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal.
         float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MAX, SCALE_VIEWPORT_MIN, reveal);
         // Calculate the offset amount from the lower left corner.
         float offset = (SCALE_VIEWPORT_MAX - vpScaled) / 2;
         // Change the viewport.
-        glViewport((int) (mWidth * offset), (int) (mHeight * offset),
-                (int) (mWidth * vpScaled), (int) (mHeight * vpScaled));
-    }
-
-    @Override
-    public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
-        mImageRevealHelper.updateAwake(!inAmbientMode, duration);
-        requestRender();
-    }
-
-    @Override
-    public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) {
-        if (frame == null || (xOffset == mXOffset && yOffset == mYOffset)) {
-            return;
-        }
-
-        int width = frame.width();
-        int height = frame.height();
-        mXOffset = xOffset;
-        mYOffset = yOffset;
-
-        if (Build.IS_DEBUGGABLE) {
-            Log.d(TAG, "onOffsetsChanged: width=" + width + ", height=" + height
-                    + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
-        }
-        mWallpaper.adjustTextureCoordinates(
-                mBitmapWidth, mBitmapHeight, width, height, mXOffset, mYOffset);
-        requestRender();
+        glViewport((int) (width * offset), (int) (height * offset),
+                (int) (width * vpScaled), (int) (height * vpScaled));
     }
 
     @Override
     public void onRevealStateChanged() {
-        requestRender();
+        mProxy.requestRender();
     }
 
-    private void requestRender() {
-        if (mGLView != null) {
-            mGLView.render();
-        }
+    @Override
+    public void onRevealStart() {
+        mProxy.preRender();
+    }
+
+    @Override
+    public void onRevealEnd() {
+        mProxy.postRender();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 4590470..35b8d203 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -30,6 +30,7 @@
 import android.icu.text.DateFormat;
 import android.icu.text.DisplayContext;
 import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Trace;
@@ -57,6 +58,7 @@
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
@@ -98,6 +100,7 @@
     private final Date mCurrentTime = new Date();
     private final Handler mHandler;
     private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
+    private final HashSet<Integer> mMediaInvisibleStates;
     private ZenModeController mZenModeController;
     private String mDatePattern;
     private DateFormat mDateFormat;
@@ -113,6 +116,7 @@
     private StatusBarStateController mStatusBarStateController;
     protected MediaMetadata mMediaMetaData;
     protected boolean mDozing;
+    private boolean mMediaIsVisible;
 
     /**
      * Receiver responsible for time ticking and updating the date format.
@@ -169,6 +173,11 @@
         mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
         mDndUri = Uri.parse(KEYGUARD_DND_URI);
         mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
+
+        mMediaInvisibleStates = new HashSet<>();
+        mMediaInvisibleStates.add(PlaybackState.STATE_NONE);
+        mMediaInvisibleStates.add(PlaybackState.STATE_STOPPED);
+        mMediaInvisibleStates.add(PlaybackState.STATE_PAUSED);
     }
 
     /**
@@ -209,31 +218,33 @@
     }
 
     protected boolean needsMediaLocked() {
-        return mMediaMetaData != null && mDozing;
+        return mMediaMetaData != null && mMediaIsVisible && mDozing;
     }
 
     protected void addMediaLocked(ListBuilder listBuilder) {
-        if (mMediaMetaData != null) {
-            CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
-            if (TextUtils.isEmpty(title)) {
-                title = getContext().getResources().getString(R.string.music_controls_no_title);
+        if (mMediaMetaData == null) {
+            return;
+        }
+
+        CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
+        if (TextUtils.isEmpty(title)) {
+            title = getContext().getResources().getString(R.string.music_controls_no_title);
+        }
+        listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title));
+
+        CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
+        if (!TextUtils.isEmpty(album)) {
+            RowBuilder albumBuilder = new RowBuilder(mMediaUri);
+            albumBuilder.setTitle(album);
+
+            Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+            IconCompat mediaIconCompat = mediaIcon == null ? null
+                    : IconCompat.createFromIcon(getContext(), mediaIcon);
+            if (mediaIconCompat != null) {
+                albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE);
             }
-            listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title));
 
-            CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
-            if (!TextUtils.isEmpty(album)) {
-                RowBuilder albumBuilder = new RowBuilder(mMediaUri);
-                albumBuilder.setTitle(album);
-
-                Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
-                IconCompat mediaIconCompat = mediaIcon == null ? null
-                        : IconCompat.createFromIcon(getContext(), mediaIcon);
-                if (mediaIconCompat != null) {
-                    albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE);
-                }
-
-                listBuilder.addRow(albumBuilder);
-            }
+            listBuilder.addRow(albumBuilder);
         }
     }
 
@@ -411,9 +422,14 @@
      * @param metadata New metadata.
      */
     @Override
-    public void onMetadataChanged(MediaMetadata metadata) {
+    public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
         synchronized (this) {
+            boolean nextVisible = !mMediaInvisibleStates.contains(state);
+            if (nextVisible == mMediaIsVisible && metadata == mMediaMetaData) {
+                return;
+            }
             mMediaMetaData = metadata;
+            mMediaIsVisible = nextVisible;
         }
         notifyChange();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 3a0534d2..f784293 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -44,6 +44,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.util.Utils;
 
 public class MediaProjectionPermissionActivity extends Activity
         implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
@@ -96,44 +97,50 @@
         TextPaint paint = new TextPaint();
         paint.setTextSize(42);
 
-        String label = aInfo.loadLabel(packageManager).toString();
+        CharSequence dialogText = null;
+        if (Utils.isHeadlessRemoteDisplayProvider(packageManager, mPackageName)) {
+            dialogText = getString(R.string.media_projection_dialog_service_text);
+        } else {
+            String label = aInfo.loadLabel(packageManager).toString();
 
-        // If the label contains new line characters it may push the security
-        // message below the fold of the dialog. Labels shouldn't have new line
-        // characters anyways, so just truncate the message the first time one
-        // is seen.
-        final int labelLength = label.length();
-        int offset = 0;
-        while (offset < labelLength) {
-            final int codePoint = label.codePointAt(offset);
-            final int type = Character.getType(codePoint);
-            if (type == Character.LINE_SEPARATOR
-                    || type == Character.CONTROL
-                    || type == Character.PARAGRAPH_SEPARATOR) {
-                label = label.substring(0, offset) + ELLIPSIS;
-                break;
+            // If the label contains new line characters it may push the security
+            // message below the fold of the dialog. Labels shouldn't have new line
+            // characters anyways, so just truncate the message the first time one
+            // is seen.
+            final int labelLength = label.length();
+            int offset = 0;
+            while (offset < labelLength) {
+                final int codePoint = label.codePointAt(offset);
+                final int type = Character.getType(codePoint);
+                if (type == Character.LINE_SEPARATOR
+                        || type == Character.CONTROL
+                        || type == Character.PARAGRAPH_SEPARATOR) {
+                    label = label.substring(0, offset) + ELLIPSIS;
+                    break;
+                }
+                offset += Character.charCount(codePoint);
             }
-            offset += Character.charCount(codePoint);
+
+            if (label.isEmpty()) {
+                label = mPackageName;
+            }
+
+            String unsanitizedAppName = TextUtils.ellipsize(label,
+                    paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
+            String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
+
+            String actionText = getString(R.string.media_projection_dialog_text, appName);
+            SpannableString message = new SpannableString(actionText);
+
+            int appNameIndex = actionText.indexOf(appName);
+            if (appNameIndex >= 0) {
+                message.setSpan(new StyleSpan(Typeface.BOLD),
+                        appNameIndex, appNameIndex + appName.length(), 0);
+            }
+            dialogText = message;
         }
 
-        if (label.isEmpty()) {
-            label = mPackageName;
-        }
-
-        String unsanitizedAppName = TextUtils.ellipsize(label,
-                paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
-        String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
-
-        String actionText = getString(R.string.media_projection_dialog_text, appName);
-        SpannableString message = new SpannableString(actionText);
-
-        int appNameIndex = actionText.indexOf(appName);
-        if (appNameIndex >= 0) {
-            message.setSpan(new StyleSpan(Typeface.BOLD),
-                    appNameIndex, appNameIndex + appName.length(), 0);
-        }
-
-        String dialogTitle = getString(R.string.media_projection_dialog_title, appName);
+        String dialogTitle = getString(R.string.media_projection_dialog_title);
 
         View dialogTitleView = View.inflate(this, R.layout.media_projection_dialog_title, null);
         TextView titleText = (TextView) dialogTitleView.findViewById(R.id.dialog_title);
@@ -141,7 +148,7 @@
 
         mDialog = new AlertDialog.Builder(this)
                 .setCustomTitle(dialogTitleView)
-                .setMessage(message)
+                .setMessage(dialogText)
                 .setPositiveButton(R.string.media_projection_action_text, this)
                 .setNegativeButton(android.R.string.cancel, this)
                 .setOnCancelListener(this)
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index f6cd199..4982dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -118,6 +118,8 @@
 
     private static final String SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING =
             "android.settings.BATTERY_SAVER_SETTINGS";
+    public static final String BATTERY_SAVER_SCHEDULE_SCREEN_INTENT_ACTION =
+            "com.android.settings.BATTERY_SAVER_SCHEDULE_SETTINGS";
 
     private static final String BATTERY_SAVER_DESCRIPTION_URL_KEY = "url";
 
@@ -152,16 +154,18 @@
     private SystemUIDialog mThermalShutdownDialog;
     @VisibleForTesting SystemUIDialog mUsbHighTempDialog;
     private BatteryStateSnapshot mCurrentBatterySnapshot;
+    private ActivityStarter mActivityStarter;
 
     /**
      */
     @Inject
-    public PowerNotificationWarnings(Context context) {
+    public PowerNotificationWarnings(Context context, ActivityStarter activityStarter) {
         mContext = context;
         mNoMan = mContext.getSystemService(NotificationManager.class);
         mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mKeyguard = mContext.getSystemService(KeyguardManager.class);
         mReceiver.init();
+        mActivityStarter = activityStarter;
     }
 
     @Override
@@ -172,7 +176,6 @@
         pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
         pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null);
         pw.print("mSaverEnabledConfirmation=");
-        pw.println(mSaverEnabledConfirmation != null ? "not null" : null);
         pw.print("mHighTempWarning="); pw.println(mHighTempWarning);
         pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
         pw.print("mThermalShutdownDialog=");
@@ -305,8 +308,7 @@
                         .setWhen(0)
                         .setShowWhen(false)
                         .setContentTitle(mContext.getString(R.string.auto_saver_title))
-                        .setContentText(mContext.getString(R.string.auto_saver_text,
-                                getLowBatteryAutoTriggerDefaultLevel()));
+                        .setContentText(mContext.getString(R.string.auto_saver_text));
         nb.setContentIntent(pendingBroadcast(ACTION_ENABLE_AUTO_SAVER));
         nb.setDeleteIntent(pendingBroadcast(ACTION_DISMISS_AUTO_SAVER_SUGGESTION));
         nb.addAction(0,
@@ -673,51 +675,14 @@
         return builder;
     }
 
-    private void showAutoSaverEnabledConfirmation() {
-        if (mSaverEnabledConfirmation != null) return;
-
-        // Open the Battery Saver setting page.
-        final Intent actionBatterySaverSetting =
-                new Intent(SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        final SystemUIDialog d = new SystemUIDialog(mContext);
-        d.setTitle(R.string.auto_saver_enabled_title);
-        d.setMessage(mContext.getString(R.string.auto_saver_enabled_text,
-                getLowBatteryAutoTriggerDefaultLevel()));
-
-        // "Got it". Just close the dialog. Automatic battery has been enabled already.
-        d.setPositiveButton(R.string.auto_saver_okay_action,
-                (dialog, which) -> onAutoSaverEnabledConfirmationClosed());
-
-        // "Settings" -> Opens the battery saver settings activity.
-        d.setNeutralButton(R.string.open_saver_setting_action, (dialog, which) -> {
-            mContext.startActivity(actionBatterySaverSetting);
-            onAutoSaverEnabledConfirmationClosed();
-        });
-        d.setShowForAllUsers(true);
-        d.setOnDismissListener((dialog) -> onAutoSaverEnabledConfirmationClosed());
-        d.show();
-        mSaverEnabledConfirmation = d;
-    }
-
-    private void onAutoSaverEnabledConfirmationClosed() {
-        mSaverEnabledConfirmation = null;
-    }
-
     private void setSaverMode(boolean mode, boolean needFirstTimeWarning) {
         BatterySaverUtils.setPowerSaveMode(mContext, mode, needFirstTimeWarning);
     }
 
-    private void scheduleAutoBatterySaver() {
-        int autoTriggerThreshold = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_lowBatteryWarningLevel);
-        if (autoTriggerThreshold == 0) {
-            autoTriggerThreshold = 15;
-        }
-
-        BatterySaverUtils.ensureAutoBatterySaver(mContext, autoTriggerThreshold);
-        showAutoSaverEnabledConfirmation();
+    private void startBatterySaverSchedulePage() {
+        Intent intent = new Intent(BATTERY_SAVER_SCHEDULE_SCREEN_INTENT_ACTION);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mActivityStarter.startActivity(intent, true /* dismissShade */);
     }
 
     private final class Receiver extends BroadcastReceiver {
@@ -771,7 +736,7 @@
                 dismissAutoSaverSuggestion();
             } else if (ACTION_ENABLE_AUTO_SAVER.equals(action)) {
                 dismissAutoSaverSuggestion();
-                scheduleAutoBatterySaver();
+                startBatterySaverSchedulePage();
             } else if (ACTION_AUTO_SAVER_NO_THANKS.equals(action)) {
                 dismissAutoSaverSuggestion();
                 BatterySaverUtils.suppressAutoBatterySaver(context);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 1c0974a..82a2c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -23,9 +23,13 @@
 import android.content.Intent
 import android.content.IntentFilter
 import android.os.Handler
+import android.os.Looper
+import android.os.Message
 import android.os.UserHandle
 import android.os.UserManager
+import android.provider.DeviceConfig
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
 import com.android.systemui.Dependency.BG_HANDLER_NAME
 import com.android.systemui.Dependency.MAIN_HANDLER_NAME
 import com.android.systemui.R
@@ -39,6 +43,9 @@
 import javax.inject.Named
 import javax.inject.Singleton
 
+fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+
 @Singleton
 class PrivacyItemController @Inject constructor(
         val context: Context,
@@ -47,7 +54,8 @@
         @Named(BG_HANDLER_NAME) private val bgHandler: Handler
 ) : Dumpable {
 
-    companion object {
+    @VisibleForTesting
+    internal companion object {
         val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
                 AppOpsManager.OP_RECORD_AUDIO,
                 AppOpsManager.OP_COARSE_LOCATION,
@@ -57,6 +65,9 @@
                 Intent.ACTION_MANAGED_PROFILE_REMOVED)
         const val TAG = "PrivacyItemController"
         const val SYSTEM_UID = 1000
+        const val MSG_ADD_CALLBACK = 0
+        const val MSG_REMOVE_CALLBACK = 1
+        const val MSG_UPDATE_LISTENING_STATE = 2
     }
 
     @VisibleForTesting
@@ -70,6 +81,7 @@
     val systemApp =
             PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
     private val callbacks = mutableListOf<WeakReference<Callback>>()
+    private val messageHandler = H(WeakReference(this), uiHandler.looper)
 
     private val notifyChanges = Runnable {
         val list = privacyList
@@ -81,6 +93,20 @@
         uiHandler.post(notifyChanges)
     }
 
+    private var indicatorsAvailable = isPermissionsHubEnabled()
+    @VisibleForTesting
+    internal val devicePropertyChangedListener =
+            object : DeviceConfig.OnPropertyChangedListener {
+        override fun onPropertyChanged(namespace: String, name: String, value: String?) {
+            if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace) &&
+                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(name)) {
+                indicatorsAvailable = java.lang.Boolean.parseBoolean(value)
+                messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
+                messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
+            }
+        }
+    }
+
     private val cb = object : AppOpsController.Callback {
         override fun onActiveStateChanged(
             code: Int,
@@ -103,6 +129,11 @@
             registerReceiver()
         }
 
+    init {
+        DeviceConfig.addOnPropertyChangedListener(
+                DeviceConfig.NAMESPACE_PRIVACY, context.mainExecutor, devicePropertyChangedListener)
+    }
+
     private fun unregisterReceiver() {
         context.unregisterReceiver(userSwitcherReceiver)
     }
@@ -123,8 +154,14 @@
         bgHandler.post(updateListAndNotifyChanges)
     }
 
-    @VisibleForTesting
-    internal fun setListening(listen: Boolean) {
+    /**
+     * Updates listening status based on whether there are callbacks and the indicators are enabled
+     *
+     * This is only called from private (add/remove)Callback and from the config listener, all in
+     * main thread.
+     */
+    private fun setListeningState() {
+        val listen = !callbacks.isEmpty() and indicatorsAvailable
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -134,32 +171,44 @@
         } else {
             appOpsController.removeCallback(OPS, cb)
             unregisterReceiver()
+            // Make sure that we remove all indicators and notify listeners if we are not
+            // listening anymore due to indicators being disabled
+            update(false)
         }
     }
 
     private fun addCallback(callback: WeakReference<Callback>) {
         callbacks.add(callback)
-        if (callbacks.isNotEmpty() && !listening) setListening(true)
+        if (callbacks.isNotEmpty() && !listening) {
+            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
+            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
+        }
         // Notify this callback if we didn't set to listening
-        else uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
+        else if (listening) uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
     }
 
     private fun removeCallback(callback: WeakReference<Callback>) {
         // Removes also if the callback is null
         callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
-        if (callbacks.isEmpty()) setListening(false)
+        if (callbacks.isEmpty()) {
+            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
+            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
+        }
     }
 
     fun addCallback(callback: Callback) {
-        addCallback(WeakReference(callback))
+        messageHandler.obtainMessage(MSG_ADD_CALLBACK, callback).sendToTarget()
     }
 
     fun removeCallback(callback: Callback) {
-        removeCallback(WeakReference(callback))
+        messageHandler.obtainMessage(MSG_REMOVE_CALLBACK, callback).sendToTarget()
     }
 
     private fun updatePrivacyList() {
-
+        if (!listening) {
+            privacyList = emptyList()
+            return
+        }
         val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
                 .mapNotNull { toPrivacyItem(it) }.distinct()
         privacyList = list
@@ -217,4 +266,29 @@
             }
         }
     }
+
+    private class H(
+            private val outerClass: WeakReference<PrivacyItemController>,
+            looper: Looper
+    ) : Handler(looper) {
+        override fun handleMessage(msg: Message) {
+            super.handleMessage(msg)
+            when (msg.what) {
+                MSG_UPDATE_LISTENING_STATE -> outerClass.get()?.setListeningState()
+
+                MSG_ADD_CALLBACK -> {
+                    if (msg.obj !is PrivacyItemController.Callback) return
+                    outerClass.get()?.addCallback(
+                            WeakReference(msg.obj as PrivacyItemController.Callback))
+                }
+
+                MSG_REMOVE_CALLBACK -> {
+                    if (msg.obj !is PrivacyItemController.Callback) return
+                    outerClass.get()?.removeCallback(
+                            WeakReference(msg.obj as PrivacyItemController.Callback))
+                }
+                else -> {}
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
new file mode 100644
index 0000000..c654621
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.qs
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+/**
+ * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader].
+ *
+ * If both elements are visible, it splits the available space according to the following rules:
+ * * If both views add up to less than the total space, they take all the space they need.
+ * * If both views are larger than half the space, each view takes half the space.
+ * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining
+ * space.
+ */
+class QSHeaderInfoLayout @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyle: Int = 0,
+        defStyleRes: Int = 0
+) : FrameLayout(context, attrs, defStyle, defStyleRes) {
+
+    private lateinit var alarmContainer: View
+    private lateinit var ringerContainer: View
+    private lateinit var statusSeparator: View
+    private val location = Location(0, 0)
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        alarmContainer = findViewById(R.id.alarm_container)
+        ringerContainer = findViewById(R.id.ringer_container)
+        statusSeparator = findViewById(R.id.status_separator)
+    }
+
+    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+        // At most one view is there
+        if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b)
+        else {
+            val layoutRTL = isLayoutRtl
+            val width = r - l
+            val height = b - t
+            var offset = 0
+
+            offset += alarmContainer.layoutView(width, height, offset, layoutRTL)
+            offset += statusSeparator.layoutView(width, height, offset, layoutRTL)
+            ringerContainer.layoutView(width, height, offset, layoutRTL)
+        }
+    }
+
+    private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int {
+        location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL)
+        layout(location.left, 0, location.right, pHeight)
+        return this.measuredWidth
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(
+                MeasureSpec.makeMeasureSpec(
+                        MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST),
+                heightMeasureSpec)
+        val width = MeasureSpec.getSize(widthMeasureSpec)
+        // Once we measure the views, using as much space as they need, we need to remeasure them
+        // assigning them their final width. This is because TextViews decide whether to MARQUEE
+        // after onMeasure.
+        if (statusSeparator.visibility != View.GONE) {
+            val alarmWidth = alarmContainer.measuredWidth
+            val separatorWidth = statusSeparator.measuredWidth
+            val ringerWidth = ringerContainer.measuredWidth
+            val availableSpace = MeasureSpec.getSize(width) - separatorWidth
+            if (alarmWidth < availableSpace / 2) {
+                measureChild(
+                        ringerContainer,
+                        MeasureSpec.makeMeasureSpec(
+                                Math.min(ringerWidth, availableSpace - alarmWidth),
+                                MeasureSpec.AT_MOST),
+                        heightMeasureSpec)
+            } else if (ringerWidth < availableSpace / 2) {
+                measureChild(alarmContainer,
+                        MeasureSpec.makeMeasureSpec(
+                                Math.min(alarmWidth, availableSpace - ringerWidth),
+                                MeasureSpec.AT_MOST),
+                        heightMeasureSpec)
+            } else {
+                measureChild(
+                        alarmContainer,
+                        MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
+                        heightMeasureSpec)
+                measureChild(
+                        ringerContainer,
+                        MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
+                        heightMeasureSpec)
+            }
+        }
+        setMeasuredDimension(width, measuredHeight)
+    }
+
+    private data class Location(var left: Int, var right: Int) {
+        /**
+         * Sets the [left] and [right] with the correct values for laying out the child, respecting
+         * RTL. Only set the variable through here to prevent concurrency issues.
+         * This is done to prevent allocation of [Pair] in [onLayout].
+         */
+        fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) {
+            if (RTL) {
+                left = parentWidth - offset - width
+                right = parentWidth - offset
+            } else {
+                left = offset
+                right = offset + width
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 42c616c..9842748 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -34,6 +34,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.AlarmClock;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateUtils;
@@ -54,6 +55,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DualToneHandler;
@@ -65,6 +67,7 @@
 import com.android.systemui.privacy.PrivacyDialogBuilder;
 import com.android.systemui.privacy.PrivacyItem;
 import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyItemControllerKt;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -141,6 +144,7 @@
     private OngoingPrivacyChip mPrivacyChip;
     private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
+    private boolean mPermissionsHubEnabled;
 
     private PrivacyItemController mPrivacyItemController;
 
@@ -154,6 +158,20 @@
     private boolean mHasTopCutout = false;
     private boolean mPrivacyChipLogged = false;
 
+    private final DeviceConfig.OnPropertyChangedListener mPropertyListener =
+            new DeviceConfig.OnPropertyChangedListener() {
+                @Override
+                public void onPropertyChanged(String namespace, String name, String value) {
+                    if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace)
+                            && SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(
+                            name)) {
+                        mPermissionsHubEnabled = Boolean.valueOf(value);
+                        StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
+                        iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+                    }
+                }
+            };
+
     private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
         @Override
         public void privacyChanged(List<PrivacyItem> privacyItems) {
@@ -236,6 +254,12 @@
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
+
+        mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled();
+        // Change the ignored slots when DeviceConfig flag changes
+        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+                mContext.getMainExecutor(), mPropertyListener);
+
     }
 
     private List<String> getIgnoredIconSlots() {
@@ -244,8 +268,10 @@
                 com.android.internal.R.string.status_bar_camera));
         ignored.add(mContext.getResources().getString(
                 com.android.internal.R.string.status_bar_microphone));
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_location));
+        if (mPermissionsHubEnabled) {
+            ignored.add(mContext.getResources().getString(
+                    com.android.internal.R.string.status_bar_location));
+        }
 
         return ignored;
     }
@@ -262,7 +288,7 @@
     }
 
     private void setChipVisibility(boolean chipVisible) {
-        if (chipVisible) {
+        if (chipVisible && mPermissionsHubEnabled) {
             mPrivacyChip.setVisibility(View.VISIBLE);
             // Makes sure that the chip is logged as viewed at most once each time QS is opened
             // mListening makes sure that the callback didn't return after the user closed QS
@@ -295,6 +321,7 @@
         }
         mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
         mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
+        mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
 
         return isOriginalVisible != ringerVisible ||
                 !Objects.equals(originalRingerText, mRingerModeTextView.getText());
@@ -311,6 +338,7 @@
         }
         mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
         mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
+        mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
 
         return isOriginalVisible != alarmVisible ||
                 !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1f857ff..9045539 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -551,7 +551,7 @@
             mResId = resId;
         }
 
-        public static Icon get(int resId) {
+        public static synchronized Icon get(int resId) {
             Icon icon = ICONS.get(resId);
             if (icon == null) {
                 icon = new ResourceIcon(resId);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 78c7cd4..1848219 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -27,9 +27,6 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
@@ -44,6 +41,7 @@
 import android.hardware.input.InputManager;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -68,6 +66,7 @@
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.phone.NavigationBarFragment;
+import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.CallbackController;
@@ -270,6 +269,19 @@
         }
 
         @Override
+        public void onAssistantGestureCompletion(float velocity) {
+            if (!verifyCaller("onAssistantGestureCompletion")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void startAssistant(Bundle bundle) {
             if (!verifyCaller("startAssistant")) {
                 return;
@@ -450,20 +462,20 @@
         // Assumes device always starts with back button until launcher tells it that it does not
         mBackButtonAlpha = 1.0f;
 
+        // Listen for nav bar mode changes
         mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
 
-        // Listen for the package update changes.
-        if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
-            updateEnabledState();
-            mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
-            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-            filter.addDataScheme("package");
-            filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
-                    PatternMatcher.PATTERN_LITERAL);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            // TODO: Shouldn't this be per-user?
-            mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
-        }
+        // Listen for device provisioned/user setup
+        updateEnabledState();
+        mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
+
+        // Listen for launcher package changes
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addDataScheme("package");
+        filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
+                PatternMatcher.PATTERN_LITERAL);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
     }
 
     public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
@@ -491,6 +503,7 @@
         }
         if (mSysUiStateFlags != newState) {
             mSysUiStateFlags = newState;
+            notifySystemUiStateChanged(mSysUiStateFlags);
             notifySystemUiStateFlags(mSysUiStateFlags);
         }
     }
@@ -502,18 +515,19 @@
     private void updateSystemUiStateFlags() {
         final NavigationBarController navBar = Dependency.get(NavigationBarController.class);
         final NavigationBarFragment navBarFragment = navBar.getDefaultNavigationBarFragment();
+        final NavigationBarView navBarView = navBar.getNavigationBarView(mContext.getDisplayId());
         final StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
-        final boolean panelExpanded = statusBar != null && statusBar.getPanel() != null
-                && statusBar.getPanel().isFullyExpanded();
-        final boolean bouncerShowing = statusBar != null && statusBar.isBouncerShowing();
+
         mSysUiStateFlags = 0;
-        mSysUiStateFlags |= (navBarFragment != null && !navBarFragment.isNavBarWindowVisible())
-                ? SYSUI_STATE_NAV_BAR_HIDDEN : 0;
-        mSysUiStateFlags |= panelExpanded
-                ? SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED : 0;
-        mSysUiStateFlags |= bouncerShowing
-                ? SYSUI_STATE_BOUNCER_SHOWING : 0;
-        mSysUiStateFlags |= navBarFragment != null ? navBarFragment.getA11yButtonState(null) : 0;
+        if (navBarFragment != null) {
+            navBarFragment.updateSystemUiStateFlags(-1);
+        }
+        if (navBarView != null) {
+            navBarView.updateSystemUiStateFlags();
+        }
+        if (statusBar != null) {
+            statusBar.updateSystemUiStateFlags();
+        }
         notifySystemUiStateFlags(mSysUiStateFlags);
     }
 
@@ -619,6 +633,7 @@
         mConnectionCallbacks.add(listener);
         listener.onConnectionChanged(mOverviewProxy != null);
         listener.onBackButtonAlphaChanged(mBackButtonAlpha, false);
+        listener.onSystemUiStateChanged(mSysUiStateFlags);
     }
 
     @Override
@@ -683,6 +698,18 @@
         }
     }
 
+    private void notifyAssistantGestureCompletion(float velocity) {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onAssistantGestureCompletion(velocity);
+        }
+    }
+
+    private void notifySystemUiStateChanged(int sysuiStateFlags) {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onSystemUiStateChanged(sysuiStateFlags);
+        }
+    }
+
     private void notifyStartAssistant(Bundle bundle) {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
             mConnectionCallbacks.get(i).startAssistant(bundle);
@@ -722,6 +749,11 @@
         pw.print("  quickStepIntent="); pw.println(mQuickStepIntent);
         pw.print("  quickStepIntentResolved="); pw.println(isEnabled());
         pw.print("  mSysUiStateFlags="); pw.println(mSysUiStateFlags);
+        pw.println("    " + QuickStepContract.getSystemUiStateString(mSysUiStateFlags));
+        pw.print("    backGestureDisabled=");
+        pw.println(QuickStepContract.isBackGestureDisabled(mSysUiStateFlags));
+        pw.print("    assistantGestureDisabled=");
+        pw.println(QuickStepContract.isAssistantGestureDisabled(mSysUiStateFlags));
     }
 
     public interface OverviewProxyListener {
@@ -730,7 +762,9 @@
         default void onOverviewShown(boolean fromHome) {}
         default void onQuickScrubStarted() {}
         default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
+        default void onSystemUiStateChanged(int sysuiStateFlags) {}
         default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
+        default void onAssistantGestureCompletion(float velocity) {}
         default void startAssistant(Bundle bundle) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f97be1ea..6329af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -115,6 +115,7 @@
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
+    private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -281,10 +282,16 @@
          * @see IStatusBar#onDisplayReady(int)
          */
         default void onDisplayReady(int displayId) { }
+
         /**
          * @see DisplayManager.DisplayListener#onDisplayRemoved(int)
          */
         default void onDisplayRemoved(int displayId) { }
+
+        /**
+         * @see IStatusBar#onRecentsAnimationStateChanged(boolean)
+         */
+        default void onRecentsAnimationStateChanged(boolean running) { }
     }
 
     @VisibleForTesting
@@ -785,6 +792,14 @@
         }
     }
 
+    @Override
+    public void onRecentsAnimationStateChanged(boolean running) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_RECENTS_ANIMATION_STATE_CHANGED, running ? 1 : 0, 0)
+                    .sendToTarget();
+        }
+    }
+
     private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
         if (displayId == INVALID_DISPLAY) return;
@@ -1071,6 +1086,11 @@
                         mCallbacks.get(i).onDisplayReady(msg.arg1);
                     }
                     break;
+                case MSG_RECENTS_ANIMATION_STATE_CHANGED:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).onRecentsAnimationStateChanged(msg.arg1 > 0);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index fd76a79..25bde3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -78,6 +78,7 @@
 
     private static final int MSG_HIDE_TRANSIENT = 1;
     private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
+    private static final int MSG_SWIPE_UP_TO_UNLOCK = 3;
     private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
 
     private final Context mContext;
@@ -318,6 +319,7 @@
         mTransientIndication = transientIndication;
         mTransientTextColorState = textColorState;
         mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+        mHandler.removeMessages(MSG_SWIPE_UP_TO_UNLOCK);
         if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
             // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
             mWakeLock.setAcquired(true);
@@ -536,10 +538,26 @@
                 hideTransientIndication();
             } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
                 mLockIcon.setTransientBiometricsError(false);
+            } else if (msg.what == MSG_SWIPE_UP_TO_UNLOCK) {
+                showSwipeUpToUnlock();
             }
         }
     };
 
+    private void showSwipeUpToUnlock() {
+        if (mDozing) {
+            return;
+        }
+
+        String message = mContext.getString(R.string.keyguard_unlock);
+        if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+            mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
+        } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+            showTransientIndication(message);
+            hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+        }
+    }
+
     public void setDozing(boolean dozing) {
         if (mDozing == dozing) {
             return;
@@ -620,12 +638,20 @@
                 return;
             }
             animatePadlockError();
+            boolean showSwipeToUnlock =
+                    msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
                         mInitialTextColorState);
             } else if (updateMonitor.isScreenOn()) {
                 showTransientIndication(helpString);
-                hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+                if (!showSwipeToUnlock) {
+                    hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+                }
+            }
+            if (showSwipeToUnlock) {
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWIPE_UP_TO_UNLOCK),
+                        TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index d8070cd..5d4172d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -525,6 +525,7 @@
             setLockscreenPublicMode(isProfilePublic, userId);
             mUsersWithSeperateWorkChallenge.put(userId, needsSeparateChallenge);
         }
+        getEntryManager().updateNotifications();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index b9e0c60..75ef185 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -150,8 +150,8 @@
             if (state != null) {
                 if (!isPlaybackActive(state.getState())) {
                     clearCurrentMediaNotification();
-                    dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
                 }
+                dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
             }
         }
 
@@ -242,7 +242,8 @@
 
     public void addCallback(MediaListener callback) {
         mMediaListeners.add(callback);
-        callback.onMetadataChanged(mMediaMetadata);
+        callback.onMetadataOrStateChanged(mMediaMetadata,
+                getMediaControllerPlaybackState(mMediaController));
     }
 
     public void removeCallback(MediaListener callback) {
@@ -357,9 +358,10 @@
         if (mPresenter != null) {
             mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
         }
+        @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
         ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
         for (int i = 0; i < callbacks.size(); i++) {
-            callbacks.get(i).onMetadataChanged(mMediaMetadata);
+            callbacks.get(i).onMetadataOrStateChanged(mMediaMetadata, state);
         }
     }
 
@@ -698,6 +700,12 @@
     }
 
     public interface MediaListener {
-        void onMetadataChanged(MediaMetadata metadata);
+        /**
+         * Called whenever there's new metadata or playback state.
+         * @param metadata Current metadata.
+         * @param state Current playback state
+         * @see PlaybackState.State
+         */
+        void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index a065f67..b89b5cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -169,7 +169,7 @@
         }
     }
 
-    @SuppressWarnings("unchecked")
+    @Suppress("unchecked_cast")
     private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
         return try {
             noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 942f566..e4e8c80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -94,6 +94,8 @@
 
     private static final int BUTTON_ANIM_TIME_MS = 200;
 
+    private static final boolean SHOW_BUTTON_SUMMARY = false;
+
     private INotificationManager mINotificationManager;
     private PackageManager mPm;
     private MetricsLogger mMetricsLogger;
@@ -580,25 +582,27 @@
             transition.setDuration(BUTTON_ANIM_TIME_MS);
             TransitionManager.beginDelayedTransition(this, transition);
         }
-        if (blockState == ACTION_ALERT) {
-            TextView view = findViewById(R.id.alert_summary);
-            view.setVisibility(VISIBLE);
-            findViewById(R.id.silence_summary).setVisibility(GONE);
-            view.setText(R.string.notification_channel_summary_default);
-        } else {
-            TextView view = findViewById(R.id.silence_summary);
-            view.setVisibility(VISIBLE);
-            findViewById(R.id.alert_summary).setVisibility(GONE);
-            if (mShowInStatusBar) {
-                if (mShowOnLockscreen) {
-                    view.setText(R.string.notification_channel_summary_low_status_lock);
-                } else {
-                    view.setText(R.string.notification_channel_summary_low_status);
-                }
-            } else if (mShowOnLockscreen) {
-                view.setText(R.string.notification_channel_summary_low_lock);
+        if (SHOW_BUTTON_SUMMARY) {
+            if (blockState == ACTION_ALERT) {
+                TextView view = findViewById(R.id.alert_summary);
+                view.setVisibility(VISIBLE);
+                findViewById(R.id.silence_summary).setVisibility(GONE);
+                view.setText(R.string.notification_channel_summary_default);
             } else {
-                view.setText(R.string.notification_channel_summary_low);
+                TextView view = findViewById(R.id.silence_summary);
+                view.setVisibility(VISIBLE);
+                findViewById(R.id.alert_summary).setVisibility(GONE);
+                if (mShowInStatusBar) {
+                    if (mShowOnLockscreen) {
+                        view.setText(R.string.notification_channel_summary_low_status_lock);
+                    } else {
+                        view.setText(R.string.notification_channel_summary_low_status);
+                    }
+                } else if (mShowOnLockscreen) {
+                    view.setText(R.string.notification_channel_summary_low_lock);
+                } else {
+                    view.setText(R.string.notification_channel_summary_low);
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 82599f0..5747bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -28,6 +28,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -40,18 +42,22 @@
 class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvider {
     private final NotificationStackScrollLayout mParent;
     private final ActivityStarter mActivityStarter;
+    private final StatusBarStateController mStatusBarStateController;
     private final boolean mUseMultipleSections;
 
     private SectionHeaderView mGentleHeader;
     private boolean mGentleHeaderVisible = false;
+    @Nullable private ExpandableNotificationRow mFirstGentleNotif;
     @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
 
     NotificationSectionsManager(
             NotificationStackScrollLayout parent,
             ActivityStarter activityStarter,
+            StatusBarStateController statusBarStateController,
             boolean useMultipleSections) {
         mParent = parent;
         mActivityStarter = activityStarter;
+        mStatusBarStateController = statusBarStateController;
         mUseMultipleSections = useMultipleSections;
     }
 
@@ -92,7 +98,7 @@
 
     @Override
     public boolean beginsSection(View view) {
-        return view == mGentleHeader;
+        return view == getFirstLowPriorityChild();
     }
 
     /**
@@ -104,6 +110,7 @@
             return;
         }
 
+        mFirstGentleNotif = null;
         int firstGentleNotifIndex = -1;
 
         final int n = mParent.getChildCount();
@@ -114,6 +121,7 @@
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
                 if (!row.getEntry().isHighPriority()) {
                     firstGentleNotifIndex = i;
+                    mFirstGentleNotif = row;
                     break;
                 }
             }
@@ -126,9 +134,12 @@
     }
 
     private void adjustGentleHeaderVisibilityAndPosition(int firstGentleNotifIndex) {
+        final boolean showGentleHeader =
+                firstGentleNotifIndex != -1
+                        && mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
         final int currentHeaderIndex = mParent.indexOfChild(mGentleHeader);
 
-        if (firstGentleNotifIndex == -1) {
+        if (!showGentleHeader) {
             if (mGentleHeaderVisible) {
                 mGentleHeaderVisible = false;
                 mParent.removeView(mGentleHeader);
@@ -208,7 +219,11 @@
 
     @Nullable
     private ActivatableNotificationView getFirstLowPriorityChild() {
-        return mGentleHeaderVisible ? mGentleHeader : null;
+        if (mGentleHeaderVisible) {
+            return mGentleHeader;
+        } else {
+            return mFirstGentleNotif;
+        }
     }
 
     @Nullable
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 de187f1..8c73e98 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
@@ -409,7 +409,7 @@
     private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
         @Override
         public void getOutline(View view, Outline outline) {
-            if (mAmbientState.isDarkAtAll() || !mShowDarkShelf) {
+            if (mAmbientState.isDarkAtAll()) {
                 float xProgress = mDarkXInterpolator.getInterpolation(
                         (1 - mLinearDarkAmount) * mBackgroundXFactor);
                 outline.setRoundRect(mBackgroundAnimationRect,
@@ -507,7 +507,6 @@
     /**
      * If the {@link NotificationShelf} should be visible when dark.
      */
-    private boolean mShowDarkShelf;
     private boolean mAnimateBottomOnLayout;
 
     @Inject
@@ -518,7 +517,8 @@
             NotificationRoundnessManager notificationRoundnessManager,
             AmbientPulseManager ambientPulseManager,
             DynamicPrivacyController dynamicPrivacyController,
-            ActivityStarter activityStarter) {
+            ActivityStarter activityStarter,
+            StatusBarStateController statusBarStateController) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
 
@@ -534,6 +534,7 @@
                 new NotificationSectionsManager(
                         this,
                         activityStarter,
+                        statusBarStateController,
                         NotificationUtils.useNewInterruptionModel(context));
         mSectionsManager.inflateViews(context);
         mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
@@ -913,7 +914,8 @@
             if (child.getVisibility() != View.GONE
                     && child instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0) {
+                if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0
+                        && row.getProvider().shouldShowGutsOnSnapOpen()) {
                     top = Math.min(top, row.getTranslationY());
                     bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
                 }
@@ -1365,8 +1367,7 @@
             mIsClipped = clipped;
         }
 
-        if (!mAmbientPulseManager.hasNotifications()
-                && mAmbientState.isFullyDark() && mShowDarkShelf) {
+        if (!mAmbientPulseManager.hasNotifications() && mAmbientState.isFullyDark()) {
             setClipBounds(null);
         } else if (mAmbientState.isDarkAtAll()) {
             clipToOutline = true;
@@ -4378,6 +4379,7 @@
         mStackScrollAlgorithm.setIsExpanded(isExpanded);
         mAmbientState.setShadeExpanded(isExpanded);
         mStateAnimator.setShadeExpanded(isExpanded);
+        mSwipeHelper.setIsExpanded(isExpanded);
         if (changed) {
             if (!mIsExpanded) {
                 mGroupManager.collapseAllGroups();
@@ -4719,9 +4721,6 @@
         if (mAmbientState.isDark() == dark) {
             return;
         }
-        if (!dark) {
-            mShowDarkShelf = false;
-        }
         mAmbientState.setDark(dark);
         if (animate && mAnimationsEnabled) {
             mDarkNeedsAnimation = true;
@@ -4783,7 +4782,7 @@
         boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
         if (nowFullyDark != wasFullyDark) {
             updateContentHeight();
-            if (nowFullyDark && mShowDarkShelf) {
+            if (nowFullyDark) {
                 updateDarkShelfVisibility();
             }
         }
@@ -4799,14 +4798,6 @@
         requestChildrenUpdate();
     }
 
-    /**
-     * If the shelf should be visible when the device is in ambient mode (dozing.)
-     */
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void showDarkShelf() {
-        mShowDarkShelf = true;
-    }
-
     private void updateDarkShelfVisibility() {
         DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
         if (dozeParameters.shouldControlScreenOff()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 4569b66..0f711922 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -48,6 +48,7 @@
     private static final long SWIPE_MENU_TIMING = 200;
 
     private NotificationMenuRowPlugin mCurrMenuRow;
+    private boolean mIsExpanded;
 
     public NotificationSwipeHelper(int swipeDirection, NotificationCallback callback,
             Context context, NotificationMenuRowPlugin.OnMenuEventListener menuListener) {
@@ -97,6 +98,10 @@
         return mFalsingCheck;
     }
 
+    public void setIsExpanded(boolean isExpanded) {
+        mIsExpanded = isExpanded;
+    }
+
     @Override
     protected void onChildSnappedBack(View animView, float targetLeft) {
         if (mCurrMenuRow != null && targetLeft == 0) {
@@ -200,7 +205,9 @@
         boolean slowSwipedFarEnough = swipedEnoughToShowMenu(menuRow) && isSlowSwipe;
         boolean isFastNonDismissGesture =
                 gestureFastEnough && !gestureTowardsMenu && !isDismissGesture;
-        boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough || isFastNonDismissGesture;
+        boolean isAbleToShowMenu = menuRow.shouldShowGutsOnSnapOpen() || mIsExpanded;
+        boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough
+                || (isFastNonDismissGesture && isAbleToShowMenu);
         int menuSnapTarget = menuRow.getMenuSnapTarget();
         boolean isNonFalseMenuRevealingGesture =
                 !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 4638c40..aba64bdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -17,10 +17,6 @@
 
 import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
@@ -318,9 +314,7 @@
             // either the bouncer is showing or the notification panel is hidden
             int stateFlags = mOverviewProxyService.getSystemUiStateFlags();
             mIsOnLeftEdge = ev.getX() <= mEdgeWidth;
-            mAllowGesture = (stateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
-                    && ((stateFlags & SYSUI_STATE_BOUNCER_SHOWING) == SYSUI_STATE_BOUNCER_SHOWING
-                            || (stateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0)
+            mAllowGesture = !QuickStepContract.isBackGestureDisabled(stateFlags)
                     && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
             if (mAllowGesture) {
                 mEdgePanelLp.gravity = mIsOnLeftEdge
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index a3e3426..b8887c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -44,6 +44,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -484,10 +485,13 @@
     }
 
     /**
-     * Triggered after the unlock animation is over and the user is looking at launcher.
+     * Called whenever the scrims become opaque, transparent or semi-transparent.
      */
-    public void onKeyguardFadedAway() {
-        mWakeAndUnlockRunning = false;
-        update();
+    public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
+        if (mWakeAndUnlockRunning
+                && scrimsVisible == ScrimController.VISIBILITY_FULLY_TRANSPARENT) {
+            mWakeAndUnlockRunning = false;
+            update();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 4603ba6..4f223c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -25,6 +25,7 @@
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.os.VibrationEffect;
+import android.util.DisplayMetrics;
 import android.util.MathUtils;
 import android.view.ContextThemeWrapper;
 import android.view.MotionEvent;
@@ -47,14 +48,14 @@
 
 public class NavigationBarEdgePanel extends View {
 
-    private static final long COLOR_ANIMATION_DURATION_MS = 100;
-    private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 140;
+    private static final long COLOR_ANIMATION_DURATION_MS = 120;
+    private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80;
     private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100;
 
     /**
-     * The minimum time required since the first vibration effect to receive a second one
+     * The time required since the first vibration effect to automatically trigger a click
      */
-    private static final int MIN_TIME_BETWEEN_EFFECTS_MS = 120;
+    private static final int GESTURE_DURATION_FOR_CLICK_MS = 400;
 
     /**
      * The size of the protection of the arrow in px. Only used if this is not background protected
@@ -79,7 +80,7 @@
     /**
      * The angle that is added per 1000 px speed to the angle of the leg
      */
-    private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 8;
+    private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 4;
 
     /**
      * The maximum angle offset allowed due to speed
@@ -92,15 +93,15 @@
     private static final float ARROW_THICKNESS_DP = 2.5f;
 
     /**
-     * The amount of rubber banding we do for the horizontal translation beyond the base translation
+     * The amount of rubber banding we do for the vertical translation
      */
-    private static final int RUBBER_BAND_AMOUNT = 10;
+    private static final int RUBBER_BAND_AMOUNT = 15;
 
     /**
      * The interpolator used to rubberband
      */
     private static final Interpolator RUBBER_BAND_INTERPOLATOR
-            = new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT, 1.0f, 1.0f, 1.0f);
+            = new PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f);
 
     /**
      * The amount of rubber banding we do for the translation before base translation
@@ -189,6 +190,7 @@
     private int mCurrentArrowColor;
     private float mDisappearAmount;
     private long mVibrationTime;
+    private int mScreenSize;
 
     private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener
             = new DynamicAnimation.OnAnimationEndListener() {
@@ -281,9 +283,8 @@
         mAngleAnimation =
                 new SpringAnimation(this, CURRENT_ANGLE);
         mAngleAppearForce = new SpringForce()
-                .setStiffness(SpringForce.STIFFNESS_LOW)
-                .setDampingRatio(0.4f)
-                .setFinalPosition(ARROW_ANGLE_WHEN_EXTENDED_DEGREES);
+                .setStiffness(500)
+                .setDampingRatio(0.5f);
         mAngleDisappearForce = new SpringForce()
                 .setStiffness(SpringForce.STIFFNESS_MEDIUM)
                 .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
@@ -447,13 +448,14 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
-        // TODO: read the gesture length from the nav controller.
         mMaxTranslation = getWidth() - mArrowPaddingEnd;
     }
 
     private void loadDimens() {
         mArrowPaddingEnd = getContext().getResources().getDimensionPixelSize(
                 R.dimen.navigation_edge_panel_padding);
+        DisplayMetrics metrics = getResources().getDisplayMetrics();
+        mScreenSize = Math.min(metrics.widthPixels, metrics.heightPixels);
     }
 
     private void updateArrowDirection() {
@@ -510,7 +512,7 @@
         if (!mArrowsPointLeft) {
             x = -x;
         }
-        float extent = 1.0f - mDisappearAmount;
+        float extent = MathUtils.lerp(1.0f, 0.75f, mDisappearAmount);
         x = x * extent;
         y = y * extent;
         mArrowPath.reset();
@@ -529,27 +531,29 @@
     }
 
     private void triggerBack() {
-        if (SystemClock.uptimeMillis() - mVibrationTime >= MIN_TIME_BETWEEN_EFFECTS_MS) {
-            mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
-        }
         mVelocityTracker.computeCurrentVelocity(1000);
         // Only do the extra translation if we're not already flinging
-        boolean doExtraTranslation = Math.abs(mVelocityTracker.getXVelocity()) < 1000;
-        if (doExtraTranslation) {
-            setDesiredTranslation(mDesiredTranslation + dp(16), true /* animate */);
+        boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500;
+        if (isSlow
+                || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) {
+            mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
         }
 
         // Let's also snap the angle a bit
-        if (mAngleOffset < -4) {
-            mAngleOffset = Math.max(-16, mAngleOffset - 16);
+        if (mAngleOffset > -4) {
+            mAngleOffset = Math.max(-8, mAngleOffset - 8);
             updateAngle(true /* animated */);
         }
 
         // Finally, after the translation, animate back and disappear the arrow
         Runnable translationEnd = () -> {
-            setTriggerBack(false /* false */, true /* animate */);
+            // let's snap it back
+            mAngleOffset = Math.max(0, mAngleOffset + 8);
+            updateAngle(true /* animated */);
+
             mTranslationAnimation.setSpring(mTriggerBackSpring);
-            setDesiredTranslation(0, true /* animated */);
+            // Translate the arrow back a bit to make for a nice transition
+            setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);
             animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)
                     .withEndAction(() -> setVisibility(GONE));
             mArrowDisappearAnimation.start();
@@ -584,6 +588,7 @@
         setTriggerBack(false /* triggerBack */, false /* animated */);
         setDesiredTranslation(0, false /* animated */);
         setCurrentTranslation(0);
+        updateAngle(false /* animate */);
         mPreviousTouchTranslation = 0;
         mTotalTouchDelta = 0;
         mVibrationTime = 0;
@@ -621,7 +626,7 @@
         // Let's make sure we only go to the baseextend and apply rubberbanding afterwards
         if (touchTranslation > mBaseTranslation) {
             float diff = touchTranslation - mBaseTranslation;
-            float progress = MathUtils.saturate(diff / (mBaseTranslation * RUBBER_BAND_AMOUNT));
+            float progress = MathUtils.saturate(diff / (mScreenSize - mBaseTranslation));
             progress = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
                     * (mMaxTranslation - mBaseTranslation);
             touchTranslation = mBaseTranslation + progress;
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 1429718..c9ba76c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -329,8 +329,7 @@
         notifyNavigationBarScreenOn();
 
         mOverviewProxyService.addCallback(mOverviewProxyListener);
-        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
-                !isNavBarWindowVisible(), mDisplayId);
+        updateSystemUiStateFlags(-1);
 
         // Currently there is no accelerometer sensor on non-default display.
         if (mIsOnDefaultDisplay) {
@@ -458,8 +457,7 @@
             mNavigationBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
 
-            mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
-                    !isNavBarWindowVisible(), mDisplayId);
+            updateSystemUiStateFlags(-1);
             mNavigationBarView.getRotateSuggestionButton()
                     .onNavigationBarWindowVisibilityChange(isNavBarWindowVisible());
         }
@@ -576,7 +574,9 @@
                 | StatusBarManager.DISABLE_SEARCH);
         if (masked != mDisabledFlags1) {
             mDisabledFlags1 = masked;
-            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
+            if (mNavigationBarView != null) {
+                mNavigationBarView.setDisabledFlags(state1);
+            }
             updateScreenPinningGestures();
         }
 
@@ -860,18 +860,30 @@
 
     private void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
         boolean[] feedbackEnabled = new boolean[1];
-        int flags = getA11yButtonState(feedbackEnabled);
+        int a11yFlags = getA11yButtonState(feedbackEnabled);
 
         mNavigationBarView.getRotateSuggestionButton()
                 .setAccessibilityFeedbackEnabled(feedbackEnabled[0]);
 
-        boolean clickable = (flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
-        boolean longClickable = (flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+        boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+        boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
         mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
-        mOverviewProxyService.setSystemUiStateFlag(
-                SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable, mDisplayId);
-        mOverviewProxyService.setSystemUiStateFlag(
-                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable, mDisplayId);
+
+        updateSystemUiStateFlags(a11yFlags);
+    }
+
+    public void updateSystemUiStateFlags(int a11yFlags) {
+        if (a11yFlags < 0) {
+            a11yFlags = getA11yButtonState(null);
+        }
+        boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+        boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE,
+                clickable, mDisplayId);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE,
+                longClickable, mDisplayId);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN,
+                !isNavBarWindowVisible(), mDisplayId);
     }
 
     /**
@@ -979,6 +991,17 @@
     public void onNavigationModeChanged(int mode) {
         mNavBarMode = mode;
         updateScreenPinningGestures();
+
+        // Workaround for b/132825155, for secondary users, we currently don't receive configuration
+        // changes on overlay package change since SystemUI runs for the system user. In this case,
+        // trigger a new configuration change to ensure that the nav bar is updated in the same way.
+        int userId = ActivityManagerWrapper.getInstance().getCurrentUserId();
+        if (userId != UserHandle.USER_SYSTEM) {
+            mHandler.post(() -> {
+                FragmentHostManager fragmentHost = FragmentHostManager.get(mNavigationBarView);
+                fragmentHost.reloadFragments();
+            });
+        }
     }
 
     public void disableAnimationsDuringHide(long delay) {
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 d85b8ac..5f61975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -18,7 +18,10 @@
 
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 
 import android.animation.LayoutTransition;
@@ -319,6 +322,7 @@
 
     public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
         mPanelView = panel;
+        updateSystemUiStateFlags();
     }
 
     @Override
@@ -573,6 +577,7 @@
         updateNavButtonIcons();
         updateSlippery();
         setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
+        updateSystemUiStateFlags();
     }
 
     public void updateNavButtonIcons() {
@@ -700,8 +705,21 @@
 
     public void onPanelExpandedChange() {
         updateSlippery();
-        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
-                mPanelView.isFullyExpanded(), getContext().getDisplayId());
+        updateSystemUiStateFlags();
+    }
+
+    public void updateSystemUiStateFlags() {
+        int displayId = mContext.getDisplayId();
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING,
+                ActivityManagerWrapper.getInstance().isScreenPinningActive(), displayId);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_OVERVIEW_DISABLED,
+                (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0, displayId);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_HOME_DISABLED,
+                (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0, displayId);
+        if (mPanelView != null) {
+            mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
+                    mPanelView.isFullyExpanded() && !mPanelView.isInSettings(), displayId);
+        }
     }
 
     public void updateStates() {
@@ -967,12 +985,6 @@
             // If car mode or density changes, we need to reset the icons.
             updateNavButtonIcons();
         }
-
-        if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
-            mTintController.start();
-        } else {
-            mTintController.stop();
-        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index fa2263f..6e56854 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -63,6 +63,7 @@
     }
 
     private final Context mContext;
+    private Context mCurrentUserContext;
     private final IOverlayManager mOverlayManager;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final UiOffloadThread mUiOffloadThread;
@@ -127,6 +128,7 @@
             DeviceProvisionedController deviceProvisionedController,
             UiOffloadThread uiOffloadThread) {
         mContext = context;
+        mCurrentUserContext = context;
         mOverlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
         mUiOffloadThread = uiOffloadThread;
@@ -145,13 +147,13 @@
     }
 
     public void updateCurrentInteractionMode(boolean notify) {
-        Context context = getCurrentUserContext();
-        int mode = getCurrentInteractionMode(context);
+        mCurrentUserContext = getCurrentUserContext();
+        int mode = getCurrentInteractionMode(mCurrentUserContext);
         mMode = mode;
         if (DEBUG) {
             Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode
-                    + " contextUser=" + context.getUserId());
-            dumpAssetPaths(context);
+                    + " contextUser=" + mCurrentUserContext.getUserId());
+            dumpAssetPaths(mCurrentUserContext);
         }
 
         if (notify) {
@@ -163,7 +165,7 @@
 
     public int addListener(ModeChangedListener listener) {
         mListeners.add(listener);
-        return getCurrentInteractionMode(mContext);
+        return getCurrentInteractionMode(mCurrentUserContext);
     }
 
     public void removeListener(ModeChangedListener listener) {
@@ -265,7 +267,7 @@
             defaultOverlays = "failed_to_fetch";
         }
         pw.println("  defaultOverlays=" + defaultOverlays);
-        dumpAssetPaths(getCurrentUserContext());
+        dumpAssetPaths(mCurrentUserContext);
     }
 
     private void dumpAssetPaths(Context context) {
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 da9f244..3fe7f36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2853,9 +2853,6 @@
         if (dozing == mDozing) return;
         mDozing = dozing;
         mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
-        if (mDozing) {
-            mNotificationStackScroller.showDarkShelf();
-        }
         mKeyguardBottomArea.setDozing(mDozing, animate);
 
         if (mBarState == StatusBarState.KEYGUARD
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 c6bbf7b..a9a3b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -1169,14 +1169,13 @@
     }
 
     protected void notifyBarPanelExpansionChanged() {
-        float fraction = mInstantExpanding ? 1 : mExpandedFraction;
         if (mBar != null) {
-            mBar.panelExpansionChanged(fraction, fraction > 0f
+            mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
                     || mPeekAnimator != null || mInstantExpanding
                     || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
         }
         if (mExpansionListener != null) {
-            mExpansionListener.onPanelExpansionChanged(fraction, mTracking);
+            mExpansionListener.onPanelExpansionChanged(mExpandedFraction, mTracking);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 50e406f..ee43879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -44,6 +44,7 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.privacy.PrivacyItem;
 import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyItemControllerKt;
 import com.android.systemui.privacy.PrivacyType;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
@@ -82,7 +83,8 @@
                 ZenModeController.Callback,
                 DeviceProvisionedListener,
                 KeyguardMonitor.Callback,
-                PrivacyItemController.Callback {
+                PrivacyItemController.Callback,
+                LocationController.LocationChangeCallback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -257,6 +259,7 @@
         mKeyguardMonitor.addCallback(this);
         mPrivacyItemController.addCallback(this);
         mSensorPrivacyController.addCallback(mSensorPrivacyListener);
+        mLocationController.addCallback(this);
 
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
     }
@@ -635,6 +638,20 @@
         mIconController.setIconVisibility(mSlotLocation, showLocation);
     }
 
+    @Override
+    public void onLocationActiveChanged(boolean active) {
+        if (!PrivacyItemControllerKt.isPermissionsHubEnabled()) updateLocation();
+    }
+
+    // Updates the status view based on the current state of location requests.
+    private void updateLocation() {
+        if (mLocationController.isLocationActive()) {
+            mIconController.setIconVisibility(mSlotLocation, true);
+        } else {
+            mIconController.setIconVisibility(mSlotLocation, false);
+        }
+    }
+
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
index d59319e..2b0bb21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
@@ -27,6 +27,7 @@
 import android.view.CompositionSamplingListener;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 
 import com.android.systemui.R;
@@ -153,8 +154,12 @@
         boolean isSamplingEnabled = mSamplingEnabled && !mSamplingRequestBounds.isEmpty()
                 && (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart);
         if (isSamplingEnabled) {
-            SurfaceControl stopLayerControl = mSampledView.getViewRootImpl().getSurfaceControl();
-            if (!stopLayerControl.isValid()) {
+            ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
+            SurfaceControl stopLayerControl = null;
+            if (viewRootImpl != null) {
+                 stopLayerControl = viewRootImpl.getSurfaceControl();
+            }
+            if (stopLayerControl == null || !stopLayerControl.isValid()) {
                 if (!mWaitingOnDraw) {
                     mWaitingOnDraw = true;
                     // The view might be attached but we haven't drawn yet, so wait until the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index ed79476..5dcbea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.IntDef;
 import android.app.AlarmManager;
 import android.content.Context;
 import android.graphics.Color;
@@ -52,6 +53,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.function.Consumer;
 
 /**
@@ -84,6 +87,15 @@
      * When at least 1 scrim is fully opaque (alpha set to 1.)
      */
     public static final int VISIBILITY_FULLY_OPAQUE = 2;
+
+    @IntDef(prefix = { "VISIBILITY_" }, value = {
+            VISIBILITY_FULLY_TRANSPARENT,
+            VISIBILITY_SEMI_TRANSPARENT,
+            VISIBILITY_FULLY_OPAQUE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScrimVisibility {}
+
     /**
      * Default alpha value for most scrims.
      */
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 17f0d5a..72726b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
 import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -538,7 +539,6 @@
             }
             if (mKeyguardMonitor.isKeyguardFadingAway()) {
                 mStatusBarKeyguardViewManager.onKeyguardFadedAway();
-                mStatusBarWindow.onKeyguardFadedAway();
             }
         }
 
@@ -786,6 +786,7 @@
         Dependency.get(InitController.class).addPostInitTask(
                 () -> setUpDisableFlags(disabledFlags1, disabledFlags2));
 
+        updateSystemUiStateFlags();
     }
 
     // ================================================================================
@@ -929,6 +930,9 @@
                     if (mStatusBarWindowController != null) {
                         mStatusBarWindowController.setScrimsVisibility(scrimsVisible);
                     }
+                    if (mStatusBarWindow != null) {
+                        mStatusBarWindow.onScrimVisibilityChanged(scrimsVisible);
+                    }
                 }, DozeParameters.getInstance(mContext),
                 mContext.getSystemService(AlarmManager.class));
         mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
@@ -2154,6 +2158,11 @@
         }
     }
 
+    @Override
+    public void onRecentsAnimationStateChanged(boolean running) {
+        setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
+    }
+
     protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) {
         return computeBarMode(oldVal, newVal);
     }
@@ -2241,6 +2250,20 @@
         }
     }
 
+    /** Returns whether the top activity is in fullscreen mode. */
+    public boolean inFullscreenMode() {
+        return 0
+                != (mSystemUiVisibility
+                        & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION));
+    }
+
+    /** Returns whether the top activity is in immersive mode. */
+    public boolean inImmersiveMode() {
+        return 0
+                != (mSystemUiVisibility
+                        & (View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY));
+    }
+
     private boolean areLightsOn() {
         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
     }
@@ -3117,7 +3140,6 @@
 
             // If the state didn't change, we may still need to update public mode
             mLockscreenUserManager.updatePublicMode();
-            mEntryManager.updateNotifications();
         }
         if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
             if (!mStatusBarStateController.isKeyguardRequested()) {
@@ -3272,11 +3294,6 @@
             }
             return true;
         }
-        if (mStatusBarWindowController.hasAssistActiveSession()) {
-            // Back pressed during an assist session, cancel it.
-            mAssistManager.hideAssist();
-            return true;
-        }
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
             if (mNotificationPanel.canPanelBeCollapsed()) {
                 animateCollapsePanels();
@@ -3401,6 +3418,7 @@
         updateDozingState();
         checkBarModes();
         updateScrimController();
+        updateSystemUiStateFlags();
         mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
         mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
                 mUnlockMethodCache.isMethodSecure(),
@@ -3567,10 +3585,16 @@
         if (!mBouncerShowing) {
             updatePanelExpansionForKeyguard();
         }
+        updateSystemUiStateFlags();
+    }
 
-        // Notify overview proxy service of the new states
-        Dependency.get(OverviewProxyService.class).setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING,
-                isBouncerShowing(), mContext.getDisplayId());
+    public void updateSystemUiStateFlags() {
+        OverviewProxyService overviewProxyService = Dependency.get(OverviewProxyService.class);
+        overviewProxyService.setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
+                mStatusBarStateController.getState() == StatusBarState.KEYGUARD,
+                mDisplayId);
+        overviewProxyService.setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING,
+                isBouncerShowing(), mDisplayId);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 0865eb6..6691f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -111,6 +111,7 @@
     }
 
     protected void onWorkChallengeChanged() {
+        mLockscreenUserManager.updatePublicMode();
         if (mPendingWorkRemoteInputView != null
                 && !mLockscreenUserManager.isAnyProfilePublicMode()) {
             // Expand notification panel and the notification row, then click on remote input view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 603c969..e949ade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -60,9 +60,6 @@
 
         initResources();
 
-        mAssistManager.setAssistSysUiChangeListener((isVisible) -> {
-            updateTouchableRegion();
-        });
         mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
             updateTouchableRegion();
         });
@@ -79,7 +76,6 @@
         boolean shouldObserve =
                 mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpManager.isHeadsUpGoingAway()
                         || mBubbleController.hasBubbles()
-                        || mAssistManager.hasAssistUi()
                         || mForceCollapsedUntilLayout
                         || hasCutoutInset;
         if (shouldObserve == mShouldAdjustInsets) {
@@ -143,11 +139,6 @@
         if (bubbleRect != null) {
             info.touchableRegion.union(bubbleRect);
         }
-
-        Rect assistRect = mAssistManager.getTouchableRegion();
-        if (assistRect != null) {
-            info.touchableRegion.union(assistRect);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 2969cf3..631920c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -208,8 +208,7 @@
                 || state.bubbleExpanded) {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable
-                || state.assistActiveSession) {
+        } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else {
@@ -247,7 +246,7 @@
     private boolean isExpanded(State state) {
         return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
                 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
-                || state.headsUpShowing || state.bubblesShowing || state.assistShowing
+                || state.headsUpShowing || state.bubblesShowing
                 || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
     }
 
@@ -501,32 +500,6 @@
     }
 
     /**
-     * Sets whether assist UI is showing on the screen.
-     *
-     * @param assistShowing whether any assist UI is being shown.
-     * @param activeSession whether AssistManager has an active assist session in progress.
-     */
-    public void setAssistState(boolean assistShowing, boolean activeSession) {
-        mCurrentState.assistShowing = assistShowing;
-        mCurrentState.assistActiveSession = activeSession;
-        apply(mCurrentState);
-    }
-
-    /**
-     * The assist UI showing state for the status bar.
-     */
-    public boolean getAssistShowing() {
-        return mCurrentState.assistShowing;
-    }
-
-    /**
-     * The AssistManager is handling an active assist session.
-     */
-    public boolean hasAssistActiveSession() {
-        return mCurrentState.assistActiveSession;
-    }
-
-    /**
      * Sets if there is a bubble being expanded on the screen.
      */
     public void setBubbleExpanded(boolean bubbleExpanded) {
@@ -602,10 +575,6 @@
         boolean notTouchable;
         boolean bubblesShowing;
         boolean bubbleExpanded;
-        // Assist manager is rendering any UI.
-        boolean assistShowing;
-        // Assist manager is handling an active assist session.
-        boolean assistActiveSession;
 
         /**
          * The {@link StatusBar} state from the status bar.
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 712e962..049972e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -68,6 +68,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
 import com.android.systemui.tuner.TunerService;
 
 import java.io.FileDescriptor;
@@ -277,15 +278,6 @@
         }
     }
 
-    /**
-     * Called after finished unlocking and the status bar window is already collapsed.
-     */
-    public void onKeyguardFadedAway() {
-        if (mLockIcon != null) {
-            mLockIcon.onKeyguardFadedAway();
-        }
-    }
-
     public void setStatusBarView(PhoneStatusBarView statusBarView) {
         mStatusBarView = statusBarView;
     }
@@ -509,6 +501,15 @@
         pw.print("  mTouchActive="); pw.println(mTouchActive);
     }
 
+    /**
+     * Called whenever the scrims become opaque, transparent or semi-transparent.
+     */
+    public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
+        if (mLockIcon != null) {
+            mLockIcon.onScrimVisibilityChanged(scrimsVisible);
+        }
+    }
+
     public class LayoutParams extends FrameLayout.LayoutParams {
 
         public boolean ignoreRightInset;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 6e36c01..d061649 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -250,6 +250,15 @@
     }
 
     /**
+     * Sets the list of ignored icon slots clearing the current list.
+     * @param slots names of the icons to ignore
+     */
+    public void setIgnoredSlots(List<String> slots) {
+        mIgnoredSlots.clear();
+        addIgnoredSlots(slots);
+    }
+
+    /**
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
@@ -369,8 +378,18 @@
         public int visibleState = STATE_ICON;
         public boolean justAdded = true;
 
+        // How far we are from the end of the view actually is the most relevant for animation
+        float distanceToViewEnd = -1;
+
         @Override
         public void applyToView(View view) {
+            float parentWidth = 0;
+            if (view.getParent() instanceof View) {
+                parentWidth = ((View) view.getParent()).getWidth();
+            }
+
+            float currentDistanceToEnd = parentWidth - xTranslation;
+
             if (!(view instanceof StatusIconDisplayable)) {
                 return;
             }
@@ -394,7 +413,7 @@
                     // all other transitions (to/from dot, etc)
                     animationProperties = ANIMATE_ALL_PROPERTIES;
                 }
-            } else if (visibleState != STATE_HIDDEN && xTranslation != view.getTranslationX()) {
+            } else if (visibleState != STATE_HIDDEN && distanceToViewEnd != currentDistanceToEnd) {
                 // Visibility isn't changing, just animate position
                 animationProperties = X_ANIMATION_PROPERTIES;
             }
@@ -407,6 +426,8 @@
             }
 
             justAdded = false;
+            distanceToViewEnd = currentDistanceToEnd;
+
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 505dd16..a5bb92d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.R;
+import com.android.systemui.util.Utils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -234,6 +235,10 @@
 
     private String getAppName(String packageName) {
         final PackageManager pm = mContext.getPackageManager();
+        if (Utils.isHeadlessRemoteDisplayProvider(pm, packageName)) {
+            return "";
+        }
+
         try {
             final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
             if (appInfo != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
index ecf608b..aec14be 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbContaminantActivity.java
@@ -16,30 +16,33 @@
 
 package com.android.systemui.usb;
 
-import android.app.AlertDialog;
-import android.content.DialogInterface;
+import android.app.Activity;
 import android.content.Intent;
 import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
+import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
 import com.android.systemui.R;
 
 /**
  * Activity that alerts the user when contaminant is detected on USB port.
  */
-public class UsbContaminantActivity extends AlertActivity
-                                  implements DialogInterface.OnClickListener {
+public class UsbContaminantActivity extends Activity implements View.OnClickListener {
     private static final String TAG = "UsbContaminantActivity";
 
     private UsbPort mUsbPort;
+    private TextView mLearnMore;
+    private TextView mGotIt;
+    private TextView mEnableUsb;
+    private TextView mTitle;
+    private TextView mMessage;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -47,22 +50,30 @@
         window.addSystemFlags(WindowManager.LayoutParams
                 .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
 
         super.onCreate(icicle);
+        setContentView(R.layout.contaminant_dialog);
 
         Intent intent = getIntent();
         ParcelableUsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
         mUsbPort = port.getUsbPort(getSystemService(UsbManager.class));
 
-        final AlertController.AlertParams ap = mAlertParams;
-        ap.mTitle = getString(R.string.usb_contaminant_title);
-        ap.mMessage = getString(R.string.usb_contaminant_message);
-        ap.mNegativeButtonText = getString(android.R.string.ok);
-        ap.mNeutralButtonText = getString(R.string.usb_disable_contaminant_detection);
-        ap.mNegativeButtonListener = this;
-        ap.mNeutralButtonListener = this;
+        mLearnMore = findViewById(R.id.learnMore);
+        mEnableUsb = findViewById(R.id.enableUsb);
+        mGotIt = findViewById(R.id.gotIt);
+        mTitle = findViewById(R.id.title);
+        mMessage = findViewById(R.id.message);
 
-        setupAlert();
+        mTitle.setText(getString(R.string.usb_contaminant_title));
+        mMessage.setText(getString(R.string.usb_contaminant_message));
+        mEnableUsb.setText(getString(R.string.usb_disable_contaminant_detection));
+        mGotIt.setText(getString(R.string.got_it));
+        mLearnMore.setText(getString(R.string.learn_more));
+
+        mEnableUsb.setOnClickListener(this);
+        mGotIt.setOnClickListener(this);
+        mLearnMore.setOnClickListener(this);
     }
 
     @Override
@@ -71,8 +82,8 @@
     }
 
     @Override
-    public void onClick(DialogInterface dialog, int which) {
-        if (which == AlertDialog.BUTTON_NEUTRAL) {
+    public void onClick(View v) {
+        if (v == mEnableUsb) {
             try {
                 mUsbPort.enableContaminantDetection(false);
                 Toast.makeText(this, R.string.usb_port_enabled,
@@ -80,6 +91,13 @@
             } catch (Exception e) {
                 Log.e(TAG, "Unable to notify Usb service", e);
             }
+        } else if (v == mLearnMore) {
+            final Intent intent = new Intent();
+            intent.setClassName("com.android.settings",
+                    "com.android.settings.HelpTrampoline");
+            intent.putExtra(Intent.EXTRA_TEXT,
+                    "help_url_usb_contaminant_detected");
+            startActivity(intent);
         }
         finish();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 6b4f7ed..1102bb7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -14,6 +14,9 @@
 
 package com.android.systemui.util;
 
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.view.View;
 
 import com.android.systemui.SysUiServiceProvider;
@@ -87,4 +90,24 @@
             return mDisabled;
         }
     }
+
+
+    /**
+     * Returns {@code true} iff the package {@code packageName} is a headless remote display
+     * provider, i.e, that the package holds the privileged {@code REMOTE_DISPLAY_PROVIDER}
+     * permission and that it doesn't host a launcher icon.
+     */
+    public static boolean isHeadlessRemoteDisplayProvider(PackageManager pm, String packageName) {
+        if (pm.checkPermission(Manifest.permission.REMOTE_DISPLAY_PROVIDER, packageName)
+                != PackageManager.PERMISSION_GRANTED) {
+            return false;
+        }
+
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        homeIntent.setPackage(packageName);
+
+        return pm.queryIntentActivities(homeIntent, 0).isEmpty();
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6bed43e..6208ab8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -143,10 +143,21 @@
     @Test
     public void testTelephonyCapable_SimState_Absent() {
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
-                , IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
-                , putPhoneInfo(intent,null, false));
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
+                putPhoneInfo(intent, null, false));
+        mTestableLooper.processAllMessages();
+        assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
+    }
+
+    @Test
+    public void testTelephonyCapable_SimState_CardIOError() {
+        Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+        mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
+                putPhoneInfo(intent, null, false));
         mTestableLooper.processAllMessages();
         assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index 2990b3e..74b15fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -45,7 +45,8 @@
 
     @Spy
     private ExpandedAnimationController mExpandedController =
-            new ExpandedAnimationController(new Point(500, 1000) /* displaySize */);
+            new ExpandedAnimationController(
+                new Point(500, 1000) /* displaySize */, 0 /* expandedViewPadding */);
     private int mStackOffset;
     private float mBubblePadding;
     private float mBubbleSize;
@@ -144,6 +145,7 @@
     }
 
     @Test
+    @Ignore("Flaky")
     public void testMagnetToDismiss_dismiss() throws InterruptedException {
         expand();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index a398fba..c6acef5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -138,6 +138,16 @@
         }
 
         @Override
+        public boolean post(Runnable action) {
+            return mMainThreadHandler.post(action);
+        }
+
+        @Override
+        public boolean postDelayed(Runnable action, long delayMillis) {
+            return mMainThreadHandler.postDelayed(action, delayMillis);
+        }
+
+        @Override
         public void setController(PhysicsAnimationController controller) {
             runOnMainThreadAndBlock(
                     () -> super.setController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index b83276b..9218a8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -38,6 +38,9 @@
 import org.mockito.Mockito;
 import org.mockito.Spy;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
@@ -46,12 +49,13 @@
     private TestableStackController mStackController = new TestableStackController();
 
     private int mStackOffset;
+    private Runnable mCheckStartPosSet;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        addOneMoreThanRenderLimitBubbles();
         mLayout.setController(mStackController);
+        addOneMoreThanRenderLimitBubbles();
         mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
     }
 
@@ -154,6 +158,7 @@
     }
 
     @Test
+    @Ignore("Flaking")
     public void testChildAdded() throws InterruptedException {
         // Move the stack to y = 500.
         mStackController.moveFirstBubbleWithStackFollowing(0f, 500f);
@@ -166,6 +171,8 @@
                 0,
                 new FrameLayout.LayoutParams(50, 50));
 
+        waitForStartPosToBeSet();
+        waitForLayoutMessageQueue();
         waitForPropertyAnimations(
                 DynamicAnimation.TRANSLATION_X,
                 DynamicAnimation.TRANSLATION_Y,
@@ -227,6 +234,7 @@
     }
 
     @Test
+    @Ignore("Flaky")
     public void testMagnetToDismiss_dismiss() throws InterruptedException {
         final Runnable after = Mockito.mock(Runnable.class);
 
@@ -246,6 +254,7 @@
     }
 
     @Test
+    @Ignore("Flaking")
     public void testMagnetToDismiss_demagnetizeThenDrag() throws InterruptedException {
         final Runnable after = Mockito.mock(Runnable.class);
 
@@ -293,6 +302,28 @@
         }
     }
 
+    /** Waits up to 2 seconds for the initial stack position to be initialized. */
+    private void waitForStartPosToBeSet() throws InterruptedException {
+        final CountDownLatch animLatch = new CountDownLatch(1);
+
+        mCheckStartPosSet = () -> {
+            if (mStackController.getStackPosition().x >= 0) {
+                animLatch.countDown();
+            } else {
+                mMainThreadHandler.post(mCheckStartPosSet);
+            }
+        };
+
+        mMainThreadHandler.post(mCheckStartPosSet);
+
+        try {
+            animLatch.await(2, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            mMainThreadHandler.removeCallbacks(mCheckStartPosSet);
+            throw e;
+        }
+    }
+
     /**
      * Testable version of the stack controller that dispatches its animations on the main thread.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index c534de7..355e260 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -29,6 +29,7 @@
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
 import android.net.Uri;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
@@ -105,7 +106,7 @@
         MediaMetadata metadata = mock(MediaMetadata.class);
         when(metadata.getText(any())).thenReturn("metadata");
         mProvider.onDozingChanged(true);
-        mProvider.onMetadataChanged(metadata);
+        mProvider.onMetadataOrStateChanged(metadata, PlaybackState.STATE_PLAYING);
         mProvider.onBindSlice(mProvider.getUri());
         verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
         verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST));
@@ -170,7 +171,7 @@
     public void onMetadataChanged_updatesSlice() {
         mProvider.onDozingChanged(true);
         reset(mContentResolver);
-        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
         verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
 
         // Hides after waking up
@@ -181,7 +182,7 @@
 
     @Test
     public void onDozingChanged_updatesSliceIfMedia() {
-        mProvider.onMetadataChanged(mock(MediaMetadata.class));
+        mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
         reset(mContentResolver);
         // Show media when dozing
         mProvider.onDozingChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 58c9311..afb63ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.util.NotificationChannels;
 
 import org.junit.Before;
@@ -57,7 +58,8 @@
     public void setUp() throws Exception {
         // Test Instance.
         mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
-        mPowerNotificationWarnings = new PowerNotificationWarnings(mContext);
+        ActivityStarter starter = mDependency.injectMockDependency(ActivityStarter.class);
+        mPowerNotificationWarnings = new PowerNotificationWarnings(mContext, starter);
         BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
                 BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
         mPowerNotificationWarnings.updateSnapshot(snapshot);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 6033ed2..e2e0bb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -24,13 +24,14 @@
 import android.os.Handler
 import android.os.UserHandle
 import android.os.UserManager
-import androidx.test.filters.SmallTest
+import android.provider.DeviceConfig
+import android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
 import com.android.systemui.Dependency
-import com.android.systemui.Dependency.BG_HANDLER
-import com.android.systemui.Dependency.MAIN_HANDLER
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.appops.AppOpItem
@@ -38,6 +39,7 @@
 import org.hamcrest.Matchers.hasItem
 import org.hamcrest.Matchers.not
 import org.hamcrest.Matchers.nullValue
+import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertThat
 import org.junit.Assert.assertTrue
@@ -106,6 +108,9 @@
         mContext.addMockSystemService(UserManager::class.java, userManager)
         mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
                 DEVICE_SERVICES_STRING)
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+                "true", false)
 
         doReturn(listOf(object : UserInfo() {
             init {
@@ -116,9 +121,15 @@
         privacyItemController = PrivacyItemController(mContext)
     }
 
+    @After
+    fun tearDown() {
+        DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_PRIVACY)
+    }
+
     @Test
     fun testSetListeningTrueByAddingCallback() {
         privacyItemController.addCallback(callback)
+        testableLooper.processAllMessages()
         verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
                 any(AppOpsController.Callback::class.java))
         testableLooper.processAllMessages()
@@ -126,18 +137,16 @@
     }
 
     @Test
-    fun testSetListeningTrue() {
-        privacyItemController.setListening(true)
-        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+    fun testSetListeningFalseByRemovingLastCallback() {
+        privacyItemController.addCallback(callback)
+        testableLooper.processAllMessages()
+        verify(appOpsController, never()).removeCallback(any(IntArray::class.java),
                 any(AppOpsController.Callback::class.java))
-    }
-
-    @Test
-    fun testSetListeningFalse() {
-        privacyItemController.setListening(true)
-        privacyItemController.setListening(false)
+        privacyItemController.removeCallback(callback)
+        testableLooper.processAllMessages()
         verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
                 any(AppOpsController.Callback::class.java))
+        verify(callback).privacyChanged(emptyList())
     }
 
     @Test
@@ -168,7 +177,8 @@
     fun testRegisterReceiver_allUsers() {
         val spiedContext = spy(mContext)
         val itemController = PrivacyItemController(spiedContext)
-        itemController.setListening(true)
+        itemController.addCallback(callback)
+        testableLooper.processAllMessages()
         verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
                 eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
                 eq(null))
@@ -268,4 +278,16 @@
         assertEquals(list, privacyList)
         assertTrue(list !== privacyList)
     }
+
+    @Test
+    fun testNotListeningWhenIndicatorsDisabled() {
+        privacyItemController.devicePropertyChangedListener.onPropertyChanged(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
+                "false")
+        privacyItemController.addCallback(callback)
+        testableLooper.processAllMessages()
+        verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
+                any(AppOpsController.Callback::class.java))
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
new file mode 100644
index 0000000..9c1cad6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.qs.tileimpl
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ResourceIconTest : SysuiTestCase() {
+
+    val resIds = (1..1000).toList()
+
+    @Test
+    fun testMultipleThreadedPut() {
+        resIds.parallelStream().forEach {
+            QSTileImpl.ResourceIcon.get(it)
+        }
+        resIds.forEach {
+            assertEquals(it, (QSTileImpl.ResourceIcon.get(it) as QSTileImpl.ResourceIcon).mResId)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index b049632..84a7d4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -358,4 +358,11 @@
         waitForIdleSync();
         verify(mCallbacks).onDisplayRemoved(eq(SECONDARY_DISPLAY));
     }
+
+    @Test
+    public void testOnRecentsAnimationStateChanged() {
+        mCommandQueue.onRecentsAnimationStateChanged(true);
+        waitForIdleSync();
+        verify(mCallbacks).onRecentsAnimationStateChanged(eq(true));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 67c4a92..b99958a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -38,6 +38,8 @@
 
 import com.android.systemui.ActivityStarterDelegate;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 import org.junit.Before;
@@ -57,17 +59,24 @@
 
     @Mock private NotificationStackScrollLayout mNssl;
     @Mock private ActivityStarterDelegate mActivityStarterDelegate;
+    @Mock private StatusBarStateController mStatusBarStateController;
 
     private NotificationSectionsManager mSectionsManager;
 
     @Before
     public void setUp() {
-        mSectionsManager = new NotificationSectionsManager(mNssl, mActivityStarterDelegate, true);
+        mSectionsManager =
+                new NotificationSectionsManager(
+                        mNssl,
+                        mActivityStarterDelegate,
+                        mStatusBarStateController,
+                        true);
         // Required in order for the header inflation to work properly
         when(mNssl.generateLayoutParams(any(AttributeSet.class)))
                 .thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
         mSectionsManager.inflateViews(mContext);
         when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
     }
 
     @Test
@@ -185,6 +194,49 @@
         verify(mNssl).addView(mSectionsManager.getGentleHeaderView(), 1);
     }
 
+    @Test
+    public void testHeaderNotShownOnLockscreen() {
+        // GIVEN a stack of HI and LO notifs on the lockscreen
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+
+        // WHEN we update the section headers
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the section header is not added
+        verify(mNssl, never()).addView(eq(mSectionsManager.getGentleHeaderView()), anyInt());
+    }
+
+    @Test
+    public void testHeaderShownWhenEnterLockscreen() {
+        // GIVEN a stack of HI and LO notifs on the lockscreen
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+        mSectionsManager.updateSectionBoundaries();
+
+        // WHEN we unlock
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the section header is added
+        verify(mNssl).addView(mSectionsManager.getGentleHeaderView(), 3);
+    }
+
+    @Test
+    public void testHeaderHiddenWhenEnterLockscreen() {
+        // GIVEN a stack of HI and LO notifs on the shade
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+        setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+        mSectionsManager.updateSectionBoundaries();
+
+        // WHEN we go back to the keyguard
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+        mSectionsManager.updateSectionBoundaries();
+
+        // Then the section header is removed
+        verify(mNssl).removeView(eq(mSectionsManager.getGentleHeaderView()));
+    }
+
     private enum ChildType { HEADER, HIPRI, LOPRI }
 
     private void setStackState(ChildType... children) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 09b8062..56265d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -153,7 +153,8 @@
                 true /* allowLongPress */, mNotificationRoundnessManager,
                 new AmbientPulseManager(mContext),
                 mock(DynamicPrivacyController.class),
-                mock(ActivityStarterDelegate.class));
+                mock(ActivityStarterDelegate.class),
+                mock(StatusBarStateController.class));
         mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
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 08471d8..2f9d892 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
@@ -119,25 +119,10 @@
         mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
         InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController);
         inOrder.verify(mNotificationStackScrollLayout).setDark(eq(true), eq(true), eq(null));
-        inOrder.verify(mNotificationStackScrollLayout).showDarkShelf();
         inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
     }
 
     @Test
-    public void testSetDozing_showsDarkShelfWithDefaultClock() {
-        when(mKeyguardStatusView.hasCustomClock()).thenReturn(false);
-        mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
-        verify(mNotificationStackScrollLayout).showDarkShelf();
-    }
-
-    @Test
-    public void testSetDozing_showsDarkShelfWhenCustomClock() {
-        when(mKeyguardStatusView.hasCustomClock()).thenReturn(true);
-        mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
-        verify(mNotificationStackScrollLayout).showDarkShelf();
-    }
-
-    @Test
     public void testSetExpandedHeight() {
         mNotificationPanelView.setExpandedHeight(200);
         assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index a5a4fec..2616466 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6530,68 +6530,78 @@
     // OS: Q
     SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557;
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // OPEN: Emergency dialer opened
     // CLOSE: Emergency dialer closed
     //  SUBTYPE: The entry type that user opened emergency dialer
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    EMERGENCY_DIALER = 1558;
+    EMERGENCY_DIALER = 1558 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // FIELD: The screen is currently locked
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    FIELD_EMERGENCY_DIALER_IS_SCREEN_LOCKED = 1559;
+    FIELD_EMERGENCY_DIALER_IS_SCREEN_LOCKED = 1559 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // FIELD: Bit flag indicating the actions performed by user
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    FIELD_EMERGENCY_DIALER_USER_ACTIONS = 1560;
+    FIELD_EMERGENCY_DIALER_USER_ACTIONS = 1560 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // FIELD: The duration user stayed at emergency dialer
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    FIELD_EMERGENCY_DIALER_DURATION_MS = 1561;
+    FIELD_EMERGENCY_DIALER_DURATION_MS = 1561 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // ACTION: Making call via emergency dialer
     //  SUBTYPE: The UI that user made phone call
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    EMERGENCY_DIALER_MAKE_CALL = 1562;
+    EMERGENCY_DIALER_MAKE_CALL = 1562 [deprecated=true];
 
     // FIELD: The phone number type of a call user made
-    // CATEGORY: EMERGENCY_DIALER
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2
     // OS: Q
     FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE = 1563;
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // FIELD: There is a shortcut for the phone number
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    FIELD_EMERGENCY_DIALER_PHONE_NUMBER_HAS_SHORTCUT = 1564;
+    FIELD_EMERGENCY_DIALER_PHONE_NUMBER_HAS_SHORTCUT = 1564 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // FIELD: The phone is in pocket while using emergency dialer
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    FIELD_EMERGENCY_DIALER_IN_POCKET = 1565;
+    FIELD_EMERGENCY_DIALER_IN_POCKET = 1565 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // ACTION: The second tap on emergency shortcut to make a phone call
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP = 1566;
+    EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP = 1566 [deprecated=true];
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // FIELD: The time in milliseconds of second tap on shortcut since first tap
     // CATEGORY: EMERGENCY_DIALER
     // OS: Q
-    FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL = 1567;
+    FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL = 1567 [deprecated=true];
 
     // OPEN: Power menu is opened
     // CATEGORY: GLOBAL_SYSTEM_UI
     // OS: Q
     POWER_MENU = 1568;
 
+    // DEPRECATED: The metrics for emergency dialer has been revised.
     // ACTION: User tapped emergency dialer icon in the power menu.
     // CATEGORY: GLOBAL_SYSTEM_UI
     // OS: Q
-    ACTION_EMERGENCY_DIALER_FROM_POWER_MENU = 1569;
+    ACTION_EMERGENCY_DIALER_FROM_POWER_MENU = 1569 [deprecated=true];
 
     // OPEN: Settings > System > Input & Gesture > Wake screen
     // OS: Q
@@ -7293,12 +7303,73 @@
     // ACTION: Settings > Initialize Search bar > Verify Slice > Other exception
     ACTION_VERIFY_SLICE_OTHER_EXCEPTION = 1727;
 
-    // ---- Skipping ahead to avoid conflicts between master and release branches.
-
     // OPEN: Settings > System > Gestures > Global Actions Panel
     // CATEGORY: SETTINGS
     // OS: Q
-    GLOBAL_ACTIONS_PANEL_SETTINGS = 1800;
+    GLOBAL_ACTIONS_PANEL_SETTINGS = 1728;
+
+    // ACTION: Making call via emergency dialer (Ver.2)
+    //  SUBTYPE: The UI that user made phone call
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2
+    // OS: Q
+    EMERGENCY_DIALER_MAKE_CALL_V2 = 1729;
+
+    // ACTION: Result of an emergency call
+    //  SUBTYPE: Is the call active, i.e. accepted by the remote
+    // CATEGORY: EMERGENCY_DIALER_CALL_RESULT
+    // OS: Q
+    EMERGENCY_DIALER_CALL_RESULT = 1730;
+
+    // FIELD: The entry point that the user launch emergency dialer from
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_LAUNCH_FROM = 1731;
+
+    // FIELD: The reason why the traditional emergency dialer shows up
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_UI_MODE_ERROR_CODE = 1732;
+
+    // FIELD: The device is currently locked
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_IS_DEVICE_LOCKED = 1733;
+
+    // FIELD: The country ISO code of current network
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2, EMERGENCY_DIALER_CALL_RESULT
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_NETWORK_COUNTRY_ISO = 1734;
+
+    // FIELD: The mobile operator code (MNC) of current network
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2, EMERGENCY_DIALER_CALL_RESULT
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_NETWORK_OPERATOR = 1735;
+
+    // FIELD: Radio version of the device
+    // CATEGORY: EMERGENCY_DIALER_MAKE_CALL_V2
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_RADIO_VERSION = 1736;
+
+    // FIELD: The duration between the start of dialing and the call is active.
+    // CATEGORY: EMERGENCY_DIALER_CALL_RESULT
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_RESPONSE_TIME = 1737;
+
+    // FIELD: The duration between the start of dialing and the call being disconnected.
+    // CATEGORY: EMERGENCY_DIALER_CALL_RESULT
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_DISCONNECT_TIME = 1738;
+
+    // FIELD: The cause of a disconnected call.
+    // CATEGORY: EMERGENCY_DIALER_CALL_RESULT
+    // OS: Q
+    FIELD_EMERGENCY_DIALER_DISCONNECT_CAUSE = 1739;
+
+    // OPEN: Settings > Display > Dark Theme
+    // CATEGORY: SETTINGS
+    // OS: Q
+    // Note: Only shows up on first time toggle
+    DIALOG_DARK_UI_INFO = 1740;
 
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
diff --git a/services/art-profile b/services/art-profile
index cf0f13c..a9d5982 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -13532,7 +13532,7 @@
 HSPLcom/android/server/uri/UriGrantsManagerService;->checkUriPermission(Lcom/android/server/uri/GrantUri;II)Z
 HSPLcom/android/server/uri/UriGrantsManagerService;->enforceNotIsolatedCaller(Ljava/lang/String;)V
 HSPLcom/android/server/uri/UriGrantsManagerService;->findOrCreateUriPermission(Ljava/lang/String;Ljava/lang/String;ILcom/android/server/uri/GrantUri;)Lcom/android/server/uri/UriPermission;
-PLcom/android/server/uri/UriGrantsManagerService;->getPersistedUriPermissions(Ljava/lang/String;Z)Landroid/content/pm/ParceledListSlice;
+PLcom/android/server/uri/UriGrantsManagerService;->getPersistedUriPermissions(Ljava/lang/String;ZZ)Landroid/content/pm/ParceledListSlice;
 HSPLcom/android/server/uri/UriGrantsManagerService;->getProviderInfo(Ljava/lang/String;II)Landroid/content/pm/ProviderInfo;
 HSPLcom/android/server/uri/UriGrantsManagerService;->grantUriPermission(ILjava/lang/String;Lcom/android/server/uri/GrantUri;ILcom/android/server/uri/UriPermissionOwner;I)V
 HSPLcom/android/server/uri/UriGrantsManagerService;->grantUriPermissionFromIntent(ILjava/lang/String;Landroid/content/Intent;Lcom/android/server/uri/UriPermissionOwner;I)V
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
new file mode 100644
index 0000000..dd1b84b
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.contentcapture;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
+import android.service.contentcapture.FlushMetrics;
+import android.util.StatsLog;
+
+import java.util.List;
+
+/** @hide */
+public final class ContentCaptureMetricsLogger {
+    /**
+     * Class only contains static utility functions, and should not be instantiated
+     */
+    private ContentCaptureMetricsLogger() {
+    }
+
+    /** @hide */
+    public static void writeServiceEvent(int eventType, @NonNull String serviceName,
+            @Nullable String targetPackage) {
+        StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, eventType, serviceName,
+                targetPackage);
+    }
+
+    /** @hide */
+    public static void writeServiceEvent(int eventType, @NonNull ComponentName service,
+            @Nullable ComponentName target) {
+        writeServiceEvent(eventType, ComponentName.flattenToShortString(service),
+                ComponentName.flattenToShortString(target));
+    }
+
+    /** @hide */
+    public static void writeServiceEvent(int eventType, @NonNull ComponentName service,
+            @Nullable String targetPackage) {
+        writeServiceEvent(eventType, ComponentName.flattenToShortString(service), targetPackage);
+    }
+
+    /** @hide */
+    public static void writeServiceEvent(int eventType, @NonNull ComponentName service) {
+        writeServiceEvent(eventType, ComponentName.flattenToShortString(service), null);
+    }
+
+    /** @hide */
+    public static void writeSetWhitelistEvent(@Nullable ComponentName service,
+            @Nullable List<String> packages, @Nullable List<ComponentName> activities) {
+        final String serviceName = ComponentName.flattenToShortString(service);
+        StringBuilder stringBuilder = new StringBuilder();
+        if (packages != null && packages.size() > 0) {
+            final int size = packages.size();
+            stringBuilder.append(packages.get(0));
+            for (int i = 1; i < size; i++) {
+                stringBuilder.append(" ");
+                stringBuilder.append(packages.get(i));
+            }
+        }
+        if (activities != null && activities.size() > 0) {
+            stringBuilder.append(" ");
+            stringBuilder.append(activities.get(0).flattenToShortString());
+            final int size = activities.size();
+            for (int i = 1; i < size; i++) {
+                stringBuilder.append(" ");
+                stringBuilder.append(activities.get(i).flattenToShortString());
+            }
+        }
+        StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS,
+                StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_WHITELIST,
+                serviceName, stringBuilder.toString());
+    }
+
+    /** @hide */
+    public static void writeSessionEvent(int sessionId, int event, int flags,
+            @NonNull ComponentName service, @Nullable ComponentName app, boolean isChildSession) {
+        StatsLog.write(StatsLog.CONTENT_CAPTURE_SESSION_EVENTS, sessionId, event, flags,
+                ComponentName.flattenToShortString(service),
+                ComponentName.flattenToShortString(app), isChildSession);
+    }
+
+    /** @hide */
+    public static void writeSessionFlush(int sessionId, @NonNull ComponentName service,
+            @Nullable ComponentName app, @NonNull FlushMetrics fm,
+            @NonNull ContentCaptureOptions options, int flushReason) {
+        StatsLog.write(StatsLog.CONTENT_CAPTURE_FLUSHED, sessionId,
+                ComponentName.flattenToShortString(service),
+                ComponentName.flattenToShortString(app), fm.sessionStarted, fm.sessionFinished,
+                fm.viewAppearedCount, fm.viewDisappearedCount, fm.viewTextChangedCount,
+                options.maxBufferSize, options.idleFlushingFrequencyMs,
+                options.textChangeFlushingFrequencyMs, flushReason);
+    }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 67c3d01..a186d4e 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -24,6 +24,9 @@
 import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED;
 import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
 
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSetWhitelistEvent;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -35,6 +38,7 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
 import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -48,6 +52,7 @@
 import android.service.contentcapture.ActivityEvent.ActivityEventType;
 import android.service.contentcapture.ContentCaptureService;
 import android.service.contentcapture.ContentCaptureServiceInfo;
+import android.service.contentcapture.FlushMetrics;
 import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.util.ArrayMap;
@@ -55,6 +60,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.StatsLog;
 import android.view.contentcapture.ContentCaptureCondition;
 import android.view.contentcapture.DataRemovalRequest;
 
@@ -231,7 +237,6 @@
         resurrectSessionsLocked();
     }
 
-    // TODO(b/119613670): log metrics
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
             @NonNull ActivityPresentationInfo activityPresentationInfo, int sessionId, int uid,
@@ -263,9 +268,14 @@
 
         if (!enabled) {
             // TODO: it would be better to split in differet reasons, like
-            // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
+            // STATE_DISABLED_NO and STATE_DISABLED_BY_DEVICE_POLICY
             setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
                     /* binder= */ null);
+            // Log metrics.
+            writeSessionEvent(sessionId,
+                    StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+                    STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
+                    componentName, /* isChildSession= */ false);
             return;
         }
         if (serviceComponentName == null) {
@@ -285,6 +295,11 @@
             }
             setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED,
                     /* binder= */ null);
+            // Log metrics.
+            writeSessionEvent(sessionId,
+                    StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+                    STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName,
+                    componentName, /* isChildSession= */ false);
             return;
         }
 
@@ -294,6 +309,11 @@
                     + ": ignoring because it already exists for " + existingSession.mActivityToken);
             setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
                     /* binder=*/ null);
+            // Log metrics.
+            writeSessionEvent(sessionId,
+                    StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+                    STATE_DISABLED | STATE_DUPLICATED_ID,
+                    serviceComponentName, componentName, /* isChildSession= */ false);
             return;
         }
 
@@ -302,11 +322,15 @@
         }
 
         if (mRemoteService == null) {
-            // TODO(b/119613670): log metrics
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                     + ": ignoring because service is not set");
             setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
                     /* binder= */ null);
+            // Log metrics.
+            writeSessionEvent(sessionId,
+                    StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+                    STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
+                    componentName, /* isChildSession= */ false);
             return;
         }
 
@@ -324,7 +348,6 @@
         newSession.notifySessionStartedLocked(clientReceiver);
     }
 
-    // TODO(b/119613670): log metrics
     @GuardedBy("mLock")
     public void finishSessionLocked(int sessionId) {
         if (!isEnabledLocked()) {
@@ -553,6 +576,7 @@
                         + " for user " + mUserId);
             }
             mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
+            writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
 
             // Must disable session that are not the whitelist anymore...
             final int numSessions = mSessions.size();
@@ -602,7 +626,6 @@
                     mConditionsByPkg.put(packageName, new ArraySet<>(conditions));
                 }
             }
-            // TODO(b/119613670): log metrics
         }
 
         @Override
@@ -616,6 +639,15 @@
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
+            writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED,
+                    getServiceComponentName());
+        }
+
+        @Override
+        public void writeSessionFlush(int sessionId, ComponentName app, FlushMetrics flushMetrics,
+                ContentCaptureOptions options, int flushReason) {
+            ContentCaptureMetricsLogger.writeSessionFlush(sessionId, getServiceComponentName(), app,
+                    flushMetrics, options, flushReason);
         }
     }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 2171033..18daf32 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -18,6 +18,9 @@
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeServiceEvent;
+import static com.android.server.contentcapture.ContentCaptureMetricsLogger.writeSessionEvent;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -28,6 +31,7 @@
 import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.DataRemovalRequest;
 
@@ -77,6 +81,8 @@
             if (connected) {
                 try {
                     mService.onConnected(mServerCallback, sVerbose, sDebug);
+                    writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
+                            mComponentName);
                 } finally {
                     // Update the system-service state, in case the service reconnected after
                     // dying
@@ -84,6 +90,8 @@
                 }
             } else {
                 mService.onDisconnected();
+                writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
+                        mComponentName);
             }
         } catch (Exception e) {
             Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
@@ -102,6 +110,10 @@
             @NonNull IResultReceiver clientReceiver, int initialState) {
         scheduleAsyncRequest(
                 (s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver, initialState));
+        // Metrics logging.
+        writeSessionEvent(sessionId,
+                StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_STARTED, initialState,
+                getComponentName(), context.getActivityComponent(), /* is_child_session= */ false);
     }
 
     /**
@@ -110,6 +122,11 @@
      */
     public void onSessionFinished(int sessionId) {
         scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
+        // Metrics logging.
+        writeSessionEvent(sessionId,
+                StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_FINISHED,
+                /* flags= */ 0, getComponentName(), /* app= */ null,
+                /* is_child_session= */ false);
     }
 
     /**
@@ -124,6 +141,8 @@
      */
     public void onDataRemovalRequest(@NonNull DataRemovalRequest request) {
         scheduleAsyncRequest((s) -> s.onDataRemovalRequest(request));
+        writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_USER_DATA_REMOVED,
+                mComponentName);
     }
 
     /**
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 72899f6..cb61259 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1138,8 +1138,8 @@
                     ? clampPositive(whenElapsed + a.windowLength)
                     : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
         }
-        a.whenElapsed = whenElapsed;
-        a.maxWhenElapsed = maxElapsed;
+        a.expectedWhenElapsed = a.whenElapsed = whenElapsed;
+        a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed;
         setImplLocked(a, true, doValidate);
     }
 
@@ -1243,7 +1243,7 @@
                 alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
                 // Also schedule its next recurrence
                 final long delta = alarm.count * alarm.repeatInterval;
-                final long nextElapsed = alarm.whenElapsed + delta;
+                final long nextElapsed = alarm.expectedWhenElapsed + delta;
                 setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                         maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                         alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
@@ -3596,10 +3596,9 @@
                     // this adjustment will be zero if we're late by
                     // less than one full repeat interval
                     alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
-
                     // Also schedule its next recurrence
                     final long delta = alarm.count * alarm.repeatInterval;
-                    final long nextElapsed = alarm.whenElapsed + delta;
+                    final long nextElapsed = alarm.expectedWhenElapsed + delta;
                     setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                             maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
                             alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 9d979a6..f92d0e0 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -152,7 +152,18 @@
 
     @Override
     public boolean isInUse() throws RemoteException {
-        return getGsiService().isGsiRunning();
+        boolean gsidWasRunning = "running".equals(SystemProperties.get("init.svc.gsid"));
+        boolean isInUse = false;
+
+        try {
+            isInUse = getGsiService().isGsiRunning();
+        } finally {
+            if (!gsidWasRunning && !isInUse) {
+                SystemProperties.set("ctl.stop", "gsid");
+            }
+        }
+
+        return isInUse;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index fc355b7..bf56bc0 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -96,6 +96,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.os.storage.DiskInfo;
 import android.os.storage.IObbActionListener;
 import android.os.storage.IStorageEventListener;
@@ -3242,7 +3243,22 @@
         public void opChanged(int op, int uid, String packageName) throws RemoteException {
             if (!ENABLE_ISOLATED_STORAGE) return;
 
-            remountUidExternalStorage(uid, getMountMode(uid, packageName));
+            if (op == OP_REQUEST_INSTALL_PACKAGES) {
+                // Only handling the case when the appop is denied. The other cases will be
+                // handled in the synchronous callback from AppOpsService.
+                if (packageName != null && mIAppOpsService.checkOperation(
+                        OP_REQUEST_INSTALL_PACKAGES, uid, packageName) != MODE_ALLOWED) {
+                    try {
+                        ActivityManager.getService().killUid(
+                                UserHandle.getAppId(uid), UserHandle.getUserId(uid),
+                                "OP_REQUEST_INSTALL_PACKAGES is denied");
+                    } catch (RemoteException e) {
+                        // same process - should not happen
+                    }
+                }
+            } else {
+                remountUidExternalStorage(uid, getMountMode(uid, packageName));
+            }
         }
     };
 
@@ -3579,8 +3595,14 @@
             if (Process.isIsolated(uid)) {
                 return Zygote.MOUNT_EXTERNAL_NONE;
             }
+
+            final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid);
+            if (packageName == null) {
+                packageName = packagesForUid[0];
+            }
+
             if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) {
-                return Zygote.MOUNT_EXTERNAL_DEFAULT;
+                return Zygote.MOUNT_EXTERNAL_NONE;
             }
 
             // Determine if caller is holding runtime permission
@@ -3601,8 +3623,18 @@
             // runtime permission; this is a firm CDD requirement
             final boolean hasInstall = mIPackageManager.checkUidPermission(INSTALL_PACKAGES,
                     uid) == PERMISSION_GRANTED;
-            final boolean hasInstallOp = mIAppOpsService.checkOperation(OP_REQUEST_INSTALL_PACKAGES,
-                    uid, packageName) == MODE_ALLOWED;
+            boolean hasInstallOp = false;
+            // OP_REQUEST_INSTALL_PACKAGES is granted/denied per package but vold can't
+            // update mountpoints of a specific package. So, check the appop for all packages
+            // sharing the uid and allow same level of storage access for all packages even if
+            // one of the packages has the appop granted.
+            for (String uidPackageName : packagesForUid) {
+                if (mIAppOpsService.checkOperation(
+                        OP_REQUEST_INSTALL_PACKAGES, uid, uidPackageName) == MODE_ALLOWED) {
+                    hasInstallOp = true;
+                    break;
+                }
+            }
             if ((hasInstall || hasInstallOp) && hasWrite) {
                 return Zygote.MOUNT_EXTERNAL_WRITE;
             }
@@ -3870,6 +3902,14 @@
             if (ENABLE_ISOLATED_STORAGE) {
                 return getMountMode(uid, packageName);
             }
+            try {
+                if (packageName == null) {
+                    final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid);
+                    packageName = packagesForUid[0];
+                }
+            } catch (RemoteException e) {
+                // Should not happen - same process
+            }
             // No locking - CopyOnWriteArrayList
             int mountMode = Integer.MAX_VALUE;
             for (ExternalStorageMountPolicy policy : mPolicies) {
@@ -3918,5 +3958,23 @@
             }
             return true;
         }
+
+        public void onAppOpsChanged(int code, int uid,
+                @Nullable String packageName, int mode) {
+            if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE
+                    || code == OP_WRITE_EXTERNAL_STORAGE
+                    || code == OP_REQUEST_INSTALL_PACKAGES)) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    final UserManagerInternal userManagerInternal =
+                            LocalServices.getService(UserManagerInternal.class);
+                    if (userManagerInternal.isUserInitialized(UserHandle.getUserId(uid))) {
+                        onExternalStoragePolicyChanged(uid, packageName);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 1ce65561..ff6a537 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -45,7 +45,8 @@
 
     private static SystemServerInitThreadPool sInstance;
 
-    private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(4,
+    private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(
+            Runtime.getRuntime().availableProcessors(),
             "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
 
     private List<String> mPendingTasks = new ArrayList<>();
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index a7fb99f..e097d85 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -617,9 +617,13 @@
             // deadlock and the watchdog as a whole to be ineffective)
             Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                     public void run() {
-                        mActivity.addErrorToDropBox(
-                                "watchdog", null, "system_server", null, null, null,
-                                subject, null, stack, null);
+                        // If a watched thread hangs before init() is called, we don't have a
+                        // valid mActivity. So we can't log the error to dropbox.
+                        if (mActivity != null) {
+                            mActivity.addErrorToDropBox(
+                                    "watchdog", null, "system_server", null, null, null,
+                                    subject, null, stack, null);
+                        }
                         StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
                     }
                 };
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d8f5937..a809d21 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -33,6 +33,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_PROVIDERS;
+import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -735,11 +736,11 @@
          * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
          * method.
          */
-        void put(int key, ProcessRecord value) {
+        void put(ProcessRecord app) {
             synchronized (this) {
-                mPidMap.put(key, value);
+                mPidMap.put(app.pid, app);
             }
-            mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
+            mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController());
         }
 
         /**
@@ -747,11 +748,18 @@
          * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
          * method.
          */
-        void remove(int pid) {
+        void remove(ProcessRecord app) {
+            boolean removed = false;
             synchronized (this) {
-                mPidMap.remove(pid);
+                final ProcessRecord existingApp = mPidMap.get(app.pid);
+                if (existingApp != null && existingApp.startSeq == app.startSeq) {
+                    mPidMap.remove(app.pid);
+                    removed = true;
+                }
             }
-            mAtmInternal.onProcessUnMapped(pid);
+            if (removed) {
+                mAtmInternal.onProcessUnMapped(app.pid);
+            }
         }
 
         /**
@@ -759,17 +767,18 @@
          * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
          * method.
          */
-        boolean removeIfNoThread(int pid) {
+        boolean removeIfNoThread(ProcessRecord app) {
             boolean removed = false;
             synchronized (this) {
-                final ProcessRecord app = get(pid);
-                if (app != null && app.thread == null) {
-                    mPidMap.remove(pid);
+                final ProcessRecord existingApp = get(app.pid);
+                if (existingApp != null && existingApp.startSeq == app.startSeq
+                        && app.thread == null) {
+                    mPidMap.remove(app.pid);
                     removed = true;
                 }
             }
             if (removed) {
-                mAtmInternal.onProcessUnMapped(pid);
+                mAtmInternal.onProcessUnMapped(app.pid);
             }
             return removed;
         }
@@ -1970,7 +1979,7 @@
                 app.getWindowProcessController().setPid(MY_PID);
                 app.maxAdj = ProcessList.SYSTEM_ADJ;
                 app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
-                mPidsSelfLocked.put(app.pid, app);
+                mPidsSelfLocked.put(app);
                 mProcessList.updateLruProcessLocked(app, false, null);
                 updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
             }
@@ -4601,7 +4610,7 @@
     @GuardedBy("this")
     private final void processStartTimedOutLocked(ProcessRecord app) {
         final int pid = app.pid;
-        boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
+        boolean gone = mPidsSelfLocked.removeIfNoThread(app);
 
         if (gone) {
             Slog.w(TAG, "Process " + app + " failed to attach");
@@ -4658,6 +4667,26 @@
             synchronized (mPidsSelfLocked) {
                 app = mPidsSelfLocked.get(pid);
             }
+            if (app != null && (app.startUid != callingUid || app.startSeq != startSeq)) {
+                String processName = null;
+                final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
+                if (pending != null) {
+                    processName = pending.processName;
+                }
+                final String msg = "attachApplicationLocked process:" + processName
+                        + " startSeq:" + startSeq
+                        + " pid:" + pid
+                        + " belongs to another existing app:" + app.processName
+                        + " startSeq:" + app.startSeq;
+                Slog.wtf(TAG, msg);
+                // SafetyNet logging for b/131105245.
+                EventLog.writeEvent(0x534e4554, "131105245", app.startUid, msg);
+                // If there is already an app occupying that pid that hasn't been cleaned up
+                cleanUpApplicationRecordLocked(app, false, false, -1,
+                            true /*replacingPid*/);
+                mPidsSelfLocked.remove(app);
+                app = null;
+            }
         } else {
             app = null;
         }
@@ -4666,7 +4695,7 @@
         // update the internal state.
         if (app == null && startSeq > 0) {
             final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
-            if (pending != null && pending.startUid == callingUid
+            if (pending != null && pending.startUid == callingUid && pending.startSeq == startSeq
                     && mProcessList.handleProcessStartedLocked(pending, pid, pending
                             .isUsingWrapper(),
                             startSeq, true)) {
@@ -13642,7 +13671,7 @@
             return true;
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
-            mPidsSelfLocked.remove(app.pid);
+            mPidsSelfLocked.remove(app);
             mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             if (app.isolated) {
@@ -18538,6 +18567,21 @@
         }
     }
 
+    /**
+     * Synchronously update the system ActivityThread, bypassing any deferred threading so any
+     * resources and overlaid values are available immediately.
+     */
+    public void updateSystemUiContext() {
+        PackageManagerInternal packageManagerInternal;
+        synchronized (this) {
+            packageManagerInternal = getPackageManagerInternalLocked();
+        }
+
+        ApplicationInfo ai = packageManagerInternal.getApplicationInfo("android",
+                GET_SHARED_LIBRARY_FILES, Binder.getCallingUid(), UserHandle.USER_SYSTEM);
+        ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
+    }
+
     void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
         final boolean updateFrameworkRes = packagesToUpdate.contains("android");
         if (updateFrameworkRes) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f86ba27..e2b59b4 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -467,23 +467,25 @@
         }
     }
 
-    public void noteJobStart(String name, int uid) {
+    /** A scheduled job was started. */
+    public void noteJobStart(String name, int uid, int standbyBucket, int jobid) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteJobStartLocked(name, uid);
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uid, null,
                     name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
-                    JobProtoEnums.STOP_REASON_UNKNOWN);
+                    JobProtoEnums.STOP_REASON_UNKNOWN, standbyBucket, jobid);
         }
     }
 
-    public void noteJobFinish(String name, int uid, int stopReason) {
+    /** A scheduled job was finished. */
+    public void noteJobFinish(String name, int uid, int stopReason, int standbyBucket, int jobid) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteJobFinishLocked(name, uid, stopReason);
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uid, null,
                     name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
-                    stopReason);
+                    stopReason, standbyBucket, jobid);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b394eea..dc94c1e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1445,10 +1445,11 @@
         long startTime = SystemClock.elapsedRealtime();
         if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
             checkSlow(startTime, "startProcess: removing from pids map");
-            mService.mPidsSelfLocked.remove(app.pid);
+            mService.mPidsSelfLocked.remove(app);
             mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             checkSlow(startTime, "startProcess: done removing from pids map");
             app.setPid(0);
+            app.startSeq = 0;
         }
 
         if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
@@ -1656,6 +1657,14 @@
         app.killedByAm = false;
         app.removed = false;
         app.killed = false;
+        if (app.startSeq != 0) {
+            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+                    + " with non-zero startSeq:" + app.startSeq);
+        }
+        if (app.pid != 0) {
+            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+                    + " with non-zero pid:" + app.pid);
+        }
         final long startSeq = app.startSeq = ++mProcStartSeqCounter;
         app.setStartParams(uid, hostingRecord, seInfo, startTime);
         app.setUsingWrapper(invokeWith != null
@@ -2063,12 +2072,15 @@
         // If there is already an app occupying that pid that hasn't been cleaned up
         if (oldApp != null && !app.isolated) {
             // Clean up anything relating to this pid first
-            Slog.w(TAG, "Reusing pid " + pid
-                    + " while app is still mapped to it");
+            Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
+                    + " startSeq:" + app.startSeq
+                    + " pid:" + pid
+                    + " belongs to another existing app:" + oldApp.processName
+                    + " startSeq:" + oldApp.startSeq);
             mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
                     true /*replacingPid*/);
         }
-        mService.mPidsSelfLocked.put(pid, app);
+        mService.mPidsSelfLocked.put(app);
         synchronized (mService.mPidsSelfLocked) {
             if (!procAttached) {
                 Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
@@ -2241,7 +2253,7 @@
                 .pendingStart)) {
             int pid = app.pid;
             if (pid > 0) {
-                mService.mPidsSelfLocked.remove(pid);
+                mService.mPidsSelfLocked.remove(app);
                 mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
                 mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
                 if (app.isolated) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 448e773..0a76845 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1314,6 +1314,7 @@
         }
 
         if (callbackSpecs == null) {
+            notifyOpChangedSync(code, uid, null, mode);
             return;
         }
 
@@ -1335,6 +1336,16 @@
                 }
             }
         }
+
+        notifyOpChangedSync(code, uid, null, mode);
+    }
+
+    private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) {
+        final StorageManagerInternal storageManagerInternal =
+                LocalServices.getService(StorageManagerInternal.class);
+        if (storageManagerInternal != null) {
+            storageManagerInternal.onAppOpsChanged(code, uid, packageName, mode);
+        }
     }
 
     /**
@@ -1438,6 +1449,8 @@
                     AppOpsService::notifyOpChanged,
                     this, repCbs, code, uid, packageName));
         }
+
+        notifyOpChangedSync(code, uid, packageName, mode);
     }
 
     private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code,
@@ -3801,8 +3814,9 @@
             if (mActiveWatchers.size() > 0 && dumpMode < 0) {
                 needSep = true;
                 boolean printedHeader = false;
-                for (int i = 0; i < mActiveWatchers.size(); i++) {
-                    final SparseArray<ActiveCallback> activeWatchers = mActiveWatchers.valueAt(i);
+                for (int watcherNum = 0; watcherNum < mActiveWatchers.size(); watcherNum++) {
+                    final SparseArray<ActiveCallback> activeWatchers =
+                            mActiveWatchers.valueAt(watcherNum);
                     if (activeWatchers.size() <= 0) {
                         continue;
                     }
@@ -3820,16 +3834,16 @@
                     }
                     pw.print("    ");
                     pw.print(Integer.toHexString(System.identityHashCode(
-                            mActiveWatchers.keyAt(i))));
+                            mActiveWatchers.keyAt(watcherNum))));
                     pw.println(" ->");
                     pw.print("        [");
                     final int opCount = activeWatchers.size();
-                    for (i = 0; i < opCount; i++) {
-                        if (i > 0) {
+                    for (int opNum = 0; opNum < opCount; opNum++) {
+                        if (opNum > 0) {
                             pw.print(' ');
                         }
-                        pw.print(AppOpsManager.opToName(activeWatchers.keyAt(i)));
-                        if (i < opCount - 1) {
+                        pw.print(AppOpsManager.opToName(activeWatchers.keyAt(opNum)));
+                        if (opNum < opCount - 1) {
                             pw.print(',');
                         }
                     }
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index e148468..3b19d08 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -116,6 +116,14 @@
     }
 
     @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            mContext.registerReceiver(new ScreenStateReceiver(),
+                    new IntentFilter(Intent.ACTION_SCREEN_OFF));
+        }
+    }
+
+    @Override
     public void onStart() {
         publishBinderService(Context.ATTENTION_SERVICE, new BinderService());
         publishLocalService(AttentionManagerInternal.class, new LocalService());
@@ -135,10 +143,6 @@
     private boolean isServiceAvailable() {
         if (mComponentName == null) {
             mComponentName = resolveAttentionService(mContext);
-            if (mComponentName != null) {
-                mContext.registerReceiver(new ScreenStateReceiver(),
-                        new IntentFilter(Intent.ACTION_SCREEN_OFF));
-            }
         }
         return mComponentName != null;
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4cde931..15e8851 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1452,7 +1452,13 @@
                         Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
             }
             if (!TextUtils.isEmpty(assistantName)) {
-                packageName = ComponentName.unflattenFromString(assistantName).getPackageName();
+                ComponentName componentName = ComponentName.unflattenFromString(assistantName);
+                if (componentName == null) {
+                    Slog.w(TAG, "Invalid service name for "
+                            + Settings.Secure.VOICE_INTERACTION_SERVICE + ": " + assistantName);
+                    return;
+                }
+                packageName = componentName.getPackageName();
             }
         }
         if (!TextUtils.isEmpty(packageName)) {
@@ -6698,7 +6704,10 @@
             boolean isVolumeController, IMediaProjection projection) {
         AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
 
-        if (!isPolicyRegisterAllowed(policyConfig, projection)) {
+        if (!isPolicyRegisterAllowed(policyConfig,
+                                     isFocusPolicy || isTestFocusPolicy || hasFocusListener,
+                                     isVolumeController,
+                                     projection)) {
             Slog.w(TAG, "Permission denied to register audio policy for pid "
                     + Binder.getCallingPid() + " / uid " + Binder.getCallingUid()
                     + ", need MODIFY_AUDIO_ROUTING or MediaProjection that can project audio");
@@ -6739,42 +6748,71 @@
      * as those policy do not modify the audio routing.
      */
     private boolean isPolicyRegisterAllowed(AudioPolicyConfig policyConfig,
-             IMediaProjection projection) {
+                                            boolean hasFocusAccess,
+                                            boolean isVolumeController,
+                                            IMediaProjection projection) {
 
-        boolean isLoopbackRenderPolicy = policyConfig.getMixes().stream().allMatch(
-                mix -> mix.getRouteFlags() == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK));
+        boolean requireValidProjection = false;
+        boolean requireCaptureAudioOrMediaOutputPerm = false;
+        boolean requireModifyRouting = false;
 
-        if (isLoopbackRenderPolicy) {
-            boolean allowPrivilegedPlaybackCapture = policyConfig.getMixes().stream().anyMatch(
-                    mix -> mix.getRule().allowPrivilegedPlaybackCapture());
-            if (allowPrivilegedPlaybackCapture
-                    && !(hasPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
-                    || hasPermission(android.Manifest.permission.CAPTURE_MEDIA_OUTPUT))) {
-                // Opt-out can not be bypassed without a system permission
-                return false;
+        if (hasFocusAccess || isVolumeController) {
+            requireModifyRouting |= true;
+        } else if (policyConfig.getMixes().isEmpty()) {
+            // An empty policy could be used to lock the focus or add mixes later
+            requireModifyRouting |= true;
+        }
+        for (AudioMix mix : policyConfig.getMixes()) {
+            // If mix is requesting a privileged capture
+            if (mix.getRule().allowPrivilegedPlaybackCapture()) {
+                // then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
+                requireCaptureAudioOrMediaOutputPerm |= true;
+                // and its format must be low quality enough
+                String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat());
+                if (error != null) {
+                    Log.e(TAG, error);
+                    return false;
+                }
             }
 
-            if (canProjectAudio(projection)) {
-                // Policy that do not modify the audio routing only need an audio projection
-                return true;
+            // If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough
+            // otherwise MODIFY_AUDIO_ROUTING permission is required
+            if (mix.getRouteFlags() == mix.ROUTE_FLAG_LOOP_BACK_RENDER && projection != null) {
+                requireValidProjection |= true;
+            } else {
+                requireModifyRouting |= true;
             }
         }
 
-        boolean hasPermissionModifyAudioRouting =
-                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
-        if (hasPermissionModifyAudioRouting) {
-            return true;
+        if (requireCaptureAudioOrMediaOutputPerm
+                && !callerHasPermission(android.Manifest.permission.CAPTURE_MEDIA_OUTPUT)
+                && !callerHasPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)) {
+            Log.e(TAG, "Privileged audio capture requires CAPTURE_MEDIA_OUTPUT or "
+                      + "CAPTURE_AUDIO_OUTPUT system permission");
+            return false;
         }
-        return false;
+
+        if (requireValidProjection && !canProjectAudio(projection)) {
+            return false;
+        }
+
+        if (requireModifyRouting
+                && !callerHasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)) {
+            Log.e(TAG, "Can not capture audio without MODIFY_AUDIO_ROUTING");
+            return false;
+        }
+
+        return true;
     }
-    private boolean hasPermission(String permission) {
-        return PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(permission);
+
+    private boolean callerHasPermission(String permission) {
+        return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
 
     /** @return true if projection is a valid MediaProjection that can project audio. */
     private boolean canProjectAudio(IMediaProjection projection) {
         if (projection == null) {
+            Log.e(TAG, "MediaProjection is null");
             return false;
         }
 
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 7733d67..8de2595 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -42,6 +42,7 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
+import com.android.server.wm.WindowManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -90,8 +91,10 @@
 
     private ICameraService mCameraServiceRaw;
 
+    // Map of currently active camera IDs
     private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
     private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
+
     private final MetricsLogger mLogger = new MetricsLogger();
     private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
     private static final String NFC_SERVICE_BINDER_NAME = "nfc";
@@ -415,6 +418,24 @@
                     }
                     break;
                 case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
+                    // Check current active camera IDs to see if this package is already talking to
+                    // some camera
+                    boolean alreadyActivePackage = false;
+                    for (int i = 0; i < mActiveCameraUsage.size(); i++) {
+                        if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
+                            alreadyActivePackage = true;
+                            break;
+                        }
+                    }
+                    // If not already active, notify window manager about this new package using a
+                    // camera
+                    if (!alreadyActivePackage) {
+                        WindowManagerInternal wmi =
+                                LocalServices.getService(WindowManagerInternal.class);
+                        wmi.addNonHighRefreshRatePackage(clientName);
+                    }
+
+                    // Update activity events
                     CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel);
                     CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
                     if (oldEvent != null) {
@@ -426,13 +447,31 @@
                 case ICameraServiceProxy.CAMERA_STATE_IDLE:
                 case ICameraServiceProxy.CAMERA_STATE_CLOSED:
                     CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
-                    if (doneEvent != null) {
-                        doneEvent.markCompleted();
-                        mCameraUsageHistory.add(doneEvent);
-                        if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
-                            dumpUsageEvents();
+                    if (doneEvent == null) break;
+
+                    doneEvent.markCompleted();
+                    mCameraUsageHistory.add(doneEvent);
+                    if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
+                        dumpUsageEvents();
+                    }
+
+                    // Check current active camera IDs to see if this package is still talking to
+                    // some camera
+                    boolean stillActivePackage = false;
+                    for (int i = 0; i < mActiveCameraUsage.size(); i++) {
+                        if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) {
+                            stillActivePackage = true;
+                            break;
                         }
                     }
+                    // If not longer active, notify window manager about this package being done
+                    // with camera
+                    if (!stillActivePackage) {
+                        WindowManagerInternal wmi =
+                                LocalServices.getService(WindowManagerInternal.class);
+                        wmi.removeNonHighRefreshRatePackage(clientName);
+                    }
+
                     break;
             }
             boolean isEmpty = mActiveCameraUsage.isEmpty();
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index d28482e..af25ad5 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -16,11 +16,14 @@
 
 package com.android.server.clipboard;
 
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+
+import android.Manifest;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.IActivityManager;
 import android.app.IUriGrantsManager;
 import android.app.KeyguardManager;
 import android.app.UriGrantsManager;
@@ -145,6 +148,11 @@
 
 /**
  * Implementation of the clipboard for copy and paste.
+ * <p>
+ * Caution: exception for clipboard data and isInternalSysWindowAppWithWindowFocus, any of data
+ * is accessed by userId or uid should be in * the try segment between
+ * Binder.clearCallingIdentity and Binder.restoreCallingIdentity.
+ * </p>
  */
 public class ClipboardService extends SystemService {
 
@@ -152,7 +160,7 @@
     private static final boolean IS_EMULATOR =
         SystemProperties.getBoolean("ro.kernel.qemu", false);
 
-    private final IActivityManager mAm;
+    private final ActivityManagerInternal mAmInternal;
     private final IUriGrantsManager mUgm;
     private final UriGrantsManagerInternal mUgmInternal;
     private final WindowManagerInternal mWm;
@@ -173,7 +181,7 @@
     public ClipboardService(Context context) {
         super(context);
 
-        mAm = ActivityManager.getService();
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
         mUgm = UriGrantsManager.getService();
         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mWm = LocalServices.getService(WindowManagerInternal.class);
@@ -244,6 +252,87 @@
         }
     }
 
+    /**
+     * To check if the application has granted the INTERNAL_SYSTEM_WINDOW permission and window
+     * focus.
+     * <p>
+     * All of applications granted INTERNAL_SYSTEM_WINDOW has the risk to leak clip information to
+     * the other user because INTERNAL_SYSTEM_WINDOW is signature level. i.e. platform key. Because
+     * some of applications have both of INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL at
+     * the same time, that means they show the same window to all of users.
+     * </p><p>
+     * Unfortunately, all of applications with INTERNAL_SYSTEM_WINDOW starts very early and then
+     * the real window show is belong to user 0 rather user X. The result of
+     * WindowManager.isUidFocused checking user X window is false.
+     * </p>
+     * @return true if the app granted INTERNAL_SYSTEM_WINDOW permission.
+     */
+    private boolean isInternalSysWindowAppWithWindowFocus(String callingPackage) {
+        // Shell can access the clipboard for testing purposes.
+        if (mPm.checkPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW,
+                    callingPackage) == PackageManager.PERMISSION_GRANTED) {
+            if (mWm.isUidFocused(Binder.getCallingUid())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * To get the validate current userId.
+     * <p>
+     * The intending userId needs to be validated by ActivityManagerInternal.handleIncomingUser.
+     * To check if the uid of the process have the permission to run as the userId.
+     * e.x. INTERACT_ACROSS_USERS_FULL or INTERACT_ACROSS_USERS permission granted.
+     * </p>
+     * <p>
+     * The application with the granted INTERNAL_SYSTEM_WINDOW permission should run as the output
+     * of ActivityManagerInternal.handleIncomingUser rather the userId of Binder.getCAllingUid().
+     * To use the userId of Binder.getCallingUid() is the root cause that leaks the information
+     * comes from user 0 to user X.
+     * </p>
+     *
+     * @param packageName the package name of the calling side
+     * @param userId the userId passed by the calling side
+     * @return return the intending userId that has been validated by ActivityManagerInternal.
+     */
+    @UserIdInt
+    private int getIntendingUserId(String packageName, @UserIdInt int userId) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (!UserManager.supportsMultipleUsers() || callingUserId == userId) {
+            return callingUserId;
+        }
+
+        int intendingUserId = callingUserId;
+        intendingUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false /* allow all */, ALLOW_FULL_ONLY,
+                "checkClipboardServiceCallingUser", packageName);
+
+        return intendingUserId;
+    }
+
+    /**
+     * To get the current running uid who is intend to run as.
+     * In ording to distinguish the nameing and reducing the confusing names, the client client
+     * side pass userId that is intend to run as,
+     * @return return IntentingUid = validated intenting userId +
+     *         UserHandle.getAppId(Binder.getCallingUid())
+     */
+    private int getIntendingUid(String packageName, @UserIdInt int userId) {
+        return UserHandle.getUid(getIntendingUserId(packageName, userId),
+                UserHandle.getAppId(Binder.getCallingUid()));
+    }
+
+    /**
+     * To handle the difference between userId and intendingUserId, uid and intendingUid.
+     *
+     * userId means that comes from the calling side and should be validated by
+     * ActivityManagerInternal.handleIncomingUser.
+     * After validation of ActivityManagerInternal.handleIncomingUser, the userId is called
+     * 'intendingUserId' and the uid is called 'intendingUid'.
+     */
     private class ClipboardImpl extends IClipboard.Stub {
         @Override
         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
@@ -260,92 +349,112 @@
         }
 
         @Override
-        public void setPrimaryClip(ClipData clip, String callingPackage) {
+        public void setPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId) {
             synchronized (this) {
                 if (clip == null || clip.getItemCount() <= 0) {
                     throw new IllegalArgumentException("No items");
                 }
-                final int callingUid = Binder.getCallingUid();
+                final int intendingUid = getIntendingUid(callingPackage, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
                 if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
-                            callingUid)) {
+                            intendingUid, intendingUserId)) {
                     return;
                 }
-                checkDataOwnerLocked(clip, callingUid);
-                setPrimaryClipInternal(clip, callingUid);
+                checkDataOwnerLocked(clip, intendingUid);
+                setPrimaryClipInternal(clip, intendingUid);
             }
         }
 
         @Override
-        public void clearPrimaryClip(String callingPackage) {
+        public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {
             synchronized (this) {
-                final int callingUid = Binder.getCallingUid();
+                final int intendingUid = getIntendingUid(callingPackage, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
                 if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
-                        callingUid)) {
+                        intendingUid, intendingUserId)) {
                     return;
                 }
-                setPrimaryClipInternal(null, callingUid);
+                setPrimaryClipInternal(null, intendingUid);
             }
         }
 
         @Override
-        public ClipData getPrimaryClip(String pkg) {
+        public ClipData getPrimaryClip(String pkg, @UserIdInt int userId) {
             synchronized (this) {
+                final int intendingUid = getIntendingUid(pkg, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
                 if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg,
-                            Binder.getCallingUid()) || isDeviceLocked()) {
+                        intendingUid, intendingUserId)
+                        || isDeviceLocked(intendingUserId)) {
                     return null;
                 }
-                addActiveOwnerLocked(Binder.getCallingUid(), pkg);
-                return getClipboard().primaryClip;
+                addActiveOwnerLocked(intendingUid, pkg);
+                return getClipboard(intendingUserId).primaryClip;
             }
         }
 
         @Override
-        public ClipDescription getPrimaryClipDescription(String callingPackage) {
+        public ClipDescription getPrimaryClipDescription(String callingPackage,
+                @UserIdInt int userId) {
             synchronized (this) {
+                final int intendingUid = getIntendingUid(callingPackage, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
                 if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
-                            Binder.getCallingUid()) || isDeviceLocked()) {
+                        intendingUid, intendingUserId)
+                        || isDeviceLocked(intendingUserId)) {
                     return null;
                 }
-                PerUserClipboard clipboard = getClipboard();
-                return clipboard.primaryClip != null ? clipboard.primaryClip.getDescription() : null;
+                PerUserClipboard clipboard = getClipboard(intendingUserId);
+                return clipboard.primaryClip != null
+                        ? clipboard.primaryClip.getDescription() : null;
             }
         }
 
         @Override
-        public boolean hasPrimaryClip(String callingPackage) {
+        public boolean hasPrimaryClip(String callingPackage, @UserIdInt int userId) {
             synchronized (this) {
+                final int intendingUid = getIntendingUid(callingPackage, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
                 if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
-                            Binder.getCallingUid()) || isDeviceLocked()) {
+                        intendingUid, intendingUserId)
+                        || isDeviceLocked(intendingUserId)) {
                     return false;
                 }
-                return getClipboard().primaryClip != null;
+                return getClipboard(intendingUserId).primaryClip != null;
             }
         }
 
         @Override
         public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
-                String callingPackage) {
+                String callingPackage, @UserIdInt int userId) {
             synchronized (this) {
-                getClipboard().primaryClipListeners.register(listener,
-                        new ListenerInfo(Binder.getCallingUid(), callingPackage));
+                final int intendingUid = getIntendingUid(callingPackage, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
+                getClipboard(intendingUserId).primaryClipListeners.register(listener,
+                        new ListenerInfo(intendingUid, callingPackage));
             }
         }
 
         @Override
-        public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+        public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
+                String callingPackage, @UserIdInt int userId) {
             synchronized (this) {
-                getClipboard().primaryClipListeners.unregister(listener);
+                final int intendingUserId = getIntendingUserId(callingPackage, userId);
+                getClipboard(intendingUserId).primaryClipListeners.unregister(listener);
             }
         }
 
         @Override
-        public boolean hasClipboardText(String callingPackage) {
+        public boolean hasClipboardText(String callingPackage, int userId) {
             synchronized (this) {
+                final int intendingUid = getIntendingUid(callingPackage, userId);
+                final int intendingUserId = UserHandle.getUserId(intendingUid);
                 if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
-                            Binder.getCallingUid()) || isDeviceLocked()) {
+                        intendingUid, intendingUserId)
+                        || isDeviceLocked(intendingUserId)) {
                     return false;
                 }
-                PerUserClipboard clipboard = getClipboard();
+                PerUserClipboard clipboard = getClipboard(intendingUserId);
                 if (clipboard.primaryClip != null) {
                     CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
                     return text != null && text.length() > 0;
@@ -355,11 +464,7 @@
         }
     };
 
-    private PerUserClipboard getClipboard() {
-        return getClipboard(UserHandle.getCallingUserId());
-    }
-
-    private PerUserClipboard getClipboard(int userId) {
+    private PerUserClipboard getClipboard(@UserIdInt int userId) {
         synchronized (mClipboards) {
             PerUserClipboard puc = mClipboards.get(userId);
             if (puc == null) {
@@ -370,7 +475,7 @@
         }
     }
 
-    List<UserInfo> getRelatedProfiles(int userId) {
+    List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {
         final List<UserInfo> related;
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -397,7 +502,7 @@
         }
     }
 
-    void setPrimaryClipInternal(@Nullable ClipData clip, int callingUid) {
+    void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
         // Push clipboard to host, if any
         if (mHostClipboardMonitor != null) {
             if (clip == null) {
@@ -412,8 +517,8 @@
         }
 
         // Update this user
-        final int userId = UserHandle.getUserId(callingUid);
-        setPrimaryClipInternal(getClipboard(userId), clip, callingUid);
+        final int userId = UserHandle.getUserId(uid);
+        setPrimaryClipInternal(getClipboard(userId), clip, uid);
 
         // Update related users
         List<UserInfo> related = getRelatedProfiles(userId);
@@ -426,11 +531,15 @@
                 // primary clip in related users to prevent pasting stale content.
                 if (!canCopy) {
                     clip = null;
+                } else if (clip == null) {
+                    // do nothing for canCopy == true and clip == null case
+                    // To prevent from NPE happen in 'new ClipData(clip)' when run
+                    // android.content.cts.ClipboardManagerTest#testClearPrimaryClip
                 } else {
                     // We want to fix the uris of the related user's clip without changing the
                     // uris of the current user's clip.
                     // So, copy the ClipData, and then copy all the items, so that nothing
-                    // is shared in memmory.
+                    // is shared in memory.
                     clip = new ClipData(clip);
                     for (int i = clip.getItemCount() - 1; i >= 0; i--) {
                         clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
@@ -443,7 +552,7 @@
                         final boolean canCopyIntoProfile = !hasRestriction(
                                 UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
                         if (canCopyIntoProfile) {
-                            setPrimaryClipInternal(getClipboard(id), clip, callingUid);
+                            setPrimaryClipInternal(getClipboard(id), clip, uid);
                         }
                     }
                 }
@@ -452,7 +561,7 @@
     }
 
     void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
-            int callingUid) {
+            int uid) {
         revokeUris(clipboard);
         clipboard.activePermissionOwners.clear();
         if (clip == null && clipboard.primaryClip == null) {
@@ -460,7 +569,7 @@
         }
         clipboard.primaryClip = clip;
         if (clip != null) {
-            clipboard.primaryClipUid = callingUid;
+            clipboard.primaryClipUid = uid;
         } else {
             clipboard.primaryClipUid = android.os.Process.NOBODY_UID;
         }
@@ -479,7 +588,7 @@
                             clipboard.primaryClipListeners.getBroadcastCookie(i);
 
                     if (clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, li.mPackageName,
-                                li.mUid)) {
+                                li.mUid, UserHandle.getUserId(li.mUid))) {
                         clipboard.primaryClipListeners.getBroadcastItem(i)
                                 .dispatchPrimaryClipChanged();
                     }
@@ -494,13 +603,12 @@
         }
     }
 
-    private boolean isDeviceLocked() {
-        int callingUserId = UserHandle.getCallingUserId();
+    private boolean isDeviceLocked(@UserIdInt int userId) {
         final long token = Binder.clearCallingIdentity();
         try {
             final KeyguardManager keyguardManager = getContext().getSystemService(
                     KeyguardManager.class);
-            return keyguardManager != null && keyguardManager.isDeviceLocked(callingUserId);
+            return keyguardManager != null && keyguardManager.isDeviceLocked(userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -585,7 +693,7 @@
         } finally {
             Binder.restoreCallingIdentity(oldIdentity);
         }
-        PerUserClipboard clipboard = getClipboard();
+        PerUserClipboard clipboard = getClipboard(UserHandle.getUserId(uid));
         if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
             final int N = clipboard.primaryClip.getItemCount();
             for (int i=0; i<N; i++) {
@@ -630,9 +738,10 @@
         }
     }
 
-    private boolean clipboardAccessAllowed(int op, String callingPackage, int callingUid) {
+    private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
+            @UserIdInt int userId) {
         // Check the AppOp.
-        if (mAppOps.noteOp(op, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(op, uid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
             return false;
         }
         // Shell can access the clipboard for testing purposes.
@@ -641,7 +750,6 @@
             return true;
         }
         // The default IME is always allowed to access the clipboard.
-        int userId = UserHandle.getUserId(callingUid);
         String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
                 Settings.Secure.DEFAULT_INPUT_METHOD, userId);
         if (!TextUtils.isEmpty(defaultIme)) {
@@ -654,16 +762,31 @@
         switch (op) {
             case AppOpsManager.OP_READ_CLIPBOARD:
                 // Clipboard can only be read by applications with focus..
-                boolean allowed = mWm.isUidFocused(callingUid);
+                // or the application have the INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL
+                // at the same time. e.x. SystemUI. It needs to check the window focus of
+                // Binder.getCallingUid(). Without checking, the user X can't copy any thing from
+                // INTERNAL_SYSTEM_WINDOW to the other applications.
+                boolean allowed = mWm.isUidFocused(uid)
+                        || isInternalSysWindowAppWithWindowFocus(callingPackage);
                 if (!allowed && mContentCaptureInternal != null) {
                     // ...or the Content Capture Service
-                    allowed = mContentCaptureInternal.isContentCaptureServiceForUser(callingUid,
-                            userId);
+                    // The uid parameter of mContentCaptureInternal.isContentCaptureServiceForUser
+                    // is used to check if the uid has the permission BIND_CONTENT_CAPTURE_SERVICE.
+                    // if the application has the permission, let it to access user's clipboard.
+                    // To passed synthesized uid user#10_app#systemui may not tell the real uid.
+                    // userId must pass intending userId. i.e. user#10.
+                    allowed = mContentCaptureInternal.isContentCaptureServiceForUser(
+                            Binder.getCallingUid(), userId);
                 }
                 if (!allowed && mAutofillInternal != null) {
                     // ...or the Augmented Autofill Service
-                    allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(callingUid,
-                            userId);
+                    // The uid parameter of mAutofillInternal.isAugmentedAutofillServiceForUser
+                    // is used to check if the uid has the permission BIND_AUTOFILL_SERVICE.
+                    // if the application has the permission, let it to access user's clipboard.
+                    // To passed synthesized uid user#10_app#systemui may not tell the real uid.
+                    // userId must pass intending userId. i.e. user#10.
+                    allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(
+                            Binder.getCallingUid(), userId);
                 }
                 if (!allowed) {
                     Slog.e(TAG, "Denying clipboard access to " + callingPackage
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index ac3d6de..0910dac2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -19,7 +19,6 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -107,10 +106,14 @@
         }
     }
 
-    private static int getIcon(int transportType) {
-        return (transportType == TRANSPORT_WIFI) ?
-                R.drawable.stat_notify_wifi_in_range :  // TODO: Distinguish ! from ?.
-                R.drawable.stat_notify_rssi_in_range;
+    private static int getIcon(int transportType, NotificationType notifyType) {
+        if (transportType != TRANSPORT_WIFI) {
+            return R.drawable.stat_notify_rssi_in_range;
+        }
+
+        return notifyType == NotificationType.LOGGED_IN
+            ? R.drawable.ic_wifi_signal_4
+            : R.drawable.stat_notify_wifi_in_range;  // TODO: Distinguish ! from ?.
     }
 
     /**
@@ -127,6 +130,7 @@
      * @param id an identifier that uniquely identifies this notification.  This must match
      *         between show and hide calls.  We use the NetID value but for legacy callers
      *         we concatenate the range of types with the range of NetIDs.
+     * @param notifyType the type of the notification.
      * @param nai the network with which the notification is associated. For a SIGN_IN, NO_INTERNET,
      *         or LOST_INTERNET notification, this is the network we're connecting to. For a
      *         NETWORK_SWITCH notification it's the network that we switched from. When this network
@@ -173,7 +177,7 @@
         Resources r = Resources.getSystem();
         CharSequence title;
         CharSequence details;
-        int icon = getIcon(transportType);
+        int icon = getIcon(transportType, notifyType);
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
                     WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6d01375..99341d1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -254,6 +254,9 @@
     // device).
     private Point mStableDisplaySize = new Point();
 
+    // Whether the system has finished booting or not.
+    private boolean mSystemReady;
+
     // The top inset of the default display.
     // This gets persisted so that the boot animation knows how to transition from the display's
     // full size to the size configured by the user. Right now we only persist and animate the top
@@ -330,6 +333,8 @@
         mCurrentUserId = UserHandle.USER_SYSTEM;
         ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
         mWideColorSpace = colorSpaces[1];
+
+        mSystemReady = false;
     }
 
     public void setupSchedulerPolicies() {
@@ -418,6 +423,10 @@
         synchronized (mSyncRoot) {
             mSafeMode = safeMode;
             mOnlyCore = onlyCore;
+            mSystemReady = true;
+            // Just in case the top inset changed before the system was ready. At this point, any
+            // relevant configuration should be in place.
+            recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
         }
 
         mDisplayModeDirector.setListener(new AllowedDisplayModeObserver());
@@ -1057,7 +1066,10 @@
     }
 
     private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
-        if (d == null) {
+        // We must only persist the inset after boot has completed, otherwise we will end up
+        // overwriting the persisted value before the masking flag has been loaded from the
+        // resource overlay.
+        if (!mSystemReady || d == null) {
             return;
         }
         int topInset = d.getInsets().top;
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index ad81ca2..b2420b5 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -23,6 +23,8 @@
 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;
+import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MAX;
+import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MIN;
 
 import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
 
@@ -822,6 +824,9 @@
             return COLOR_MODE_SATURATED;
         } else if (displayColorSetting == 2) {
             return COLOR_MODE_AUTOMATIC;
+        } else if (displayColorSetting >= VENDOR_COLOR_MODE_RANGE_MIN
+                && displayColorSetting <= VENDOR_COLOR_MODE_RANGE_MAX) {
+            return displayColorSetting;
         } else {
             return -1;
         }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 35a82ae..ea1c49d 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1057,7 +1057,8 @@
             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     uId, null, jobStatus.getBatteryName(),
                     StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
-                    JobProtoEnums.STOP_REASON_CANCELLED);
+                    JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
+                    jobStatus.getJobId());
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index a349d87..7689bd2 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -265,7 +265,8 @@
             }
             mJobPackageTracker.noteActive(job);
             try {
-                mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
+                mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid(),
+                        job.getStandbyBucket(), job.getJobId());
             } catch (RemoteException e) {
                 // Whatever.
             }
@@ -774,7 +775,8 @@
         mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
         try {
             mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
-                    mRunningJob.getSourceUid(), mParams.getStopReason());
+                    mRunningJob.getSourceUid(), mParams.getStopReason(),
+                    mRunningJob.getStandbyBucket(), mRunningJob.getJobId());
         } catch (RemoteException e) {
             // Whatever.
         }
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 2a9d3f3..f560d69 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -254,6 +254,12 @@
         public int bgJobCountInMaxPeriod;
 
         /**
+         * The number of {@link TimingSession}s within the bucket window size. This will include
+         * sessions that started before the window as long as they end within the window.
+         */
+        public int sessionCountInWindow;
+
+        /**
          * The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
          * equals the quota. This is only valid if
          * executionTimeInWindowMs >= {@link #mAllowedTimePerPeriodMs} or
@@ -274,21 +280,34 @@
          */
         public int jobCountInAllowedTime;
 
+        /**
+         * The time after which {@link #sessionCountInAllowedTime} should be considered
+         * invalid, in the elapsed realtime timebase.
+         */
+        public long sessionCountExpirationTimeElapsed;
+
+        /**
+         * The number of {@link TimingSession}s that ran in at least the last
+         * {@link #mAllowedTimePerPeriodMs}. It may contain a few stale entries since cleanup won't
+         * happen exactly every {@link #mAllowedTimePerPeriodMs}. This should only be considered
+         * valid before elapsed realtime has reached {@link #sessionCountExpirationTimeElapsed}.
+         */
+        public int sessionCountInAllowedTime;
+
         @Override
         public String toString() {
-            return new StringBuilder()
-                    .append("expirationTime=").append(expirationTimeElapsed).append(", ")
-                    .append("windowSize=").append(windowSizeMs).append(", ")
-                    .append("executionTimeInWindow=").append(executionTimeInWindowMs).append(", ")
-                    .append("bgJobCountInWindow=").append(bgJobCountInWindow).append(", ")
-                    .append("executionTimeInMaxPeriod=").append(executionTimeInMaxPeriodMs)
-                    .append(", ")
-                    .append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ")
-                    .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed).append(", ")
-                    .append("jobCountExpirationTime=").append(jobCountExpirationTimeElapsed)
-                    .append(", ")
-                    .append("jobCountInAllowedTime=").append(jobCountInAllowedTime)
-                    .toString();
+            return "expirationTime=" + expirationTimeElapsed + ", "
+                    + "windowSize=" + windowSizeMs + ", "
+                    + "executionTimeInWindow=" + executionTimeInWindowMs + ", "
+                    + "bgJobCountInWindow=" + bgJobCountInWindow + ", "
+                    + "executionTimeInMaxPeriod=" + executionTimeInMaxPeriodMs + ", "
+                    + "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
+                    + "sessionCountInWindow=" + sessionCountInWindow + ", "
+                    + "quotaCutoffTime=" + quotaCutoffTimeElapsed + ", "
+                    + "jobCountExpirationTime=" + jobCountExpirationTimeElapsed + ", "
+                    + "jobCountInAllowedTime=" + jobCountInAllowedTime + ", "
+                    + "sessionCountExpirationTime=" + sessionCountExpirationTimeElapsed + ", "
+                    + "sessionCountInAllowedTime=" + sessionCountInAllowedTime;
         }
 
         @Override
@@ -300,10 +319,15 @@
                         && this.executionTimeInWindowMs == other.executionTimeInWindowMs
                         && this.bgJobCountInWindow == other.bgJobCountInWindow
                         && this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
+                        && this.sessionCountInWindow == other.sessionCountInWindow
                         && this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
                         && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
                         && this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
-                        && this.jobCountInAllowedTime == other.jobCountInAllowedTime;
+                        && this.jobCountInAllowedTime == other.jobCountInAllowedTime
+                        && this.sessionCountExpirationTimeElapsed
+                        == other.sessionCountExpirationTimeElapsed
+                        && this.sessionCountInAllowedTime
+                        == other.sessionCountInAllowedTime;
             } else {
                 return false;
             }
@@ -318,9 +342,12 @@
             result = 31 * result + bgJobCountInWindow;
             result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
             result = 31 * result + bgJobCountInMaxPeriod;
+            result = 31 * result + sessionCountInWindow;
             result = 31 * result + hashLong(quotaCutoffTimeElapsed);
             result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
             result = 31 * result + jobCountInAllowedTime;
+            result = 31 * result + hashLong(sessionCountExpirationTimeElapsed);
+            result = 31 * result + sessionCountInAllowedTime;
             return result;
         }
     }
@@ -401,6 +428,12 @@
     /** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
     private int mMaxJobCountPerAllowedTime = 20;
 
+    /**
+     * The maximum number of {@link TimingSession}s that can run within the past {@link
+     * #mAllowedTimePerPeriodMs}.
+     */
+    private int mMaxSessionCountPerAllowedTime = 20;
+
     private long mNextCleanupTimeElapsed = 0;
     private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
             new AlarmManager.OnAlarmListener() {
@@ -480,6 +513,29 @@
     /** The minimum number of jobs that any bucket will be allowed to run. */
     private static final int MIN_BUCKET_JOB_COUNT = 100;
 
+    /**
+     * The maximum number of {@link TimingSession}s based on its standby bucket. For each max value
+     * count in the array, the app will not be allowed to have more than that many number of
+     * {@link TimingSession}s within the latest time interval of its rolling window size.
+     *
+     * @see #mBucketPeriodsMs
+     */
+    private final int[] mMaxBucketSessionCounts = new int[]{
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_ACTIVE,
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_WORKING,
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_FREQUENT,
+            QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE
+    };
+
+    /** The minimum number of {@link TimingSession}s that any bucket will be allowed to run. */
+    private static final int MIN_BUCKET_SESSION_COUNT = 3;
+
+    /**
+     * Treat two distinct {@link TimingSession}s as the same if they start and end within this
+     * amount of time of each other.
+     */
+    private long mTimingSessionCoalescingDurationMs = 0;
+
     /** An app has reached its quota. The message should contain a {@link Package} object. */
     private static final int MSG_REACHED_QUOTA = 0;
     /** Drop any old timing sessions. */
@@ -695,14 +751,10 @@
             return true;
         }
 
-        return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0
-                && isUnderJobCountQuotaLocked(userId, packageName, standbyBucket);
-    }
-
-    private boolean isUnderJobCountQuotaLocked(final int userId, @NonNull final String packageName,
-            final int standbyBucket) {
-        ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket, false);
-        return isUnderJobCountQuotaLocked(stats, standbyBucket);
+        ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+        return getRemainingExecutionTimeLocked(stats) > 0
+                && isUnderJobCountQuotaLocked(stats, standbyBucket)
+                && isUnderSessionCountQuotaLocked(stats, standbyBucket);
     }
 
     private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats,
@@ -715,6 +767,17 @@
                 && (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
     }
 
+    private boolean isUnderSessionCountQuotaLocked(@NonNull ExecutionStats stats,
+            final int standbyBucket) {
+        final long now = sElapsedRealtimeClock.millis();
+        final boolean isUnderAllowedTimeQuota =
+                (stats.sessionCountExpirationTimeElapsed <= now
+                        || stats.sessionCountInAllowedTime
+                        < mMaxSessionCountPerAllowedTime);
+        return isUnderAllowedTimeQuota
+                && stats.sessionCountInWindow < mMaxBucketSessionCounts[standbyBucket];
+    }
+
     @VisibleForTesting
     long getRemainingExecutionTimeLocked(@NonNull final JobStatus jobStatus) {
         return getRemainingExecutionTimeLocked(jobStatus.getSourceUserId(),
@@ -739,7 +802,11 @@
         if (standbyBucket == NEVER_INDEX) {
             return 0;
         }
-        final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+        return getRemainingExecutionTimeLocked(
+                getExecutionStatsLocked(userId, packageName, standbyBucket));
+    }
+
+    private long getRemainingExecutionTimeLocked(@NonNull ExecutionStats stats) {
         return Math.min(mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs,
                 mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
     }
@@ -877,6 +944,7 @@
         stats.bgJobCountInWindow = 0;
         stats.executionTimeInMaxPeriodMs = 0;
         stats.bgJobCountInMaxPeriod = 0;
+        stats.sessionCountInWindow = 0;
         stats.quotaCutoffTimeElapsed = 0;
 
         Timer timer = mPkgTimers.get(userId, packageName);
@@ -906,12 +974,14 @@
 
         final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
         final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
+        int sessionCountInWindow = 0;
         // The minimum time between the start time and the beginning of the sessions that were
         // looked at --> how much time the stats will be valid for.
         long emptyTimeMs = Long.MAX_VALUE;
         // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
         // the most recent ones.
-        for (int i = sessions.size() - 1; i >= 0; --i) {
+        final int loopStart = sessions.size() - 1;
+        for (int i = loopStart; i >= 0; --i) {
             TimingSession session = sessions.get(i);
 
             // Window management.
@@ -924,6 +994,12 @@
                             session.startTimeElapsed + stats.executionTimeInWindowMs
                                     - mAllowedTimeIntoQuotaMs);
                 }
+                if (i == loopStart
+                        || (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
+                                > mTimingSessionCoalescingDurationMs) {
+                    // Coalesce sessions if they are very close to each other in time
+                    sessionCountInWindow++;
+                }
             } else if (startWindowElapsed < session.endTimeElapsed) {
                 // The session started before the window but ended within the window. Only include
                 // the portion that was within the window.
@@ -935,6 +1011,11 @@
                             startWindowElapsed + stats.executionTimeInWindowMs
                                     - mAllowedTimeIntoQuotaMs);
                 }
+                if (i == loopStart
+                        || (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
+                                > mTimingSessionCoalescingDurationMs) {
+                    sessionCountInWindow++;
+                }
             }
 
             // Max period check.
@@ -965,9 +1046,27 @@
             }
         }
         stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
+        stats.sessionCountInWindow = sessionCountInWindow;
     }
 
-    private void invalidateAllExecutionStatsLocked(final int userId,
+    /** Invalidate ExecutionStats for all apps. */
+    @VisibleForTesting
+    void invalidateAllExecutionStatsLocked() {
+        final long nowElapsed = sElapsedRealtimeClock.millis();
+        mExecutionStatsCache.forEach((appStats) -> {
+            if (appStats != null) {
+                for (int i = 0; i < appStats.length; ++i) {
+                    ExecutionStats stats = appStats[i];
+                    if (stats != null) {
+                        stats.expirationTimeElapsed = nowElapsed;
+                    }
+                }
+            }
+        });
+    }
+
+    @VisibleForTesting
+    void invalidateAllExecutionStatsLocked(final int userId,
             @NonNull final String packageName) {
         ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
         if (appStats != null) {
@@ -1003,6 +1102,27 @@
         }
     }
 
+    private void incrementTimingSessionCount(final int userId, @NonNull final String packageName) {
+        final long now = sElapsedRealtimeClock.millis();
+        ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
+        if (appStats == null) {
+            appStats = new ExecutionStats[mBucketPeriodsMs.length];
+            mExecutionStatsCache.add(userId, packageName, appStats);
+        }
+        for (int i = 0; i < appStats.length; ++i) {
+            ExecutionStats stats = appStats[i];
+            if (stats == null) {
+                stats = new ExecutionStats();
+                appStats[i] = stats;
+            }
+            if (stats.sessionCountExpirationTimeElapsed <= now) {
+                stats.sessionCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
+                stats.sessionCountInAllowedTime = 0;
+            }
+            stats.sessionCountInAllowedTime++;
+        }
+    }
+
     @VisibleForTesting
     void saveTimingSession(final int userId, @NonNull final String packageName,
             @NonNull final TimingSession session) {
@@ -1216,11 +1336,14 @@
         final String pkgString = string(userId, packageName);
         ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
         final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
+        final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats,
+                standbyBucket);
 
         QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
         if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
                 && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
-                && isUnderJobCountQuota) {
+                && isUnderJobCountQuota
+                && isUnderTimingSessionCountQuota) {
             // Already in quota. Why was this method called?
             if (DEBUG) {
                 Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
@@ -1253,6 +1376,10 @@
             inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
                     stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
         }
+        if (!isUnderTimingSessionCountQuota) {
+            inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
+                    stats.sessionCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
+        }
         // Only schedule the alarm if:
         // 1. There isn't one currently scheduled
         // 2. The new alarm is significantly earlier than the previous alarm (which could be the
@@ -1483,6 +1610,7 @@
             // of jobs.
             // However, cancel the currently scheduled cutoff since it's not currently useful.
             cancelCutoff();
+            incrementTimingSessionCount(mPkg.userId, mPkg.packageName);
         }
 
         /**
@@ -1842,6 +1970,14 @@
         private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
         private static final String KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME =
                 "max_count_per_allowed_time";
+        private static final String KEY_MAX_SESSION_COUNT_ACTIVE = "max_session_count_active";
+        private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working";
+        private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent";
+        private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare";
+        private static final String KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME =
+                "max_session_count_per_allowed_time";
+        private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
+                "timing_session_coalescing_duration_ms";
 
         private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
                 10 * 60 * 1000L; // 10 minutes
@@ -1866,6 +2002,16 @@
         private static final int DEFAULT_MAX_JOB_COUNT_RARE =
                 2400; // 100/hr
         private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
+        private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
+                20; // 120/hr
+        private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
+                10; // 5/hr
+        private static final int DEFAULT_MAX_SESSION_COUNT_FREQUENT =
+                8; // 1/hr
+        private static final int DEFAULT_MAX_SESSION_COUNT_RARE =
+                3; // .125/hr
+        private static final int DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME = 20;
+        private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 0;
 
         /** How much time each app will have to run jobs within their standby bucket window. */
         public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
@@ -1939,6 +2085,43 @@
          */
         public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME;
 
+        /**
+         * The maximum number of {@link TimingSession}s an app can run within this particular
+         * standby bucket's window size.
+         */
+        public int MAX_SESSION_COUNT_ACTIVE = DEFAULT_MAX_SESSION_COUNT_ACTIVE;
+
+        /**
+         * The maximum number of {@link TimingSession}s an app can run within this particular
+         * standby bucket's window size.
+         */
+        public int MAX_SESSION_COUNT_WORKING = DEFAULT_MAX_SESSION_COUNT_WORKING;
+
+        /**
+         * The maximum number of {@link TimingSession}s an app can run within this particular
+         * standby bucket's window size.
+         */
+        public int MAX_SESSION_COUNT_FREQUENT = DEFAULT_MAX_SESSION_COUNT_FREQUENT;
+
+        /**
+         * The maximum number of {@link TimingSession}s an app can run within this particular
+         * standby bucket's window size.
+         */
+        public int MAX_SESSION_COUNT_RARE = DEFAULT_MAX_SESSION_COUNT_RARE;
+
+        /**
+         * The maximum number of {@link TimingSession}s that can run within the past
+         * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
+         */
+        public int MAX_SESSION_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME;
+
+        /**
+         * Treat two distinct {@link TimingSession}s as the same if they start and end within this
+         * amount of time of each other.
+         */
+        public long TIMING_SESSION_COALESCING_DURATION_MS =
+                DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
+
         QcConstants(Handler handler) {
             super(handler);
         }
@@ -1986,6 +2169,20 @@
                     KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
             MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
                     KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME);
+            MAX_SESSION_COUNT_ACTIVE = mParser.getInt(
+                    KEY_MAX_SESSION_COUNT_ACTIVE, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
+            MAX_SESSION_COUNT_WORKING = mParser.getInt(
+                    KEY_MAX_SESSION_COUNT_WORKING, DEFAULT_MAX_SESSION_COUNT_WORKING);
+            MAX_SESSION_COUNT_FREQUENT = mParser.getInt(
+                    KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
+            MAX_SESSION_COUNT_RARE = mParser.getInt(
+                    KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE);
+            MAX_SESSION_COUNT_PER_ALLOWED_TIME = mParser.getInt(
+                    KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME,
+                    DEFAULT_MAX_SESSION_COUNT_PER_ALLOWED_TIME);
+            TIMING_SESSION_COALESCING_DURATION_MS = mParser.getLong(
+                    KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                    DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
 
             updateConstants();
         }
@@ -2071,11 +2268,48 @@
                     mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
                     changed = true;
                 }
+                int newMaxSessionCountPerAllowedPeriod = Math.max(10,
+                        MAX_SESSION_COUNT_PER_ALLOWED_TIME);
+                if (mMaxSessionCountPerAllowedTime != newMaxSessionCountPerAllowedPeriod) {
+                    mMaxSessionCountPerAllowedTime = newMaxSessionCountPerAllowedPeriod;
+                    changed = true;
+                }
+                int newActiveMaxSessionCount =
+                        Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_ACTIVE);
+                if (mMaxBucketSessionCounts[ACTIVE_INDEX] != newActiveMaxSessionCount) {
+                    mMaxBucketSessionCounts[ACTIVE_INDEX] = newActiveMaxSessionCount;
+                    changed = true;
+                }
+                int newWorkingMaxSessionCount =
+                        Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_WORKING);
+                if (mMaxBucketSessionCounts[WORKING_INDEX] != newWorkingMaxSessionCount) {
+                    mMaxBucketSessionCounts[WORKING_INDEX] = newWorkingMaxSessionCount;
+                    changed = true;
+                }
+                int newFrequentMaxSessionCount =
+                        Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_FREQUENT);
+                if (mMaxBucketSessionCounts[FREQUENT_INDEX] != newFrequentMaxSessionCount) {
+                    mMaxBucketSessionCounts[FREQUENT_INDEX] = newFrequentMaxSessionCount;
+                    changed = true;
+                }
+                int newRareMaxSessionCount =
+                        Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_RARE);
+                if (mMaxBucketSessionCounts[RARE_INDEX] != newRareMaxSessionCount) {
+                    mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
+                    changed = true;
+                }
+                long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
+                        Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
+                if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
+                    mTimingSessionCoalescingDurationMs = newSessionCoalescingDurationMs;
+                    changed = true;
+                }
 
                 if (changed && mShouldThrottle) {
                     // Update job bookkeeping out of band.
                     BackgroundThread.getHandler().post(() -> {
                         synchronized (mLock) {
+                            invalidateAllExecutionStatsLocked();
                             maybeUpdateAllConstraintsLocked();
                         }
                     });
@@ -2100,6 +2334,14 @@
             pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println();
             pw.printPair(KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, MAX_JOB_COUNT_PER_ALLOWED_TIME)
                     .println();
+            pw.printPair(KEY_MAX_SESSION_COUNT_ACTIVE, MAX_SESSION_COUNT_ACTIVE).println();
+            pw.printPair(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
+            pw.printPair(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
+            pw.printPair(KEY_MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE).println();
+            pw.printPair(KEY_MAX_SESSION_COUNT_PER_ALLOWED_TIME, MAX_SESSION_COUNT_PER_ALLOWED_TIME)
+                    .println();
+            pw.printPair(KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+                    TIMING_SESSION_COALESCING_DURATION_MS).println();
             pw.decreaseIndent();
         }
 
@@ -2125,6 +2367,18 @@
             proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
             proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
                     MAX_JOB_COUNT_PER_ALLOWED_TIME);
+            proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_ACTIVE,
+                    MAX_SESSION_COUNT_ACTIVE);
+            proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_WORKING,
+                    MAX_SESSION_COUNT_WORKING);
+            proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_FREQUENT,
+                    MAX_SESSION_COUNT_FREQUENT);
+            proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RARE,
+                    MAX_SESSION_COUNT_RARE);
+            proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_ALLOWED_TIME,
+                    MAX_SESSION_COUNT_PER_ALLOWED_TIME);
+            proto.write(ConstantsProto.QuotaController.TIMING_SESSION_COALESCING_DURATION_MS,
+                    TIMING_SESSION_COALESCING_DURATION_MS);
             proto.end(qcToken);
         }
     }
@@ -2144,6 +2398,12 @@
 
     @VisibleForTesting
     @NonNull
+    int[] getBucketMaxSessionCounts() {
+        return mMaxBucketSessionCounts;
+    }
+
+    @VisibleForTesting
+    @NonNull
     long[] getBucketWindowSizes() {
         return mBucketPeriodsMs;
     }
@@ -2176,6 +2436,16 @@
     }
 
     @VisibleForTesting
+    long getTimingSessionCoalescingDurationMs() {
+        return mTimingSessionCoalescingDurationMs;
+    }
+
+    @VisibleForTesting
+    int getMaxSessionCountPerAllowedTime() {
+        return mMaxSessionCountPerAllowedTime;
+    }
+
+    @VisibleForTesting
     @Nullable
     List<TimingSession> getTimingSessions(int userId, String packageName) {
         return mTimingSessions.get(userId, packageName);
@@ -2401,6 +2671,9 @@
                                 StateControllerProto.QuotaController.ExecutionStats.BG_JOB_COUNT_IN_MAX_PERIOD,
                                 es.bgJobCountInMaxPeriod);
                         proto.write(
+                                StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_WINDOW,
+                                es.sessionCountInWindow);
+                        proto.write(
                                 StateControllerProto.QuotaController.ExecutionStats.QUOTA_CUTOFF_TIME_ELAPSED,
                                 es.quotaCutoffTimeElapsed);
                         proto.write(
@@ -2409,6 +2682,12 @@
                         proto.write(
                                 StateControllerProto.QuotaController.ExecutionStats.JOB_COUNT_IN_ALLOWED_TIME,
                                 es.jobCountInAllowedTime);
+                        proto.write(
+                                StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_EXPIRATION_TIME_ELAPSED,
+                                es.sessionCountExpirationTimeElapsed);
+                        proto.write(
+                                StateControllerProto.QuotaController.ExecutionStats.SESSION_COUNT_IN_ALLOWED_TIME,
+                                es.sessionCountInAllowedTime);
                         proto.end(esToken);
                     }
                 }
diff --git a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
index 596a4c0..82c33f5 100644
--- a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
+++ b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -20,9 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-
 import android.util.Log;
 import android.util.Slog;
+
 import com.android.server.am.ActivityManagerService;
 import com.android.server.job.JobSchedulerService;
 
@@ -67,6 +67,9 @@
 
         IntentFilter filter = new IntentFilter();
 
+        // Screen state
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+
         // State of GarageMode
         filter.addAction(ACTION_GARAGE_MODE_ON);
         filter.addAction(ACTION_GARAGE_MODE_OFF);
@@ -97,6 +100,9 @@
         } else if (action.equals(ACTION_UNFORCE_IDLE)) {
             logIfDebug("Unforcing idle...");
             setForceIdleState(false);
+        } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
+            logIfDebug("Screen is on...");
+            handleScreenOn();
         } else if (action.equals(ACTION_GARAGE_MODE_ON)) {
             logIfDebug("GarageMode is on...");
             mGarageModeOn = true;
@@ -147,9 +153,23 @@
         }
     }
 
-    private void logIfDebug(String msg) {
+    private void handleScreenOn() {
+        if (mForced || mGarageModeOn) {
+            // Even though screen is on, the device remains idle
+            logIfDebug("Screen is on, but device cannot exit idle");
+        } else if (mIdle) {
+            // Exiting idle
+            logIfDebug("Device is exiting idle");
+            mIdle = false;
+        } else {
+            // Already in non-idle state. Nothing to do
+            logIfDebug("Device is already non-idle");
+        }
+    }
+
+    private static void logIfDebug(String msg) {
         if (DEBUG) {
             Slog.v(TAG, msg);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index aa51aec..86a84e3 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -70,7 +70,6 @@
     private static final String CONFIG_GPS_LOCK = "GPS_LOCK";
     private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC";
     public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS";
-    public static final String CONFIG_ES_NOTIFY_INT = "ES_NOTIFY_INT";
 
     // Limit on NI emergency mode time extension after emergency sessions ends
     private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300;  // 5 minute maximum
@@ -200,14 +199,6 @@
     }
 
     /**
-     * Returns the value of config parameter ES_NOTIFY_INT or {@code defaulEsNotify} if no
-     * value is provided or if there is an error parsing the configured value.
-     */
-    int getEsNotify(int defaulEsNotify) {
-        return getIntConfig(CONFIG_ES_NOTIFY_INT, defaulEsNotify);
-    }
-
-    /**
      * Updates the GNSS HAL satellite blacklist.
      */
     void setSatelliteBlacklist(int[] constellations, int[] svids) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index b2315c7..f28bce5 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -966,8 +966,9 @@
     }
 
     private void updateEnabled() {
-        // Generally follow location setting
-        boolean enabled = mContext.getSystemService(LocationManager.class).isLocationEnabled();
+        // Generally follow location setting for current user
+        boolean enabled = mContext.getSystemService(LocationManager.class)
+                .isLocationEnabledForUser(UserHandle.CURRENT);
 
         // ... but disable if PowerManager overrides
         enabled &= !mDisableGpsForPowerManager;
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index c49d900..b7e0a0f 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -18,6 +18,9 @@
 
 import android.annotation.SuppressLint;
 import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,6 +38,7 @@
 
 import com.android.internal.R;
 import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.notification.SystemNotificationChannels;
 
 import java.util.Arrays;
 import java.util.List;
@@ -58,11 +62,6 @@
     // Max wait time for synchronous method onGpsEnabledChanged() to run.
     private static final long ON_GPS_ENABLED_CHANGED_TIMEOUT_MILLIS = 3 * 1000;
 
-    // Valid values for config parameter es_notify_int for posting notification in the status
-    // bar for non-framework location requests in user-initiated emergency use cases.
-    private static final int ES_NOTIFY_NONE = 0;
-    private static final int ES_NOTIFY_ALL = 1;
-
     // Wakelocks
     private static final String WAKELOCK_KEY = TAG;
     private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
@@ -74,9 +73,9 @@
     private final Handler mHandler;
     private final Context mContext;
     private final GpsNetInitiatedHandler mNiHandler;
+    private final Notification mEmergencyLocationUserNotification;
 
     private boolean mIsGpsEnabled;
-    private boolean mEsNotify;
 
     // Number of non-framework location access proxy apps is expected to be small (< 5).
     private static final int ARRAY_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -94,6 +93,7 @@
         mNiHandler = niHandler;
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mPackageManager = mContext.getPackageManager();
+        mEmergencyLocationUserNotification = createEmergencyLocationUserNotification(mContext);
 
         // Complete initialization as the first event to run in mHandler thread. After that,
         // all object state read/update events run in the mHandler thread.
@@ -135,22 +135,7 @@
     void onConfigurationUpdated(GnssConfiguration configuration) {
         // The configuration object must be accessed only in the caller thread and not in mHandler.
         List<String> nfwLocationAccessProxyApps = configuration.getProxyApps();
-        int esNotify = configuration.getEsNotify(ES_NOTIFY_NONE);
-        runOnHandler(() -> {
-            setEsNotify(esNotify);
-            handleUpdateProxyApps(nfwLocationAccessProxyApps);
-        });
-    }
-
-    private void setEsNotify(int esNotify) {
-        if (esNotify != ES_NOTIFY_NONE && esNotify != ES_NOTIFY_ALL) {
-            Log.e(TAG, "Config parameter " + GnssConfiguration.CONFIG_ES_NOTIFY_INT
-                    + " is set to invalid value: " + esNotify
-                    + ". Using default value: " + ES_NOTIFY_NONE);
-            esNotify = ES_NOTIFY_NONE;
-        }
-
-        mEsNotify = (esNotify == ES_NOTIFY_ALL);
+        runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
     }
 
     private void handleInitialize() {
@@ -278,9 +263,6 @@
         private static final byte NFW_RESPONSE_TYPE_ACCEPTED_NO_LOCATION_PROVIDED = 1;
         private static final byte NFW_RESPONSE_TYPE_ACCEPTED_LOCATION_PROVIDED = 2;
 
-        // This must match with NfwProtocolStack enum in IGnssVisibilityControlCallback.hal.
-        private static final byte NFW_PROTOCOL_STACK_SUPL = 1;
-
         private final String mProxyAppPackageName;
         private final byte mProtocolStack;
         private final String mOtherProtocolStackName;
@@ -341,10 +323,6 @@
         private boolean isEmergencyRequestNotification() {
             return mInEmergencyMode && !isRequestAttributedToProxyApp();
         }
-
-        private boolean isRequestTypeSupl() {
-            return mProtocolStack == NFW_PROTOCOL_STACK_SUPL;
-        }
     }
 
     private void handlePermissionsChanged(int uid) {
@@ -517,27 +495,44 @@
 
         logEvent(nfwNotification, isPermissionMismatched);
 
-        if (mEsNotify && nfwNotification.isLocationProvided()) {
-            // Emulate deprecated IGnssNi.hal user notification of emergency NI requests.
-            GpsNetInitiatedHandler.GpsNiNotification notification =
-                    new GpsNetInitiatedHandler.GpsNiNotification();
-            notification.notificationId = 0;
-            notification.niType = nfwNotification.isRequestTypeSupl()
-                    ? GpsNetInitiatedHandler.GPS_NI_TYPE_EMERGENCY_SUPL
-                    : GpsNetInitiatedHandler.GPS_NI_TYPE_UMTS_CTRL_PLANE;
-            notification.needNotify = true;
-            notification.needVerify = false;
-            notification.privacyOverride = false;
-            notification.timeout = 0;
-            notification.defaultResponse = GpsNetInitiatedHandler.GPS_NI_RESPONSE_NORESP;
-            notification.requestorId = nfwNotification.mRequestorId;
-            notification.requestorIdEncoding = GpsNetInitiatedHandler.GPS_ENC_NONE;
-            notification.text = mContext.getString(R.string.global_action_emergency);
-            notification.textEncoding = GpsNetInitiatedHandler.GPS_ENC_NONE;
-            mNiHandler.setNiNotification(notification);
+        if (nfwNotification.isLocationProvided()) {
+            postEmergencyLocationUserNotification(nfwNotification);
         }
     }
 
+    private void postEmergencyLocationUserNotification(NfwNotification nfwNotification) {
+        // Emulate deprecated IGnssNi.hal user notification of emergency NI requests.
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        if (notificationManager == null) {
+            Log.w(TAG, "Could not notify user of emergency location request. Notification: "
+                    + nfwNotification);
+            return;
+        }
+
+        notificationManager.notifyAsUser(/* tag= */ null, /* notificationId= */ 0,
+                mEmergencyLocationUserNotification, UserHandle.ALL);
+    }
+
+    private static Notification createEmergencyLocationUserNotification(Context context) {
+        String firstLineText = context.getString(R.string.gpsNotifTitle);
+        String secondLineText =  context.getString(R.string.global_action_emergency);
+        String accessibilityServicesText = firstLineText + " (" + secondLineText + ")";
+        return new Notification.Builder(context, SystemNotificationChannels.NETWORK_ALERTS)
+                .setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on)
+                .setWhen(0)
+                .setOngoing(true)
+                .setAutoCancel(true)
+                .setColor(context.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setDefaults(0)
+                .setTicker(accessibilityServicesText)
+                .setContentTitle(firstLineText)
+                .setContentText(secondLineText)
+                .setContentIntent(PendingIntent.getBroadcast(context, 0, new Intent(), 0))
+                .build();
+    }
+
     private void logEvent(NfwNotification notification, boolean isPermissionMismatched) {
         StatsLog.write(StatsLog.GNSS_NFW_NOTIFICATION_REPORTED,
                 notification.mProxyAppPackageName,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b246eb6..017c1b3 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -27,7 +27,6 @@
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
@@ -560,10 +559,11 @@
 
     public void onCleanupUser(int userId) {
         hideEncryptionNotification(new UserHandle(userId));
-        // User is stopped with its CE key evicted. Require strong auth next time to be able to
-        // unlock the user's storage. Use STRONG_AUTH_REQUIRED_AFTER_BOOT since stopping and
-        // restarting a user later is equivalent to rebooting the device.
-        requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_BOOT, userId);
+        // User is stopped with its CE key evicted. Restore strong auth requirement to the default
+        // flags after boot since stopping and restarting a user later is equivalent to rebooting
+        // the device.
+        int strongAuthRequired = LockPatternUtils.StrongAuthTracker.getDefaultFlags(mContext);
+        requireStrongAuth(strongAuthRequired, userId);
     }
 
     public void onStartUser(final int userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4f85941..8c58247 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -23,6 +23,7 @@
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -45,6 +46,7 @@
 import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -81,6 +83,9 @@
 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
@@ -130,6 +135,7 @@
 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.IPackageManager;
 import android.content.pm.PackageManager;
@@ -196,6 +202,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsLog;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.view.accessibility.AccessibilityEvent;
@@ -309,7 +316,6 @@
     static final boolean ENABLE_BLOCKED_TOASTS = true;
 
     static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
-            Adjustment.KEY_IMPORTANCE,
             Adjustment.KEY_CONTEXTUAL_ACTIONS,
             Adjustment.KEY_TEXT_REPLIES};
 
@@ -1031,12 +1037,19 @@
                     final StatusBarNotification n = r.sbn;
                     final int callingUid = n.getUid();
                     final String pkg = n.getPackageName();
+                    final boolean wasBubble = r.getNotification().isBubbleNotification();
                     if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid,
                             null /* oldEntry */)) {
                         r.getNotification().flags |= FLAG_BUBBLE;
                     } else {
                         r.getNotification().flags &= ~FLAG_BUBBLE;
                     }
+                    if (wasBubble != r.getNotification().isBubbleNotification()) {
+                        // Add the "alert only once" flag so that the notification won't HUN
+                        // unnecessarily just because the bubble flag was changed.
+                        r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+                        mListeners.notifyPostedLocked(r, r);
+                    }
                 }
             }
         }
@@ -4814,10 +4827,14 @@
     private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
             NotificationRecord oldRecord) {
         Notification notification = r.getNotification();
+        Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
+        boolean intentCanBubble = metadata != null
+                && canLaunchInActivityView(getContext(), metadata.getIntent(), pkg);
 
         // Does the app want to bubble & is able to bubble
-        boolean canBubble = notification.getBubbleMetadata() != null
+        boolean canBubble = intentCanBubble
                 && mPreferencesHelper.areBubblesAllowed(pkg, userId)
+                && mPreferencesHelper.bubblesEnabled(r.sbn.getUser())
                 && r.getChannel().canBubble()
                 && !mActivityManager.isLowRamDevice();
 
@@ -4861,6 +4878,63 @@
         return false;
     }
 
+    /**
+     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+     *
+     * @param context       the context to use.
+     * @param pendingIntent the pending intent of the bubble.
+     * @param packageName   the notification package name for this bubble.
+     */
+    // Keep checks in sync with BubbleController#canLaunchInActivityView.
+    @VisibleForTesting
+    protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+            String packageName) {
+        if (pendingIntent == null) {
+            Log.w(TAG, "Unable to create bubble -- no intent");
+            return false;
+        }
+
+        // Need escalated privileges to get the intent.
+        final long token = Binder.clearCallingIdentity();
+        Intent intent;
+        try {
+            intent = pendingIntent.getIntent();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+
+        ActivityInfo info = intent != null
+                ? intent.resolveActivityInfo(context.getPackageManager(), 0)
+                : null;
+        if (info == null) {
+            StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+            Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+                    + intent);
+            return false;
+        }
+        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+            StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+            Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+                    + intent);
+            return false;
+        }
+        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+            StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
+            Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
+                    + "for intent: " + intent);
+            return false;
+        }
+        if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+            Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
+                    + intent);
+            return false;
+        }
+        return true;
+    }
+
     private void doChannelWarningToast(CharSequence toastText) {
         Binder.withCleanCallingIdentity(() -> {
             final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
@@ -5732,7 +5806,7 @@
         }
         // Suppressed because it's a silent update
         final Notification notification = record.getNotification();
-        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+        if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
             return false;
         }
         // Suppressed because another notification in its group handles alerting
@@ -5751,7 +5825,7 @@
     boolean shouldMuteNotificationLocked(final NotificationRecord record) {
         // Suppressed because it's a silent update
         final Notification notification = record.getNotification();
-        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+        if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
             return true;
         }
 
@@ -7336,7 +7410,7 @@
         static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
 
         private static final String ATT_USER_SET = "user_set";
-        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "allowed_adjustments";
+        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "q_allowed_adjustments";
         private static final String ATT_TYPES = "types";
 
         private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3f1a248..0804a73 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -736,7 +736,8 @@
             }
             updatedChannel.setImportanceLockedByCriticalDeviceFunction(
                     r.defaultAppLockedImportance);
-            if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()) {
+            if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
+                    && updatedChannel.getImportance() == IMPORTANCE_NONE) {
                 updatedChannel.setImportance(channel.getImportance());
             }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index 7ee167a..99f5839 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -120,42 +120,65 @@
                     return 1;
             }
         }
-        final String packageName = getNextArg();
 
+        final String packageName = getNextArg();
+        if (packageName != null) {
+            List<OverlayInfo> overlaysForTarget = mInterface.getOverlayInfosForTarget(
+                    packageName, userId);
+
+            // If the package is not targeted by any overlays, check if the package is an overlay.
+            if (overlaysForTarget.isEmpty()) {
+                final OverlayInfo info = mInterface.getOverlayInfo(packageName, userId);
+                if (info != null) {
+                    printListOverlay(out, info);
+                }
+                return 0;
+            }
+
+            out.println(packageName);
+
+            // Print the overlays for the target.
+            final int n = overlaysForTarget.size();
+            for (int i = 0; i < n; i++) {
+                printListOverlay(out, overlaysForTarget.get(i));
+            }
+
+            return 0;
+        }
+
+        // Print all overlays grouped by target package name.
         final Map<String, List<OverlayInfo>> allOverlays = mInterface.getAllOverlays(userId);
         for (final String targetPackageName : allOverlays.keySet()) {
-            if (targetPackageName.equals(packageName)) {
-                out.println(targetPackageName);
-            }
+            out.println(targetPackageName);
+
             List<OverlayInfo> overlaysForTarget = allOverlays.get(targetPackageName);
             final int n = overlaysForTarget.size();
             for (int i = 0; i < n; i++) {
-                final OverlayInfo oi = overlaysForTarget.get(i);
-                if (!targetPackageName.equals(packageName) && !oi.packageName.equals(packageName)) {
-                    continue;
-                }
-                String status;
-                switch (oi.state) {
-                    case OverlayInfo.STATE_ENABLED_STATIC:
-                    case OverlayInfo.STATE_ENABLED:
-                        status = "[x]";
-                        break;
-                    case OverlayInfo.STATE_DISABLED:
-                        status = "[ ]";
-                        break;
-                    default:
-                        status = "---";
-                        break;
-                }
-                out.println(String.format("%s %s", status, oi.packageName));
+                printListOverlay(out, overlaysForTarget.get(i));
             }
-            if (targetPackageName.equals(packageName)) {
-                out.println();
-            }
+            out.println();
         }
+
         return 0;
     }
 
+    private void printListOverlay(PrintWriter out, OverlayInfo oi) {
+        String status;
+        switch (oi.state) {
+            case OverlayInfo.STATE_ENABLED_STATIC:
+            case OverlayInfo.STATE_ENABLED:
+                status = "[x]";
+                break;
+            case OverlayInfo.STATE_DISABLED:
+                status = "[ ]";
+                break;
+            default:
+                status = "---";
+                break;
+        }
+        out.println(String.format("%s %s", status, oi.packageName));
+    }
+
     private int runEnableDisable(final boolean enable) throws RemoteException {
         final PrintWriter err = getErrPrintWriter();
 
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 691b38e2b..6499530 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -28,7 +28,6 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -162,29 +161,34 @@
         }
     }
 
-    List<ModuleInfo> getInstalledModules(int flags) {
+    /**
+     * By default, returns installed module info, including installed apex modules.
+     *
+     * @param flags Use {@link PackageManager#MATCH_ALL} flag to get all modules.
+     */
+    List<ModuleInfo> getInstalledModules(@PackageManager.ModuleInfoFlags int flags) {
         if (!mMetadataLoaded) {
             throw new IllegalStateException("Call to getInstalledModules before metadata loaded");
         }
 
-        ArrayList<ModuleInfo> allModules = new ArrayList<>(mModuleInfo.values());
         if ((flags & PackageManager.MATCH_ALL) != 0) {
-            return allModules;
+            return new ArrayList<>(mModuleInfo.values());
         }
 
-        ArraySet<String> allPackages;
+        List<PackageInfo> allPackages;
         try {
-            allPackages = new ArraySet<>(mPackageManager.getAllPackages());
+            allPackages = mPackageManager.getInstalledPackages(
+                    flags | PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM).getList();
         } catch (RemoteException e) {
             Slog.w(TAG, "Unable to retrieve all package names", e);
             return Collections.emptyList();
         }
 
         ArrayList<ModuleInfo> installedModules = new ArrayList<>(allPackages.size());
-        for (int i = allModules.size() - 1; i >= 0; --i) {
-            ModuleInfo mi = allModules.get(i);
-            if (allPackages.contains(mi.getPackageName())) {
-                installedModules.add(mi);
+        for (PackageInfo p : allPackages) {
+            ModuleInfo m = mModuleInfo.get(p.packageName);
+            if (m != null) {
+                installedModules.add(m);
             }
         }
         return installedModules;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 35f2149..bd88594 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -854,9 +854,9 @@
 
     @Override
     public void installExistingPackage(String packageName, int installFlags, int installReason,
-            IntentSender statusReceiver, int userId) {
+            IntentSender statusReceiver, int userId, List<String> whiteListedPermissions) {
         mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason,
-                statusReceiver);
+                whiteListedPermissions, statusReceiver);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 23aa8f0..e07a132 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13409,8 +13409,9 @@
                 installExistingPackageAsUser(
                         packageName,
                         userId,
-                        0 /*installFlags*/,
-                        PackageManager.INSTALL_REASON_DEVICE_SETUP);
+                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                        PackageManager.INSTALL_REASON_DEVICE_SETUP,
+                        null);
                 return true;
             }
 
@@ -13498,15 +13499,19 @@
      */
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
-            int installReason) {
-        return installExistingPackageAsUser(packageName, userId, installFlags, installReason, null);
+            int installReason, List<String> whiteListedPermissions) {
+        return installExistingPackageAsUser(packageName, userId, installFlags, installReason,
+                whiteListedPermissions, null);
     }
 
-    int installExistingPackageAsUser(String packageName, int userId, int installFlags,
-            int installReason, IntentSender intentSender) {
+    int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
+            @PackageManager.InstallFlags int installFlags,
+            @PackageManager.InstallReason int installReason,
+            @Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) {
         if (DEBUG_INSTALL) {
             Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
-                    + " installFlags=" + installFlags + " installReason=" + installReason);
+                    + " installFlags=" + installFlags + " installReason=" + installReason
+                    + " whiteListedPermissions=" + whiteListedPermissions);
         }
 
         final int callingUid = Binder.getCallingUid();
@@ -13569,6 +13574,13 @@
             }
 
             if (installed) {
+                if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
+                        != 0 && pkgSetting.pkg != null) {
+                    whiteListedPermissions = pkgSetting.pkg.requestedPermissions;
+                }
+                setWhitelistedRestrictedPermissions(packageName, whiteListedPermissions,
+                        PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
+
                 if (pkgSetting.pkg != null) {
                     synchronized (mInstallLock) {
                         // We don't need to freeze for a brand new install
@@ -20909,6 +20921,12 @@
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mPackages", new Throwable());
         }
+        if (!mSystemReady) {
+            // We might get called before system is ready because of package changes etc, but
+            // finding preferred activity depends on settings provider, so we ignore the update
+            // before that.
+            return false;
+        }
         final Intent intent = getHomeIntent();
         final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
                 PackageManager.GET_META_DATA, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d9a608d..f5c8049 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1162,7 +1162,7 @@
     private int runInstallExisting() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         int userId = UserHandle.USER_SYSTEM;
-        int installFlags = 0;
+        int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
         String opt;
         boolean waitTillComplete = false;
         while ((opt = getNextOption()) != null) {
@@ -1182,6 +1182,9 @@
                 case "--wait":
                     waitTillComplete = true;
                     break;
+                case "--restrict-permissions":
+                    installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
@@ -1201,7 +1204,7 @@
                 final IPackageInstaller installer = mInterface.getPackageInstaller();
                 pw.println("Installing package " + packageName + " for user: " + userId);
                 installer.installExistingPackage(packageName, installFlags, installReason,
-                        receiver.getIntentSender(), userId);
+                        receiver.getIntentSender(), userId, null);
                 final Intent result = receiver.getResult();
                 final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                         PackageInstaller.STATUS_FAILURE);
@@ -1210,7 +1213,7 @@
             }
 
             final int res = mInterface.installExistingPackageAsUser(packageName, userId,
-                    installFlags, installReason);
+                    installFlags, installReason, null);
             if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
                 throw new NameNotFoundException("Package " + packageName + " doesn't exist");
             }
@@ -2370,7 +2373,11 @@
     private InstallParams makeInstallParams() {
         final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
         final InstallParams params = new InstallParams();
+
         params.sessionParams = sessionParams;
+        // Whitelist all permissions by default
+        sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
+
         String opt;
         boolean replaceExisting = true;
         while ((opt = getNextOption()) != null) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 8273752..9442e5d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -305,7 +305,6 @@
     }
 
     public void grantDefaultPermissions(int userId) {
-        removeSystemFixedStorage(userId);
         grantPermissionsToSysComponentsAndPrivApps(userId);
         grantDefaultSystemHandlerPermissions(userId);
         grantDefaultPermissionExceptions(userId);
@@ -314,46 +313,6 @@
         }
     }
 
-    // STOPSHIP: This is meant to fix the devices messed up by storage permission model 2 and
-    //           should be removed once all devices were updated
-    private void removeSystemFixedStorage(int userId) {
-        List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
-                DEFAULT_PACKAGE_INFO_QUERY_FLAGS, userId);
-
-        for (PackageInfo pkg : packages) {
-            if (pkg == null || pkg.requestedPermissions == null) {
-                continue;
-            }
-
-            for (String permission : pkg.requestedPermissions) {
-                if (!(Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission)
-                        || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission))) {
-                    continue;
-                }
-
-                int flags = mContext.getPackageManager().getPermissionFlags(permission,
-                        pkg.packageName, UserHandle.of(userId));
-                if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) == 0) {
-                    continue;
-                }
-
-                Log.v(TAG, "Removing system fixed " + pkg.packageName + "/" + permission);
-                mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
-                        PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, UserHandle.of(userId));
-
-                if (!doesPackageSupportRuntimePermissions(pkg)
-                        || (flags & (PackageManager.FLAG_PERMISSION_USER_SET
-                        | PackageManager.FLAG_PERMISSION_POLICY_FIXED)) != 0) {
-                    continue;
-                }
-
-                Log.v(TAG, "Revoking " + pkg.packageName + "/" + permission);
-                mContext.getPackageManager().revokeRuntimePermission(pkg.packageName, permission,
-                        UserHandle.of(userId));
-            }
-        }
-    }
-
     private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) {
         Set<String> permissions = new ArraySet<>();
         for (String permission : pkg.requestedPermissions) {
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 b2ba290..2b7691b8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2091,7 +2091,7 @@
             return;
         }
 
-        if (bp.isHardOrSoftRestricted()
+        if (bp.isHardRestricted()
                 && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
             Log.e(TAG, "Cannot grant restricted non-exempt permission "
                     + permName + " for package " + packageName);
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index c272707..610c82f 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -18,6 +18,31 @@
             ]
         },
         {
+            "name": "CtsAppSecurityHostTestCases",
+            "options": [
+                {
+                    "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission"
+                }
+            ]
+        },
+        {
+            "name": "CtsPermission2TestCases",
+            "options": [
+                {
+                    "include-filter": "android.permission.cts.SharedUidPermissionsTest"
+                },
+                {
+                    "include-filter": "android.permission.cts.RestrictedPermissionsTest"
+                },
+                {
+                    "include-filter": "android.permission.cts.PermissionMaxSdkVersionTest"
+                },
+                {
+                    "include-filter": "android.permission.cts.PrivappPermissionsTest"
+                }
+            ]
+        },
+        {
             "name": "CtsStatsdHostTestCases",
             "options": [
                 {
@@ -25,5 +50,15 @@
                 }
             ]
         }
+    ],
+    "postsubmit": [
+        {
+            "name": "CtsAppSecurityHostTestCases",
+            "options": [
+                {
+                    "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
+                }
+            ]
+        }
     ]
 }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index fe0b9a6..b7e18c3 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -537,6 +537,7 @@
             Slog.d(TAG, "doAutoBatterySaverLocked: mBootCompleted=" + mBootCompleted
                     + " mSettingsLoaded=" + mSettingsLoaded
                     + " mBatteryStatusSet=" + mBatteryStatusSet
+                    + " mState=" + mState
                     + " mIsBatteryLevelLow=" + mIsBatteryLevelLow
                     + " mIsPowered=" + mIsPowered
                     + " mSettingAutomaticBatterySaver=" + mSettingAutomaticBatterySaver
@@ -689,9 +690,9 @@
                 final boolean isStickyDisabled =
                         mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky;
                 if (isStickyDisabled || shouldTurnOffSticky) {
+                    mState = STATE_OFF;
                     setStickyActive(false);
                     triggerStickyDisabledNotification();
-                    mState = STATE_OFF;
                 } else if (!mIsPowered) {
                     // Re-enable BS.
                     enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
@@ -797,7 +798,8 @@
                         Intent.ACTION_POWER_USAGE_SUMMARY));
     }
 
-    private void triggerStickyDisabledNotification() {
+    @VisibleForTesting
+    void triggerStickyDisabledNotification() {
         NotificationManager manager = mContext.getSystemService(NotificationManager.class);
         ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
                 R.string.battery_saver_notification_channel_name);
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 944e4b8..9a2778d 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -231,6 +231,20 @@
             }
         }, enableRollbackTimedOutFilter, null, getHandler());
 
+        IntentFilter userAddedIntentFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
+                    final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                    if (newUserId == -1) {
+                        return;
+                    }
+                    registerUserCallbacks(UserHandle.of(newUserId));
+                }
+            }
+        }, userAddedIntentFilter, null, getHandler());
+
         registerTimeChangeReceiver();
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index bcef66c..da1c413 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -34,10 +34,12 @@
 import android.os.PowerManager;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.util.StatsLog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.PackageWatchdog;
 import com.android.server.PackageWatchdog.PackageHealthObserver;
 import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
@@ -50,6 +52,7 @@
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -71,6 +74,9 @@
     private final Context mContext;
     private final Handler mHandler;
     private final File mLastStagedRollbackIdFile;
+    // Staged rollback ids that have been committed but their session is not yet ready
+    @GuardedBy("mPendingStagedRollbackIds")
+    private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>();
     // this field is initialized in the c'tor and then only accessed from mHandler thread, so
     // no need to guard with a lock
     private long mNumberOfNativeCrashPollsRemaining;
@@ -120,6 +126,9 @@
             if (status == RollbackManager.STATUS_SUCCESS) {
                 if (rollback.isStaged()) {
                     int rollbackId = rollback.getRollbackId();
+                    synchronized (mPendingStagedRollbackIds) {
+                        mPendingStagedRollbackIds.add(rollbackId);
+                    }
                     BroadcastReceiver listener =
                             listenForStagedSessionReady(rollbackManager, rollbackId,
                                     moduleMetadataPackage);
@@ -289,14 +298,15 @@
                     && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) {
                 PackageInstaller.SessionInfo sessionInfo =
                         packageInstaller.getSessionInfo(sessionId);
-                if (sessionInfo.isStagedSessionReady()) {
+                if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
                     mContext.unregisterReceiver(listener);
                     saveLastStagedRollbackId(rollbackId);
                     logEvent(moduleMetadataPackage,
                             StatsLog
                             .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED);
                     mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
-                } else if (sessionInfo.isStagedSessionFailed()) {
+                } else if (sessionInfo.isStagedSessionFailed()
+                        && markStagedSessionHandled(rollbackId)) {
                     logEvent(moduleMetadataPackage,
                             StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE);
                     mContext.unregisterReceiver(listener);
@@ -305,6 +315,16 @@
         }
     }
 
+    /**
+     * Returns {@code true} if staged session associated with {@code rollbackId} was marked
+     * as handled, {@code false} if already handled.
+     */
+    private boolean markStagedSessionHandled(int rollbackId) {
+        synchronized (mPendingStagedRollbackIds) {
+            return mPendingStagedRollbackIds.remove(rollbackId);
+        }
+    }
+
     private void saveLastStagedRollbackId(int stagedRollbackId) {
         try {
             FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 7d0da68..669656d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -338,8 +338,8 @@
         if (b != null) {
             sThermalService = IThermalService.Stub.asInterface(b);
             try {
-                sThermalService.registerThermalEventListenerWithType(
-                        new ThermalEventListener(), Temperature.TYPE_SKIN);
+                sThermalService.registerThermalEventListener(
+                        new ThermalEventListener());
                 Slog.i(TAG, "register thermal listener successfully");
             } catch (RemoteException e) {
                 // Should never happen.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 7734d6b..a93d2b8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -108,4 +108,9 @@
      * @param displayId display ID
      */
     void onDisplayReady(int displayId);
+
+    /**
+     * Notifies System UI whether the recents animation is running.
+     */
+    void onRecentsAnimationStateChanged(boolean running);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index b2d7084..112104d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -453,9 +453,19 @@
             if (mBar != null) {
                 try {
                     mBar.onDisplayReady(displayId);
-                } catch (RemoteException ex) { }
+                } catch (RemoteException ex) {}
             }
         }
+
+        @Override
+        public void onRecentsAnimationStateChanged(boolean running) {
+            if (mBar != null) {
+                try {
+                    mBar.onRecentsAnimationStateChanged(running);
+                } catch (RemoteException ex) {}
+            }
+
+        }
     };
 
     private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 332df95..8b332d2 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -211,9 +211,9 @@
     }
 
     @Override
-    public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions(
-            String packageName, boolean incoming) {
-        enforceNotIsolatedCaller("getPersistedUriPermissions");
+    public ParceledListSlice<android.content.UriPermission> getUriPermissions(
+            String packageName, boolean incoming, boolean persistedOnly) {
+        enforceNotIsolatedCaller("getUriPermissions");
         Preconditions.checkNotNull(packageName, "packageName");
 
         final int callingUid = Binder.getCallingUid();
@@ -240,7 +240,8 @@
                 } else {
                     for (int j = 0; j < perms.size(); j++) {
                         final UriPermission perm = perms.valueAt(j);
-                        if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
+                        if (packageName.equals(perm.targetPkg)
+                                && (!persistedOnly || perm.persistedModeFlags != 0)) {
                             result.add(perm.buildPersistedPublicApiObject());
                         }
                     }
@@ -252,7 +253,8 @@
                             mGrantedUriPermissions.valueAt(i);
                     for (int j = 0; j < perms.size(); j++) {
                         final UriPermission perm = perms.valueAt(j);
-                        if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
+                        if (packageName.equals(perm.sourcePkg)
+                                && (!persistedOnly || perm.persistedModeFlags != 0)) {
                             result.add(perm.buildPersistedPublicApiObject());
                         }
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 66b305e..1344727 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1085,22 +1085,6 @@
         if (root == this) {
             task.setRootProcess(proc);
         }
-        // Override the process configuration to match the display where the first activity in
-        // the process was launched. This can help with compat issues on secondary displays when
-        // apps use Application to obtain configuration or metrics instead of Activity.
-        final ActivityDisplay display = getDisplay();
-        if (display == null || display.mDisplayId == INVALID_DISPLAY) {
-            return;
-        }
-        if (!proc.hasActivities() && display.mDisplayId != DEFAULT_DISPLAY) {
-            proc.registerDisplayConfigurationListenerLocked(display);
-        } else if (display.mDisplayId == DEFAULT_DISPLAY) {
-            // Once an activity is launched on default display - stop listening for other displays
-            // configurations to maintain compatibility with previous platform releases. E.g. when
-            // an activity is launched in a Bubble and then moved to default screen, we should match
-            // the global device config.
-            proc.unregisterDisplayConfigurationListenerLocked();
-        }
     }
 
     boolean hasProcess() {
@@ -3249,7 +3233,7 @@
         // Update last reported values.
         final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
 
-        setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
+        setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
 
         if (mState == INITIALIZING) {
             // No need to relaunch or schedule new config for activity that hasn't been launched
@@ -3358,14 +3342,6 @@
         return true;
     }
 
-    /** Get process configuration, or global config if the process is not set. */
-    private Configuration getProcessGlobalConfiguration() {
-        if (app != null) {
-            return app.getConfiguration();
-        }
-        return mAtmService.getGlobalConfiguration();
-    }
-
     /**
      * When assessing a configuration change, decide if the changes flags and the new configurations
      * should cause the Activity to relaunch.
@@ -3473,7 +3449,7 @@
             mStackSupervisor.activityRelaunchingLocked(this);
             final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
                     pendingNewIntents, configChangeFlags,
-                    new MergedConfiguration(getProcessGlobalConfiguration(),
+                    new MergedConfiguration(mAtmService.getGlobalConfiguration(),
                             getMergedOverrideConfiguration()),
                     preserveWindow);
             final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3d59e66..b3b6efe 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
@@ -3194,6 +3195,8 @@
                 if (newTask) {
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
+                    } else if (getDisplay().isSingleTaskInstance()) {
+                        transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
                     } else {
                         // If a new task is being launched, then mark the existing top activity as
                         // supporting picture-in-picture while pausing only if the starting activity
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f255048..c885c5e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1011,12 +1011,6 @@
         if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
             return false;
         }
-        // don't abort if the callingPackage is temporarily whitelisted
-        if (mService.isPackageNameWhitelistedForBgActivityStarts(callingPackage)) {
-            Slog.w(TAG, "Background activity start for " + callingPackage
-                    + " temporarily whitelisted. This will not be supported in future Q builds.");
-            return false;
-        }
         // If we don't have callerApp at this point, no caller was provided to startActivity().
         // That's the case for PendingIntent-based starts, since the creator's process might not be
         // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
@@ -1050,6 +1044,12 @@
                     + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
             return false;
         }
+        // don't abort if the callingPackage is temporarily whitelisted
+        if (mService.isPackageNameWhitelistedForBgActivityStarts(callingPackage)) {
+            Slog.w(TAG, "Background activity start for " + callingPackage
+                    + " temporarily whitelisted. This will not be supported in future Q builds.");
+            return false;
+        }
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ed56501..dbc530d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -34,7 +34,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
-import android.util.Pair;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
@@ -189,6 +188,13 @@
     public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
 
     /**
+     * Notify listeners that contents are drawn for the first time on a single task display.
+     *
+     * @param displayId An ID of the display on which contents are drawn.
+     */
+    public abstract void notifySingleTaskDisplayDrawn(int displayId);
+
+    /**
      * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
      *
      * - DO NOT call it with the calling UID cleared.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 772e5e6..e9a266c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6087,7 +6087,8 @@
         }
 
         @Override
-        public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) {
+        public void notifyAppTransitionStarting(SparseIntArray reasons,
+                long timestamp) {
             synchronized (mGlobalLock) {
                 mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
                         reasons, timestamp);
@@ -6095,6 +6096,11 @@
         }
 
         @Override
+        public void notifySingleTaskDisplayDrawn(int displayId) {
+            mTaskChangeNotificationController.notifySingleTaskDisplayDrawn(displayId);
+        }
+
+        @Override
         public void notifyAppTransitionFinished() {
             synchronized (mGlobalLock) {
                 mStackSupervisor.notifyAppTransitionDone();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index ddd5c0a..19ccc62 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
@@ -2052,6 +2053,9 @@
             case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
                 return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
             }
+            case TRANSIT_SHOW_SINGLE_TASK_DISPLAY: {
+                return "TRANSIT_SHOW_SINGLE_TASK_DISPLAY";
+            }
             default: {
                 return "<UNKNOWN: " + transition + ">";
             }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d4c4e6a..6c5ef52 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
@@ -211,6 +212,12 @@
         mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
                 SystemClock.uptimeMillis());
 
+        if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
+            mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+                mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
+            });
+        }
+
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
         mDisplayContent.pendingLayoutChanges |=
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ecbecba..3d7e50d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2782,6 +2782,7 @@
     protected void onAnimationFinished() {
         super.onAnimationFinished();
 
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
         mTransit = TRANSIT_UNSET;
         mTransitFlags = 0;
         mNeedsZBoost = false;
@@ -2816,6 +2817,7 @@
         scheduleAnimation();
 
         mActivityRecord.onAnimationFinished();
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 82ea4fe..c3a769b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -804,10 +804,11 @@
                     mTmpApplySurfaceChangesTransactionState.preferredRefreshRate
                             = w.mAttrs.preferredRefreshRate;
                 }
+                final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
+                        .getPreferredModeId(w);
                 if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
-                        && w.mAttrs.preferredDisplayModeId != 0) {
-                    mTmpApplySurfaceChangesTransactionState.preferredModeId
-                            = w.mAttrs.preferredDisplayModeId;
+                        && preferredModeId != 0) {
+                    mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
                 }
             }
         }
@@ -2636,17 +2637,25 @@
         final WindowState imeWin = mInputMethodWindow;
         final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
                 && !mDividerControllerLocked.isImeHideRequested();
-        final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final TaskStack dockedStack = getSplitScreenPrimaryStack();
+        final boolean dockVisible = dockedStack != null;
+        final Task topDockedTask = dockVisible ? dockedStack.getTopChild() : null;
         final TaskStack imeTargetStack = mWmService.getImeFocusStackLocked();
         final int imeDockSide = (dockVisible && imeTargetStack != null) ?
                 imeTargetStack.getDockSide() : DOCKED_INVALID;
         final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
         final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
-        final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock();
         final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
         final boolean imeHeightChanged = imeVisible &&
                 imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
 
+        // This includes a case where the docked stack is unminimizing and IME is visible for the
+        // bottom side stack. The condition prevents adjusting the override task bounds for IME to
+        // the minimized docked stack bounds.
+        final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
+                || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
+                        && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
+
         // The divider could be adjusted for IME position, or be thinner than usual,
         // or both. There are three possible cases:
         // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 197a3cf..e1dd352 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -381,6 +381,8 @@
      */
     @NonNull private Insets mForwardedInsets = Insets.NONE;
 
+    private RefreshRatePolicy mRefreshRatePolicy;
+
     // -------- PolicyHandler --------
     private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1;
     private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
@@ -591,6 +593,10 @@
             mHasStatusBar = false;
             mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
         }
+
+        mRefreshRatePolicy = new RefreshRatePolicy(mService,
+                mDisplayContent.getDisplayInfo(),
+                mService.mHighRefreshRateBlacklist);
     }
 
     void systemReady() {
@@ -3596,6 +3602,10 @@
         }
     }
 
+    RefreshRatePolicy getRefreshRatePolicy() {
+        return mRefreshRatePolicy;
+    }
+
     void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("DisplayPolicy");
         prefix += "  ";
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
new file mode 100644
index 0000000..c0f53b8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.os.SystemProperties;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A Blacklist for packages that should force the display out of high refresh rate.
+ */
+class HighRefreshRateBlacklist {
+
+    private static final String SYSPROP_KEY = "ro.window_manager.high_refresh_rate_blacklist";
+    private static final String SYSPROP_KEY_LENGTH_SUFFIX = "_length";
+    private static final String SYSPROP_KEY_ENTRY_SUFFIX = "_entry";
+    private static final int MAX_ENTRIES = 50;
+
+    private ArraySet<String> mBlacklistedPackages = new ArraySet<>();
+
+    static HighRefreshRateBlacklist create() {
+        return new HighRefreshRateBlacklist(new SystemPropertyGetter() {
+            @Override
+            public int getInt(String key, int def) {
+                return SystemProperties.getInt(key, def);
+            }
+
+            @Override
+            public String get(String key) {
+                return SystemProperties.get(key);
+            }
+        });
+    }
+
+    @VisibleForTesting
+    HighRefreshRateBlacklist(SystemPropertyGetter propertyGetter) {
+
+        // Read and populate the blacklist
+        final int length = Math.min(
+                propertyGetter.getInt(SYSPROP_KEY + SYSPROP_KEY_LENGTH_SUFFIX, 0),
+                MAX_ENTRIES);
+        for (int i = 1; i <= length; i++) {
+            final String packageName = propertyGetter.get(
+                    SYSPROP_KEY + SYSPROP_KEY_ENTRY_SUFFIX + i);
+            if (!packageName.isEmpty()) {
+                mBlacklistedPackages.add(packageName);
+            }
+        }
+    }
+
+    boolean isBlacklisted(String packageName) {
+        return mBlacklistedPackages.contains(packageName);
+    }
+
+    interface SystemPropertyGetter {
+        int getInt(String key, int def);
+        @NonNull String get(String key);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index f82fdba..c5b2566 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -100,13 +100,15 @@
         }
 
         // If the activity is associated with the recents stack, then try and get that first
+        final int userId = mService.getCurrentUserId();
         mTargetActivityType = intent.getComponent() != null
                 && recentsComponent.equals(intent.getComponent())
                         ? ACTIVITY_TYPE_RECENTS
                         : ACTIVITY_TYPE_HOME;
         ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
                 mTargetActivityType);
-        ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
+        ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent(),
+                userId);
         final boolean hasExistingActivity = targetActivity != null;
         if (hasExistingActivity) {
             final ActivityDisplay display = targetActivity.getDisplay();
@@ -156,13 +158,13 @@
                         .setCallingUid(recentsUid)
                         .setCallingPackage(recentsComponent.getPackageName())
                         .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
-                        .setMayWait(mService.getCurrentUserId())
+                        .setMayWait(userId)
                         .execute();
 
                 // Move the recents activity into place for the animation
-                targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
-                        mTargetActivityType).getTopActivity();
-                targetStack = targetActivity.getActivityStack();
+                targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
+                        mTargetActivityType);
+                targetActivity = getTargetActivity(targetStack, intent.getComponent(), userId);
                 mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
                 if (DEBUG) {
                     Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
@@ -172,7 +174,6 @@
                 mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 mWindowManager.executeAppTransition();
 
-
                 // TODO: Maybe wait for app to draw in this particular case?
 
                 if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
@@ -406,17 +407,18 @@
      * @return the top activity in the {@param targetStack} matching the {@param component}, or just
      * the top activity of the top task if no task matches the component.
      */
-    private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) {
+    private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component,
+            int userId) {
         if (targetStack == null) {
             return null;
         }
 
         for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
             final TaskRecord task = targetStack.getChildAt(i);
-            if (task.getBaseIntent().getComponent().equals(component)) {
+            if (task.userId == userId && task.getBaseIntent().getComponent().equals(component)) {
                 return task.getTopActivity();
             }
         }
-        return targetStack.getTopActivity();
+        return null;
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index d2c510f..c03dabe 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -56,6 +56,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.InsetUtils;
 
@@ -87,6 +88,7 @@
     public @interface ReorderMode {}
 
     private final WindowManagerService mService;
+    private final StatusBarManagerInternal mStatusBar;
     private IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
@@ -308,6 +310,7 @@
         mRunner = remoteAnimationRunner;
         mCallbacks = callbacks;
         mDisplayId = displayId;
+        mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
     }
 
     public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
@@ -381,6 +384,9 @@
                 mMinimizedHomeBounds);
 
         mService.mWindowPlacerLocked.performSurfacePlacement();
+
+        // Notify that the animation has started
+        mStatusBar.onRecentsAnimationStateChanged(true /* running */);
     }
 
     @VisibleForTesting
@@ -594,6 +600,9 @@
                         .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
             }
         }
+
+        // Notify that the animation has ended
+        mStatusBar.onRecentsAnimationStateChanged(false /* running */);
     }
 
     void scheduleFailsafe() {
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
new file mode 100644
index 0000000..0c0cf92
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.util.ArraySet;
+import android.view.Display.Mode;
+import android.view.DisplayInfo;
+
+/**
+ * Policy to select a lower refresh rate for the display if applicable.
+ */
+class RefreshRatePolicy {
+
+    private final int mLowRefreshRateId;
+    private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
+    private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
+    private final WindowManagerService mWmService;
+
+    RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
+            HighRefreshRateBlacklist blacklist) {
+        mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
+        mHighRefreshRateBlacklist = blacklist;
+        mWmService = wmService;
+    }
+
+    /**
+     * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
+     * default mode.
+     */
+    private int findLowRefreshRateModeId(DisplayInfo displayInfo) {
+        Mode mode = displayInfo.getDefaultMode();
+        float[] refreshRates = displayInfo.getDefaultRefreshRates();
+        float bestRefreshRate = mode.getRefreshRate();
+        for (int i = refreshRates.length - 1; i >= 0; i--) {
+            if (refreshRates[i] >= 60f && refreshRates[i] < bestRefreshRate) {
+                bestRefreshRate = refreshRates[i];
+            }
+        }
+        return displayInfo.findDefaultModeByRefreshRate(bestRefreshRate);
+    }
+
+    void addNonHighRefreshRatePackage(String packageName) {
+        mNonHighRefreshRatePackages.add(packageName);
+        mWmService.requestTraversal();
+    }
+
+    void removeNonHighRefreshRatePackage(String packageName) {
+        mNonHighRefreshRatePackages.remove(packageName);
+        mWmService.requestTraversal();
+    }
+
+    int getPreferredModeId(WindowState w) {
+
+        // If app is animating, it's not able to control refresh rate because we want the animation
+        // to run in default refresh rate.
+        if (w.isAnimating()) {
+            return 0;
+        }
+
+        // If app requests a certain refresh rate or mode, don't override it.
+        if (w.mAttrs.preferredRefreshRate != 0 || w.mAttrs.preferredDisplayModeId != 0) {
+            return w.mAttrs.preferredDisplayModeId;
+        }
+
+        final String packageName = w.getOwningPackage();
+
+        // If app is using Camera, force it to default (lower) refresh rate.
+        if (mNonHighRefreshRatePackages.contains(packageName)) {
+            return mLowRefreshRateId;
+        }
+
+        // If app is blacklisted using higher refresh rate, return default (lower) refresh rate
+        if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) {
+            return mLowRefreshRateId;
+        }
+        return 0;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 3ec461d..d58c613 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -35,6 +35,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
 
 import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
@@ -1217,6 +1218,15 @@
                 if (displayShouldSleep) {
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
+                    // When the display which can only contain one task turns on, start a special
+                    // transition. {@link AppTransitionController#handleAppTransitionReady} later
+                    // picks up the transition, and schedules
+                    // {@link ITaskStackListener#onSingleTaskDisplayDrawn} callback which is
+                    // triggered after contents are drawn on the display.
+                    if (display.isSingleTaskInstance()) {
+                        display.mDisplayContent.prepareAppTransition(
+                                TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
+                    }
                     stack.awakeFromSleepingLocked();
                     if (stack.isFocusedStackOnDisplay()
                             && !mStackSupervisor.getKeyguardController()
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 34273f3..ecb0941 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -316,6 +316,18 @@
     }
 
     @Override
+    public void finishMovingTask(IWindow window) {
+        if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
+
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mService.mTaskPositioningController.finishTaskPositioning(window);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) {
         long ident = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 66200e3..27175c7 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -54,6 +54,7 @@
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
     private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20;
     private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21;
+    private static final int NOTIFY_SINGLE_TASK_DISPLAY_DRAWN = 22;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -154,6 +155,10 @@
         l.onSizeCompatModeActivityChanged(m.arg1, (IBinder) m.obj);
     };
 
+    private final TaskStackConsumer mNotifySingleTaskDisplayDrawn = (l, m) -> {
+        l.onSingleTaskDisplayDrawn(m.arg1);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -233,6 +238,9 @@
                 case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
                     forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
                     break;
+                case NOTIFY_SINGLE_TASK_DISPLAY_DRAWN:
+                    forAllRemoteListeners(mNotifySingleTaskDisplayDrawn, msg);
+                    break;
             }
         }
     }
@@ -477,4 +485,14 @@
         forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg);
         msg.sendToTarget();
     }
+
+    /**
+     * Notify listeners that contents are drawn for the first time on a single task display.
+     */
+    void notifySingleTaskDisplayDrawn(int displayId) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_SINGLE_TASK_DISPLAY_DRAWN,
+                displayId, 0 /* unused */);
+        forAllLocalListeners(mNotifySingleTaskDisplayDrawn, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index efd3e1c..2fc64ea 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -119,7 +119,7 @@
     private int mCtrlType = CTRL_NONE;
     @VisibleForTesting
     boolean mDragEnded;
-    private IBinder mClientCallback;
+    IBinder mClientCallback;
 
     InputChannel mServerChannel;
     InputChannel mClientChannel;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 3929a12..2441954 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -185,6 +185,12 @@
         return true;
     }
 
+    public void finishTaskPositioning(IWindow window) {
+        if (mTaskPositioner != null && mTaskPositioner.mClientCallback == window.asBinder()) {
+            finishTaskPositioning();
+        }
+    }
+
     void finishTaskPositioning() {
         mHandler.post(() -> {
             if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 75a8dd5..40bec14 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -492,4 +492,18 @@
      * @return {@code true} if the display should show IME when an input field become focused on it.
      */
     public abstract boolean shouldShowIme(int displayId);
+
+    /**
+     * Tell window manager about a package that should not be running with high refresh rate
+     * setting until removeNonHighRefreshRatePackage is called for the same package.
+     *
+     * This must not be called again for the same package.
+     */
+    public abstract void addNonHighRefreshRatePackage(@NonNull String packageName);
+
+    /**
+     * Tell window manager to stop constraining refresh rate for the given package.
+     */
+    public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
+
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 47df839..d39ee40 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -845,6 +845,8 @@
 
     final Configuration mTempConfiguration = new Configuration();
 
+    final HighRefreshRateBlacklist mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create();
+
     // If true, only the core apps and services are being launched because the device
     // is in a special boot mode, such as being encrypted or waiting for a decryption password.
     // For example, when this flag is true, there will be no wallpaper service.
@@ -7446,6 +7448,22 @@
                 return WindowManagerService.this.shouldShowIme(displayId);
             }
         }
+
+        @Override
+        public void addNonHighRefreshRatePackage(@NonNull String packageName) {
+            synchronized (mGlobalLock) {
+                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
+                        .addNonHighRefreshRatePackage(packageName));
+            }
+        }
+
+        @Override
+        public void removeNonHighRefreshRatePackage(@NonNull String packageName) {
+            synchronized (mGlobalLock) {
+                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
+                        .removeNonHighRefreshRatePackage(packageName));
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 5fc3997..12b62b9 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -439,10 +439,7 @@
 
     @Override
     protected ConfigurationContainer getParent() {
-        // Returning RootActivityContainer as the parent, so that this process controller always
-        // has full configuration and overrides (e.g. from display) are always added on top of
-        // global config.
-        return mAtm.mRootActivityContainer;
+        return null;
     }
 
     @HotPath(caller = HotPath.PROCESS_CHANGE)
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c35e866..5ef184a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3104,7 +3104,7 @@
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mWindowFrames.mCompatFrame);
             final MergedConfiguration mergedConfiguration =
-                    new MergedConfiguration(getProcessGlobalConfiguration(),
+                    new MergedConfiguration(mWmService.mRoot.getConfiguration(),
                     getMergedOverrideConfiguration());
 
             setLastReportedMergedConfiguration(mergedConfiguration);
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index ec7a78b..73bb579 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -32,6 +32,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <binder/IServiceManager.h>
+#include <gui/SurfaceComposerClient.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
 #include <hidl/ServiceManagement.h>
@@ -147,6 +148,8 @@
             processPowerHalReturn(ret, "powerHint");
         }
     }
+
+    SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId));
 }
 
 void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 22231c0..e1aeb33 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -108,6 +108,8 @@
 import android.app.ActivityThread;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.app.IApplicationThread;
@@ -2897,12 +2899,18 @@
                 admin.getUserHandle()).isEmpty()) {
             return false;
         }
+
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setBackgroundActivityStartsAllowed(true);
+
         if (result != null) {
             mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(),
-                    null, result, mHandler, Activity.RESULT_OK, null, null);
+                    null, AppOpsManager.OP_NONE, options.toBundle(),
+                    result, mHandler, Activity.RESULT_OK, null, null);
         } else {
-            mContext.sendBroadcastAsUser(intent, admin.getUserHandle());
+            mContext.sendBroadcastAsUser(intent, admin.getUserHandle(), null, options.toBundle());
         }
+
         return true;
     }
 
@@ -9680,7 +9688,8 @@
                 // Install the profile owner if not present.
                 if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) {
                     mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle,
-                            0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY);
+                            PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                            PackageManager.INSTALL_REASON_POLICY, null);
                 }
             } catch (RemoteException e) {
                 // Does not happen, same process
@@ -10176,7 +10185,8 @@
 
                 // Install the app.
                 mIPackageManager.installExistingPackageAsUser(packageName, userId,
-                        0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY);
+                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                        PackageManager.INSTALL_REASON_POLICY, null);
                 if (isDemo) {
                     // Ensure the app is also ENABLED for demo users.
                     mIPackageManager.setApplicationEnabledSetting(packageName,
@@ -10230,7 +10240,8 @@
                             if (isSystemApp(mIPackageManager, packageName, parentUserId)) {
                                 numberOfAppsInstalled++;
                                 mIPackageManager.installExistingPackageAsUser(packageName, userId,
-                                        0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY);
+                                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                                        PackageManager.INSTALL_REASON_POLICY, null);
                             } else {
                                 Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
                                         + " system app");
@@ -10289,9 +10300,9 @@
                 }
 
                 // Install the package.
-                result = mIPackageManager
-                        .installExistingPackageAsUser(packageName, callingUserId,
-                        0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY)
+                result = mIPackageManager.installExistingPackageAsUser(packageName, callingUserId,
+                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                        PackageManager.INSTALL_REASON_POLICY, null)
                         == PackageManager.INSTALL_SUCCEEDED;
             } catch (RemoteException re) {
                 // shouldn't happen
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e42ed3b..4e1cac9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,6 +36,7 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
+import android.hardware.display.DisplayManagerInternal;
 import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
@@ -790,6 +791,12 @@
         mSystemServiceManager.startService(new SensorPrivacyService(mSystemContext));
         traceEnd();
 
+        if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
+            // DisplayManager needs the overlay immediately.
+            mActivityManagerService.updateSystemUiContext();
+            LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
+        }
+
         // The sensor service needs access to package manager service, app ops
         // service, and permissions service, therefore we start it after them.
         // Start sensor service in a separate thread. Completion should be checked
diff --git a/services/net/Android.bp b/services/net/Android.bp
index ab11fe4..4b0a27b 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -19,6 +19,7 @@
     versions: [
         "1",
         "2",
+        "3",
     ],
 }
 
diff --git a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl
new file mode 100644
index 0000000..1e688d0
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl
@@ -0,0 +1,10 @@
+package android.net;
+interface IIpMemoryStore {
+  oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener);
+  oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener);
+  oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener);
+  oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener);
+  oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener);
+  oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener);
+  oneway void factoryReset();
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl
new file mode 100644
index 0000000..cf02c26
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl
@@ -0,0 +1,4 @@
+package android.net;
+interface IIpMemoryStoreCallbacks {
+  oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore);
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl
new file mode 100644
index 0000000..291dbef
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+parcelable Blob {
+  byte[] data;
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
new file mode 100644
index 0000000..52f40d4
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnBlobRetrievedListener {
+  oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data);
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
new file mode 100644
index 0000000..7853514
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnL2KeyResponseListener {
+  oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key);
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
new file mode 100644
index 0000000..3dd2ae6
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnNetworkAttributesRetrievedListener {
+  oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes);
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
new file mode 100644
index 0000000..46d4ecb
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnSameL3NetworkResponseListener {
+  oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response);
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl
new file mode 100644
index 0000000..54e654b
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+interface IOnStatusListener {
+  oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status);
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
new file mode 100644
index 0000000..9531ea3
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -0,0 +1,8 @@
+package android.net.ipmemorystore;
+parcelable NetworkAttributesParcelable {
+  byte[] assignedV4Address;
+  long assignedV4AddressExpiry;
+  String groupHint;
+  android.net.ipmemorystore.Blob[] dnsAddresses;
+  int mtu;
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
new file mode 100644
index 0000000..414272b
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
@@ -0,0 +1,6 @@
+package android.net.ipmemorystore;
+parcelable SameL3NetworkResponseParcelable {
+  String l2Key1;
+  String l2Key2;
+  float confidence;
+}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl
new file mode 100644
index 0000000..92c6779
--- /dev/null
+++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl
@@ -0,0 +1,4 @@
+package android.net.ipmemorystore;
+parcelable StatusParcelable {
+  int resultCode;
+}
diff --git a/services/net/java/android/net/IIpMemoryStore.aidl b/services/net/java/android/net/IIpMemoryStore.aidl
index 63feae6..add221a 100644
--- a/services/net/java/android/net/IIpMemoryStore.aidl
+++ b/services/net/java/android/net/IIpMemoryStore.aidl
@@ -110,4 +110,9 @@
      */
     void retrieveBlob(String l2Key, String clientId, String name,
             IOnBlobRetrievedListener listener);
+
+    /**
+     * Delete all data because a factory reset operation is in progress.
+     */
+    void factoryReset();
 }
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 4f0d6f1..502cd2c 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -572,7 +572,8 @@
 
         boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection,
                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
-                        | Context.BIND_ALLOW_INSTANT, new UserHandle(mUserId));
+                        | Context.BIND_INCLUDE_CAPABILITIES | Context.BIND_ALLOW_INSTANT,
+                new UserHandle(mUserId));
 
         if (!wasBound) {
             if (DEBUG) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6517303..77e2517 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -43,6 +43,8 @@
 import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
 import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
 import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.AlarmManagerService.TIME_CHANGED_MASK;
 import static com.android.server.AlarmManagerService.WORKING_INDEX;
 
 import static org.junit.Assert.assertEquals;
@@ -126,13 +128,15 @@
     private MockitoSession mMockingSession;
     private Injector mInjector;
     private volatile long mNowElapsedTest;
+    private volatile long mNowRtcTest;
     @GuardedBy("mTestTimer")
     private TestTimer mTestTimer = new TestTimer();
 
     static class TestTimer {
         private long mElapsed;
         boolean mExpired;
-        int mType;
+        private int mType;
+        private int mFlags; // Flags used to decide what needs to be evaluated.
 
         synchronized long getElapsed() {
             return mElapsed;
@@ -147,7 +151,16 @@
             return mType;
         }
 
+        synchronized int getFlags() {
+            return mFlags;
+        }
+
         synchronized void expire() throws InterruptedException {
+            expire(IS_WAKEUP_MASK); // Default: evaluate eligibility of all alarms
+        }
+
+        synchronized void expire(int flags) throws InterruptedException {
+            mFlags = flags;
             mExpired = true;
             notifyAll();
             // Now wait for the alarm thread to finish execution.
@@ -181,7 +194,7 @@
                 }
                 mTestTimer.mExpired = false;
             }
-            return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate.
+            return mTestTimer.getFlags();
         }
 
         @Override
@@ -215,6 +228,11 @@
         }
 
         @Override
+        long getCurrentTimeMillis() {
+            return mNowRtcTest;
+        }
+
+        @Override
         AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) {
             return mClockReceiver;
         }
@@ -340,7 +358,7 @@
     }
 
     @Test
-    public void testSingleAlarmSet() {
+    public void singleElapsedAlarmSet() {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
@@ -348,6 +366,33 @@
     }
 
     @Test
+    public void singleRtcAlarmSet() {
+        mNowElapsedTest = 54;
+        mNowRtcTest = 1243;     // arbitrary values of time
+        final long triggerRtc = mNowRtcTest + 5000;
+        final PendingIntent alarmPi = getNewMockPendingIntent();
+        setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi);
+        final long triggerElapsed = triggerRtc - (mNowRtcTest - mNowElapsedTest);
+        assertEquals(triggerElapsed, mTestTimer.getElapsed());
+    }
+
+    @Test
+    public void timeChangeMovesRtcAlarm() throws Exception {
+        mNowElapsedTest = 42;
+        mNowRtcTest = 4123;     // arbitrary values of time
+        final long triggerRtc = mNowRtcTest + 5000;
+        final PendingIntent alarmPi = getNewMockPendingIntent();
+        setTestAlarm(RTC_WAKEUP, triggerRtc, alarmPi);
+        final long triggerElapsed1 = mTestTimer.getElapsed();
+        final long timeDelta = -123;
+        mNowRtcTest += timeDelta;
+        mTestTimer.expire(TIME_CHANGED_MASK);
+        final long triggerElapsed2 = mTestTimer.getElapsed();
+        assertEquals("Invalid movement of triggerElapsed following time change", triggerElapsed2,
+                triggerElapsed1 - timeDelta);
+    }
+
+    @Test
     public void testSingleAlarmExpiration() throws Exception {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 2e7283c..4e89357 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -469,6 +469,7 @@
         expectedStats.bgJobCountInWindow = 0;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 0;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -479,6 +480,7 @@
         expectedStats.bgJobCountInWindow = 3;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 1;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -490,6 +492,7 @@
         expectedStats.bgJobCountInWindow = 3;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 1;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -501,6 +504,7 @@
         expectedStats.bgJobCountInWindow = 3;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 1;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -511,6 +515,7 @@
         expectedStats.bgJobCountInWindow = 4;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 2;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -522,6 +527,7 @@
         expectedStats.bgJobCountInWindow = 5;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 3;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -532,6 +538,7 @@
         expectedStats.bgJobCountInWindow = 10;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 4;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
@@ -545,6 +552,7 @@
         expectedStats.bgJobCountInWindow = 10;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 4;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
@@ -558,6 +566,7 @@
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 15;
+        expectedStats.sessionCountInWindow = 5;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
@@ -575,6 +584,7 @@
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 18;
+        expectedStats.sessionCountInWindow = 5;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
@@ -585,12 +595,13 @@
                         createTimingSession(now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
                                 2 * MINUTE_IN_MILLIS, 2));
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
-        // Invalid time is now since the earlist session straddles the max period cutoff time.
+        // Invalid time is now since the earliest session straddles the max period cutoff time.
         expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 5;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
@@ -621,6 +632,7 @@
         expectedStats.bgJobCountInWindow = 5;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 1;
         assertEquals(expectedStats,
                 mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
 
@@ -631,6 +643,7 @@
         expectedStats.bgJobCountInWindow = 10;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 2;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         assertEquals(expectedStats,
@@ -643,6 +656,7 @@
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 3;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         assertEquals(expectedStats,
@@ -655,6 +669,7 @@
         expectedStats.bgJobCountInWindow = 20;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInMaxPeriod = 20;
+        expectedStats.sessionCountInWindow = 4;
         expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS)
                 + mQcConstants.IN_QUOTA_BUFFER_MS;
         assertEquals(expectedStats,
@@ -662,10 +677,153 @@
     }
 
     /**
+     * Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
+     */
+    @Test
+    public void testGetExecutionStatsLocked_CoalescingSessions() {
+        for (int i = 0; i < 10; ++i) {
+            mQuotaController.saveTimingSession(0, "com.android.test",
+                    createTimingSession(
+                            JobSchedulerService.sElapsedRealtimeClock.millis(),
+                            5 * MINUTE_IN_MILLIS, 5));
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+            for (int j = 0; j < 5; ++j) {
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(),
+                                MINUTE_IN_MILLIS, 2));
+                advanceElapsedClock(MINUTE_IN_MILLIS);
+                advanceElapsedClock(54 * SECOND_IN_MILLIS);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1));
+                advanceElapsedClock(500);
+                advanceElapsedClock(400);
+                mQuotaController.saveTimingSession(0, "com.android.test",
+                        createTimingSession(
+                                JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1));
+                advanceElapsedClock(100);
+                advanceElapsedClock(5 * SECOND_IN_MILLIS);
+            }
+            advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+        }
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 0;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(32, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(128, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(160, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 500;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(22, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(88, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(110, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 1000;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(22, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(88, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(110, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * SECOND_IN_MILLIS;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(14, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(56, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(70, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = MINUTE_IN_MILLIS;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(4, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(16, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(20, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * MINUTE_IN_MILLIS;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(2, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(8, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(10, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 15 * MINUTE_IN_MILLIS;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(2, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(8, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(10, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+
+        // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+        // between an hour and 15 minutes.
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = HOUR_IN_MILLIS;
+        mQcConstants.updateConstants();
+
+        mQuotaController.invalidateAllExecutionStatsLocked();
+        assertEquals(0, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+        assertEquals(2, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+        assertEquals(8, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+        assertEquals(10, mQuotaController.getExecutionStatsLocked(
+                0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+    }
+
+    /**
      * Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
      */
     @Test
     public void testGetExecutionStatsLocked_Caching() {
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).invalidateAllExecutionStatsLocked();
+
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         mQuotaController.saveTimingSession(0, "com.android.test",
                 createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
@@ -697,6 +855,7 @@
         expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
         expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
         expectedStats.bgJobCountInMaxPeriod = originalStatsActive.bgJobCountInMaxPeriod;
+        expectedStats.sessionCountInWindow = originalStatsActive.sessionCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsActive.quotaCutoffTimeElapsed;
         final ExecutionStats newStatsActive = mQuotaController.getExecutionStatsLocked(0,
                 "com.android.test", ACTIVE_INDEX);
@@ -708,6 +867,7 @@
         expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
+        expectedStats.sessionCountInWindow = originalStatsWorking.sessionCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
         final ExecutionStats newStatsWorking = mQuotaController.getExecutionStatsLocked(0,
                 "com.android.test", WORKING_INDEX);
@@ -718,6 +878,7 @@
         expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
+        expectedStats.sessionCountInWindow = originalStatsFrequent.sessionCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
         final ExecutionStats newStatsFrequent = mQuotaController.getExecutionStatsLocked(0,
                 "com.android.test", FREQUENT_INDEX);
@@ -728,6 +889,7 @@
         expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
+        expectedStats.sessionCountInWindow = originalStatsRare.sessionCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
         final ExecutionStats newStatsRare = mQuotaController.getExecutionStatsLocked(0,
                 "com.android.test", RARE_INDEX);
@@ -1057,6 +1219,37 @@
     }
 
     @Test
+    public void testIsWithinQuotaLocked_TimingSession() {
+        setDischarging();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQcConstants.MAX_SESSION_COUNT_RARE = 3;
+        mQcConstants.MAX_SESSION_COUNT_FREQUENT = 4;
+        mQcConstants.MAX_SESSION_COUNT_WORKING = 5;
+        mQcConstants.MAX_SESSION_COUNT_ACTIVE = 6;
+        mQcConstants.updateConstants();
+
+        for (int i = 0; i < 7; ++i) {
+            mQuotaController.saveTimingSession(0, "com.android.test",
+                    createTimingSession(now - ((10 - i) * MINUTE_IN_MILLIS), 30 * SECOND_IN_MILLIS,
+                            2));
+            mQuotaController.incrementJobCount(0, "com.android.test", 2);
+
+            assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
+                    i < 2,
+                    mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+            assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
+                    i < 3,
+                    mQuotaController.isWithinQuotaLocked(0, "com.android.test", FREQUENT_INDEX));
+            assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
+                    i < 4,
+                    mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+            assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
+                    i < 5,
+                    mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
+        }
+    }
+
+    @Test
     public void testMaybeScheduleCleanupAlarmLocked() {
         // No sessions saved yet.
         mQuotaController.maybeScheduleCleanupAlarmLocked();
@@ -1244,6 +1437,10 @@
         // Rare window size is 24 hours.
         final int standbyBucket = RARE_INDEX;
 
+        // Prevent timing session throttling from affecting the test.
+        mQcConstants.MAX_SESSION_COUNT_RARE = 50;
+        mQcConstants.updateConstants();
+
         // No sessions saved yet.
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
         verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -1536,6 +1733,12 @@
         mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
         mQcConstants.MAX_JOB_COUNT_RARE = 2000;
         mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
+        mQcConstants.MAX_SESSION_COUNT_ACTIVE = 500;
+        mQcConstants.MAX_SESSION_COUNT_WORKING = 400;
+        mQcConstants.MAX_SESSION_COUNT_FREQUENT = 300;
+        mQcConstants.MAX_SESSION_COUNT_RARE = 200;
+        mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = 50;
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 10 * SECOND_IN_MILLIS;
 
         mQcConstants.updateConstants();
 
@@ -1552,6 +1755,13 @@
         assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
         assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
         assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
+        assertEquals(50, mQuotaController.getMaxSessionCountPerAllowedTime());
+        assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
+        assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
+        assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
+        assertEquals(200, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
+        assertEquals(10 * SECOND_IN_MILLIS,
+                mQuotaController.getTimingSessionCoalescingDurationMs());
     }
 
     @Test
@@ -1569,6 +1779,12 @@
         mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
         mQcConstants.MAX_JOB_COUNT_RARE = 1;
         mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
+        mQcConstants.MAX_SESSION_COUNT_ACTIVE = -1;
+        mQcConstants.MAX_SESSION_COUNT_WORKING = 1;
+        mQcConstants.MAX_SESSION_COUNT_FREQUENT = 2;
+        mQcConstants.MAX_SESSION_COUNT_RARE = 1;
+        mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = 0;
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = -1;
 
         mQcConstants.updateConstants();
 
@@ -1584,6 +1800,12 @@
         assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
         assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
         assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
+        assertEquals(10, mQuotaController.getMaxSessionCountPerAllowedTime());
+        assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
+        assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
+        assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
+        assertEquals(3, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
+        assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
 
         // Test larger than a day. Controller should cap at one day.
         mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
@@ -1593,6 +1815,7 @@
         mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
         mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
         mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
+        mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 25 * HOUR_IN_MILLIS;
 
         mQcConstants.updateConstants();
 
@@ -1603,6 +1826,8 @@
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
         assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
+        assertEquals(15 * MINUTE_IN_MILLIS,
+                mQuotaController.getTimingSessionCoalescingDurationMs());
     }
 
     /** Tests that TimingSessions aren't saved when the device is charging. */
@@ -2205,7 +2430,11 @@
         spyOn(mQuotaController);
         doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
 
-        final long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+        // Essentially disable session throttling.
+        mQcConstants.MAX_SESSION_COUNT_WORKING =
+                mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
+        mQcConstants.updateConstants();
+
         final int standbyBucket = WORKING_INDEX;
         setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
 
@@ -2217,6 +2446,7 @@
         // Ran jobs up to the job limit. All of them should be allowed to run.
         for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) {
             JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i);
+            setStandbyBucket(WORKING_INDEX, job);
             mQuotaController.maybeStartTrackingJobLocked(job, null);
             assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
             mQuotaController.prepareForExecutionLocked(job);
@@ -2230,6 +2460,7 @@
         // The app is now out of job count quota
         JobStatus throttledJob = createJobStatus(
                 "testStartAlarmScheduled_JobCount_AllowedTime", 42);
+        setStandbyBucket(WORKING_INDEX, throttledJob);
         mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
         assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
 
@@ -2240,4 +2471,61 @@
         verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
     }
+
+    /**
+     * Tests that the start alarm is properly scheduled when a job has been throttled due to the job
+     * count quota.
+     */
+    @Test
+    public void testStartAlarmScheduled_TimingSessionCount_AllowedTime() {
+        // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaController);
+        doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+        // Essentially disable job count throttling.
+        mQcConstants.MAX_JOB_COUNT_FREQUENT =
+                mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = Integer.MAX_VALUE;
+        // Make sure throttling is because of COUNT_PER_ALLOWED_TIME.
+        mQcConstants.MAX_SESSION_COUNT_FREQUENT =
+                mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME + 1;
+        mQcConstants.updateConstants();
+
+        final int standbyBucket = FREQUENT_INDEX;
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+        // No sessions saved yet.
+        mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
+                standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Ran jobs up to the job limit. All of them should be allowed to run.
+        for (int i = 0; i < mQcConstants.MAX_SESSION_COUNT_PER_ALLOWED_TIME; ++i) {
+            JobStatus job = createJobStatus(
+                    "testStartAlarmScheduled_TimingSessionCount_AllowedTime", i);
+            setStandbyBucket(FREQUENT_INDEX, job);
+            mQuotaController.maybeStartTrackingJobLocked(job, null);
+            assertTrue("Constraint not satisfied for job #" + (i + 1),
+                    job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+            mQuotaController.prepareForExecutionLocked(job);
+            advanceElapsedClock(SECOND_IN_MILLIS);
+            mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+            advanceElapsedClock(SECOND_IN_MILLIS);
+        }
+        // Start alarm shouldn't have been scheduled since the app was in quota up until this point.
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // The app is now out of job count quota
+        JobStatus throttledJob = createJobStatus(
+                "testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42);
+        mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
+        assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+
+        ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+                SOURCE_PACKAGE, standbyBucket);
+        final long expectedWorkingAlarmTime =
+                stats.sessionCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS;
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 212d2a8..a8faa54 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.power.batterysaver;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -22,6 +26,8 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.NotificationManager;
@@ -37,6 +43,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 
 import java.util.HashMap;
 import java.util.Objects;
@@ -201,6 +208,8 @@
         mDevice = new Device();
 
         mTarget = new TestableBatterySaverStateMachine();
+        spyOn(mTarget);
+        doNothing().when(mTarget).triggerStickyDisabledNotification();
 
         mDevice.pushBatteryStatus();
         mTarget.onBootCompleted();
@@ -423,7 +432,7 @@
         assertEquals(70, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
 
-        // Bump ump the threshold.
+        // Bump up the threshold.
         mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 70);
         mDevice.setBatteryLevel(mPersistedState.batteryLevel);
 
@@ -545,6 +554,8 @@
 
     @Test
     public void testAutoBatterySaver_withSticky_withAutoOffEnabled() {
+        InOrder inOrder = inOrder(mTarget);
+
         mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
         mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1);
         mDevice.putGlobalSetting(Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
@@ -569,6 +580,7 @@
         assertEquals(true, mDevice.batterySaverEnabled); // Stays on.
         assertEquals(95, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        inOrder.verify(mTarget, never()).triggerStickyDisabledNotification();
 
         // Scenario 2: User turns BS on manually above the threshold then charges device. BS
         // shouldn't turn back on.
@@ -584,6 +596,7 @@
         assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
         assertEquals(97, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        inOrder.verify(mTarget).triggerStickyDisabledNotification();
 
         // Scenario 3: User turns BS on manually above the threshold. Device drains below
         // threshold and then charged to below threshold. Sticky BS should activate.
@@ -612,6 +625,7 @@
         assertEquals(true, mDevice.batterySaverEnabled);
         assertEquals(30, mPersistedState.batteryLevel);
         assertEquals(true, mPersistedState.batteryLow);
+        inOrder.verify(mTarget, never()).triggerStickyDisabledNotification();
 
         // Scenario 4: User turns BS on manually above the threshold. Device drains below
         // threshold and is eventually charged to above threshold. Sticky BS should turn off.
@@ -627,6 +641,7 @@
         assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
         assertEquals(90, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        inOrder.verify(mTarget).triggerStickyDisabledNotification();
 
         // Scenario 5: User turns BS on manually below threshold and charges to below threshold.
         // Sticky BS should activate.
@@ -654,6 +669,7 @@
         assertEquals(true, mDevice.batterySaverEnabled); // Sticky BS still on.
         assertEquals(80, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        inOrder.verify(mTarget, never()).triggerStickyDisabledNotification();
 
         // Scenario 6: User turns BS on manually below threshold and eventually charges to above
         // threshold. Sticky BS should turn off.
@@ -665,6 +681,7 @@
         assertEquals(false, mDevice.batterySaverEnabled);
         assertEquals(95, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        inOrder.verify(mTarget).triggerStickyDisabledNotification();
 
         // Scenario 7: User turns BS on above threshold and then reboots device. Sticky BS
         // shouldn't activate.
@@ -676,6 +693,8 @@
         assertEquals(false, mDevice.batterySaverEnabled);
         assertEquals(93, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        // initDevice() changes the mTarget reference, so inOrder is invalid here.
+        verify(mTarget).triggerStickyDisabledNotification();
 
         // Scenario 8: User turns BS on below threshold and then reboots device without charging.
         // Sticky BS should activate.
@@ -690,6 +709,8 @@
         assertEquals(true, mDevice.batterySaverEnabled);
         assertEquals(75, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        // initDevice() changes the mTarget reference, so inOrder is invalid here.
+        verify(mTarget, never()).triggerStickyDisabledNotification();
 
         // Scenario 9: User turns BS on below threshold and then reboots device after charging
         // above threshold. Sticky BS shouldn't activate.
@@ -702,6 +723,8 @@
         assertEquals(false, mDevice.batterySaverEnabled);
         assertEquals(100, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        // initDevice() changes the mTarget reference, so inOrder is invalid here.
+        verify(mTarget).triggerStickyDisabledNotification();
 
         // Scenario 10: Somehow autoDisableLevel is set to a value below lowPowerModeTriggerLevel
         // and then user enables manually above both thresholds, discharges below
@@ -738,6 +761,8 @@
         assertEquals(true, mDevice.batterySaverEnabled);
         assertEquals(65, mPersistedState.batteryLevel);
         assertEquals(true, mPersistedState.batteryLow);
+        // initDevice() changes the mTarget reference, so inOrder is invalid here.
+        verify(mTarget, never()).triggerStickyDisabledNotification();
     }
 
     @Test
@@ -780,6 +805,7 @@
         assertEquals(false, mDevice.batterySaverEnabled); // Sticky BS no longer enabled.
         assertEquals(95, mPersistedState.batteryLevel);
         assertEquals(false, mPersistedState.batteryLow);
+        verify(mTarget).triggerStickyDisabledNotification();
     }
 
     @Test
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 cbabb0b..9ae9824 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -57,6 +57,7 @@
 import android.Manifest.permission;
 import android.annotation.RawRes;
 import android.app.Activity;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
@@ -379,7 +380,9 @@
         verify(mContext.spiedContext).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
-                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
 
         verify(getServices().ipackageManager, times(1)).setApplicationEnabledSetting(
                 eq(admin1.getPackageName()),
@@ -657,6 +660,8 @@
                         DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
                 MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
                 isNull(String.class),
+                eq(AppOpsManager.OP_NONE),
+                any(Bundle.class),
                 any(BroadcastReceiver.class),
                 eq(dpms.mHandler),
                 eq(Activity.RESULT_OK),
@@ -707,6 +712,8 @@
                         DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
                 MockUtils.checkUserHandle(DpmMockContext.CALLER_USER_HANDLE),
                 isNull(String.class),
+                eq(AppOpsManager.OP_NONE),
+                any(Bundle.class),
                 any(BroadcastReceiver.class),
                 eq(dpms.mHandler),
                 eq(Activity.RESULT_OK),
@@ -788,10 +795,14 @@
 
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(intent),
-                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM),
+                eq(null),
+                any(Bundle.class));
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(intent),
-                MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
+                MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID),
+                eq(null),
+                any(Bundle.class));
     }
 
     /**
@@ -828,10 +839,14 @@
 
         verify(mContext.spiedContext, never()).sendBroadcastAsUser(
                 MockUtils.checkIntent(intent),
-                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM),
+                eq(null),
+                any(Bundle.class));
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(intent),
-                MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
+                MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID),
+                eq(null),
+                any(Bundle.class));
     }
 
     /**
@@ -4298,7 +4313,9 @@
 
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(intent),
-                MockUtils.checkUserHandle(userHandle));
+                MockUtils.checkUserHandle(userHandle),
+                eq(null),
+                any());
 
         // CertificateMonitor.updateInstalledCertificates is called on the background thread,
         // let it finish with system uid, otherwise it will throw and crash.
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index e411fb5..bd513dc 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -18,6 +18,8 @@
 
 import static org.mockito.Mockito.mock;
 
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -324,6 +326,12 @@
     }
 
     @Override
+    public void sendBroadcastAsUser(Intent intent,
+            UserHandle user, @Nullable String receiverPermission, @Nullable Bundle options) {
+        spiedContext.sendBroadcastAsUser(intent, user, receiverPermission, options);
+    }
+
+    @Override
     public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) {
         spiedContext.sendBroadcastAsUser(intent, user, receiverPermission);
     }
@@ -338,17 +346,17 @@
     public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
             String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
             int initialCode, String initialData, Bundle initialExtras) {
-        spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
+        sendOrderedBroadcastAsUser(
+                intent, user, receiverPermission, AppOpsManager.OP_NONE, resultReceiver,
                 scheduler, initialCode, initialData, initialExtras);
-        resultReceiver.onReceive(spiedContext, intent);
     }
 
     @Override
     public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
             String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
             Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
-        spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp,
-                resultReceiver,
+        sendOrderedBroadcastAsUser(
+                intent, user, receiverPermission, appOp, null, resultReceiver,
                 scheduler, initialCode, initialData, initialExtras);
     }
 
@@ -358,6 +366,7 @@
             Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
         spiedContext.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options,
                 resultReceiver, scheduler, initialCode, initialData, initialExtras);
+        resultReceiver.onReceive(spiedContext, intent);
     }
 
     @Override
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 3ff85c8..7453c48 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -30,12 +30,14 @@
     <uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
+    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
 
         <provider android:name=".DummyProvider"
             android:authorities="com.android.services.uitests" />
+
     </application>
 
     <instrumentation
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d2332bf..87f221a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -22,6 +22,7 @@
 import static android.app.Notification.CATEGORY_CALL;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
 import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -119,7 +120,6 @@
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
-import android.service.notification.NotifyingApp;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -165,10 +165,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Consumer;
 
 @SmallTest
@@ -306,6 +304,12 @@
             void onGranted(ComponentName assistant, int userId, boolean granted);
         }
 
+        @Override
+        protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+                String packageName) {
+            // Tests for this not being true are in CTS NotificationManagerTest
+            return true;
+        }
     }
 
     private class TestableToastCallback extends ITransientNotification.Stub {
@@ -414,6 +418,19 @@
         mTestableLooper.processAllMessages();
     }
 
+    private void setUpPrefsForBubbles(boolean globalEnabled, boolean pkgEnabled,
+            boolean channelEnabled) {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.bubblesEnabled(any())).thenReturn(globalEnabled);
+        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(pkgEnabled);
+        when(mPreferencesHelper.getNotificationChannel(
+                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+                mTestNotificationChannel);
+        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
+                mTestNotificationChannel.getImportance());
+        mTestNotificationChannel.setAllowBubbles(channelEnabled);
+    }
+
     private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
         Notification.Builder nb = new Notification.Builder(mContext, "a")
                 .setContentTitle("foo")
@@ -4226,13 +4243,7 @@
     @Test
     public void testFlagBubble() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -4254,15 +4265,33 @@
     }
 
     @Test
+    public void testFlagBubble_noFlag_appNotAllowed() throws RemoteException {
+        // Bubbles are allowed!
+        setUpPrefsForBubbles(true /* global */, false /* app */, true /* channel */);
+
+        // Notif with bubble metadata but not our other misc requirements
+        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
+                null /* tvExtender */, true /* isBubble */);
+
+        // Say we're foreground
+        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+                IMPORTANCE_FOREGROUND);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifs.length);
+        assertEquals((notifs[0].getNotification().flags & FLAG_BUBBLE), 0);
+        assertFalse(mService.getNotificationRecord(
+                nr.sbn.getKey()).getNotification().isBubbleNotification());
+    }
+
+    @Test
     public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -4284,13 +4313,7 @@
     @Test
     public void testFlagBubbleNotifs_noFlag_appNotForeground() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -4312,13 +4335,7 @@
     @Test
     public void testFlagBubbleNotifs_flag_previousForegroundFlag() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
         NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
@@ -4358,13 +4375,7 @@
     public void testFlagBubbleNotifs_noFlag_previousForegroundFlag_afterRemoval()
             throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
         NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
@@ -4412,13 +4423,7 @@
     @Test
     public void testFlagBubbleNotifs_flag_messaging() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4464,13 +4469,7 @@
     @Test
     public void testFlagBubbleNotifs_flag_phonecall() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4505,13 +4504,7 @@
     @Test
     public void testFlagBubbleNotifs_noFlag_phonecall_noForegroundService() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4544,13 +4537,7 @@
     @Test
     public void testFlagBubbleNotifs_noFlag_phonecall_noPerson() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4580,13 +4567,7 @@
     @Test
     public void testFlagBubbleNotifs_noFlag_phonecall_noCategory() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4620,13 +4601,7 @@
     @Test
     public void testFlagBubbleNotifs_noFlag_messaging_appNotAllowed() throws RemoteException {
         // Bubbles are NOT allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(false /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4665,13 +4640,7 @@
     @Test
     public void testFlagBubbleNotifs_noFlag_notBubble() throws RemoteException {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif WITHOUT bubble metadata
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
@@ -4688,17 +4657,8 @@
 
     @Test
     public void testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed() throws RemoteException {
-        // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
-
-        // But not on this channel!
-        mTestNotificationChannel.setAllowBubbles(false);
+        // Bubbles are allowed except on this channel
+        setUpPrefsForBubbles(true /* global */, true /* app */, false /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4736,14 +4696,8 @@
 
     @Test
     public void testFlagBubbleNotifs_noFlag_phonecall_notAllowed() throws RemoteException {
-        // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        // Bubbles are not allowed!
+        setUpPrefsForBubbles(false /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4777,17 +4731,8 @@
 
     @Test
     public void testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed() throws RemoteException {
-        // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(false);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
-
-        // But not on this channel!
-        mTestNotificationChannel.setAllowBubbles(false);
+        // Bubbles are allowed, but not on channel.
+        setUpPrefsForBubbles(true /* global */, true /* app */, false /* channel */);
 
         // Give it bubble metadata
         Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
@@ -4992,13 +4937,7 @@
     @Test
     public void testNotificationBubbleChanged_false() throws Exception {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -5012,6 +4951,9 @@
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
+        // Reset as this is called when the notif is first sent
+        reset(mListeners);
+
         // First we were a bubble
         StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsBefore.length);
@@ -5021,22 +4963,19 @@
         mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
         waitForIdle();
 
-        // Now we are not a bubble
-        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifsAfter.length);
-        assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
+        // Make sure we are not a bubble / reported as such to listeners
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+        assertEquals((captor.getValue().getNotification().flags & FLAG_BUBBLE), 0);
+        assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0);
     }
 
     @Test
     public void testNotificationBubbleChanged_true() throws Exception {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Plain notification that has bubble metadata
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -5054,26 +4993,26 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
+        // Reset as this is called when the notif is first sent
+        reset(mListeners);
+
         // Notify we are now a bubble
         mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
         waitForIdle();
 
-        // Make sure we are a bubble
-        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifsAfter.length);
-        assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0);
+        // Make sure we are a bubble / reported as such to listeners
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+        assertTrue((captor.getValue().getNotification().flags & FLAG_BUBBLE) != 0);
+        assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0);
     }
 
     @Test
     public void testNotificationBubbleChanged_true_notAllowed() throws Exception {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif that is not a bubble
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -5082,6 +5021,9 @@
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
+        // Reset as this is called when the notif is first sent
+        reset(mListeners);
+
         // Would be a normal notification because wouldn't have met requirements to bubble
         StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsBefore.length);
@@ -5095,18 +5037,13 @@
         StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsAfter.length);
         assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
+        verify(mListeners, times(0)).notifyPostedLocked(any(), any());
     }
 
     @Test
     public void testNotificationBubbles_disabled_lowRamDevice() throws Exception {
         // Bubbles are allowed!
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true);
-        when(mPreferencesHelper.getNotificationChannel(
-                anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
-                mTestNotificationChannel);
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
-                mTestNotificationChannel.getImportance());
+        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Plain notification that has bubble metadata
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
@@ -5131,6 +5068,5 @@
         StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsAfter.length);
         assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
-
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d1a3550..e22f827 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -66,6 +66,9 @@
 import android.util.Pair;
 import android.util.Xml;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
@@ -90,9 +93,6 @@
 import java.util.Objects;
 import java.util.concurrent.ThreadLocalRandom;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class PreferencesHelperTest extends UiServiceTestCase {
@@ -2520,7 +2520,6 @@
         toAdd.add(new Pair(PKG_O, UID_O));
         mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, toAdd);
 
-
         assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
                 .isImportanceLockedByCriticalDeviceFunction());
         assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, b.getId(), false)
@@ -2623,16 +2622,20 @@
         update.setAllowBubbles(false);
 
         mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
-
         assertEquals(IMPORTANCE_HIGH,
                 mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
         assertEquals(false,
                 mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
 
         mHelper.updateNotificationChannel(PKG_O, UID_O, update, false);
-
         assertEquals(IMPORTANCE_HIGH,
                 mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
+
+        NotificationChannel updateImportanceLow = new NotificationChannel("a", "a",
+                IMPORTANCE_LOW);
+        mHelper.updateNotificationChannel(PKG_O, UID_O, updateImportanceLow, true);
+        assertEquals(IMPORTANCE_LOW,
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
     }
 
     @Test
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 5136705..4c27a3c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -49,6 +49,9 @@
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
+                  android:resizeableActivity="true" />
         <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
                   android:showWhenLocked="true" />
     </application>
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index f8fd64a..11a177a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_90;
@@ -40,7 +38,6 @@
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -59,10 +56,10 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
-import android.view.DisplayInfo;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner.Stub;
 import android.view.RemoteAnimationAdapter;
@@ -601,67 +598,6 @@
         assertNull(mActivity.pendingOptions);
     }
 
-    @Test
-    public void testSetProcessOverridesConfig() {
-        final ActivityRecord defaultDisplayActivity =
-                createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
-        assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges());
-
-        final ActivityRecord secondaryDisplayActivity =
-                createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
-        assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
-        assertEquals(secondaryDisplayActivity.getDisplay().getResolvedOverrideConfiguration(),
-                secondaryDisplayActivity.app.getRequestedOverrideConfiguration());
-
-        assertNotEquals(defaultDisplayActivity.getConfiguration(),
-                secondaryDisplayActivity.getConfiguration());
-    }
-
-    @Test
-    public void testSetProcessDoesntOverrideConfigIfAnotherActivityPresent() {
-        final ActivityRecord defaultDisplayActivity =
-                createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
-        assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges());
-
-        final ActivityRecord secondaryDisplayActivity =
-                createActivityOnDisplay(false /* defaultDisplay */, defaultDisplayActivity.app);
-        assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
-    }
-
-    @Test
-    public void testActivityOnDefaultDisplayClearsProcessOverride() {
-        final ActivityRecord secondaryDisplayActivity =
-                createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
-        assertTrue(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
-
-        final ActivityRecord defaultDisplayActivity =
-                createActivityOnDisplay(true /* defaultDisplay */,
-                        secondaryDisplayActivity.app);
-        assertFalse(defaultDisplayActivity.app.registeredForDisplayConfigChanges());
-        assertFalse(secondaryDisplayActivity.app.registeredForDisplayConfigChanges());
-    }
-
-    /**
-     * Creates an activity on display. For non-default display request it will also create a new
-     * display with custom DisplayInfo.
-     */
-    private ActivityRecord createActivityOnDisplay(boolean defaultDisplay,
-            WindowProcessController process) {
-        final ActivityDisplay display;
-        if (defaultDisplay) {
-            display = mRootActivityContainer.getDefaultDisplay();
-        } else {
-            final DisplayInfo info = new DisplayInfo();
-            info.logicalWidth = 100;
-            info.logicalHeight = 100;
-            display = addNewActivityDisplayAt(info, POSITION_TOP);
-        }
-        final TestActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build();
-    }
-
     /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
     private void prepareFixedAspectRatioUnresizableActivity() {
         setupDisplayContentForCompatDisplayInsets();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index d8c0de7..53b0add 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -195,7 +195,6 @@
         private ActivityStack mStack;
         private int mActivityFlags;
         private int mLaunchMode;
-        private WindowProcessController mWpc;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -246,11 +245,6 @@
             return this;
         }
 
-        ActivityBuilder setUseProcess(WindowProcessController wpc) {
-            mWpc = wpc;
-            return this;
-        }
-
         ActivityRecord build() {
             if (mComponent == null) {
                 final int id = sCurrentActivityId++;
@@ -296,18 +290,12 @@
                 mTaskRecord.addActivityToTop(activity);
             }
 
-            final WindowProcessController wpc;
-            if (mWpc != null) {
-                wpc = mWpc;
-            } else {
-                wpc = new WindowProcessController(mService,
-                        mService.mContext.getApplicationInfo(), "name", 12345,
-                        UserHandle.getUserId(12345), mock(Object.class),
-                        mock(WindowProcessListener.class));
-                wpc.setThread(mock(IApplicationThread.class));
-            }
+            final WindowProcessController wpc = new WindowProcessController(mService,
+                    mService.mContext.getApplicationInfo(), "name", 12345,
+                    UserHandle.getUserId(12345), mock(Object.class),
+                    mock(WindowProcessListener.class));
+            wpc.setThread(mock(IApplicationThread.class));
             activity.setProcess(wpc);
-            wpc.addActivityIfNeeded(activity);
             return activity;
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 8528954..7f35dac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -153,6 +153,7 @@
     @FlakyTest(bugId = 131005232)
     public void testLandscapeSeascapeRotationByApp() {
         // Some plumbing to get the service ready for rotation updates.
+        mWm.mDisplayReady = true;
         mWm.mDisplayEnabled = true;
 
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
@@ -185,6 +186,7 @@
     @Test
     public void testLandscapeSeascapeRotationByPolicy() {
         // Some plumbing to get the service ready for rotation updates.
+        mWm.mDisplayReady = true;
         mWm.mDisplayEnabled = true;
 
         final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
new file mode 100644
index 0000000..5586726
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.HighRefreshRateBlacklist.SystemPropertyGetter;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:HighRefreshRateBlacklistTest
+ */
+@SmallTest
+@Presubmit
+@FlakyTest
+public class HighRefreshRateBlacklistTest {
+
+    @Test
+    public void testBlacklist() {
+        HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(
+                new SystemPropertyGetter() {
+
+                    @Override
+                    public int getInt(String key, int def) {
+                        if ("ro.window_manager.high_refresh_rate_blacklist_length".equals(key)) {
+                            return 2;
+                        }
+                        return def;
+                    }
+
+                    @Override
+                    public String get(String key) {
+                        if ("ro.window_manager.high_refresh_rate_blacklist_entry1".equals(key)) {
+                            return "com.android.sample1";
+                        }
+                        if ("ro.window_manager.high_refresh_rate_blacklist_entry2".equals(key)) {
+                            return "com.android.sample2";
+                        }
+                        return "";
+                    }
+                });
+        assertTrue(blacklist.isBlacklisted("com.android.sample1"));
+        assertTrue(blacklist.isBlacklisted("com.android.sample2"));
+        assertFalse(blacklist.isBlacklisted("com.android.sample3"));
+    }
+
+    @Test
+    public void testNoBlacklist() {
+        HighRefreshRateBlacklist blacklist = new HighRefreshRateBlacklist(
+                new SystemPropertyGetter() {
+
+                    @Override
+                    public int getInt(String key, int def) {
+                        return def;
+                    }
+
+                    @Override
+                    public String get(String key) {
+                        return "";
+                    }
+                });
+        assertFalse(blacklist.isBlacklisted("com.android.sample1"));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 8b2912c..8f41a42 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -16,9 +16,11 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -56,6 +58,8 @@
 @Presubmit
 public class RecentsAnimationTest extends ActivityTestsBase {
 
+    private static final int TEST_USER_ID = 100;
+
     private final ComponentName mRecentsComponent =
             new ComponentName(mContext.getPackageName(), "RecentsActivity");
     private RecentsAnimationController mRecentsAnimationController;
@@ -223,6 +227,37 @@
         verify(mRecentsAnimationController, times(0)).cancelOnNextTransitionStart();
     }
 
+    @Test
+    public void testMultipleUserHomeActivity_findUserHomeTask() {
+        ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+        ActivityStack homeStack = display.getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+        ActivityRecord otherUserHomeActivity = new ActivityBuilder(mService)
+                .setStack(homeStack)
+                .setCreateTask(true)
+                .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
+                .build();
+        otherUserHomeActivity.getTaskRecord().userId = TEST_USER_ID;
+
+        ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        new ActivityBuilder(mService)
+                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
+                .setCreateTask(true)
+                .setStack(fullscreenStack)
+                .build();
+
+        doReturn(TEST_USER_ID).when(mService).getCurrentUserId();
+        doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
+                any() /* starting */, anyInt() /* configChanges */,
+                anyBoolean() /* preserveWindows */);
+
+        startRecentsActivity(otherUserHomeActivity.getTaskRecord().getBaseIntent().getComponent(),
+                true);
+
+        // Ensure we find the task for the right user and it is made visible
+        assertTrue(otherUserHomeActivity.visible);
+    }
+
     private void startRecentsActivity() {
         startRecentsActivity(mRecentsComponent, false /* getRecentsAnimation */);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
new file mode 100644
index 0000000..efc2fd6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.Display.Mode;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ *  atest WmTests:RefreshRatePolicyTest
+ */
+@SmallTest
+@Presubmit
+@FlakyTest
+public class RefreshRatePolicyTest extends WindowTestsBase {
+
+    private static final int LOW_MODE_ID = 3;
+
+    private RefreshRatePolicy mPolicy;
+    private HighRefreshRateBlacklist mBlacklist = mock(HighRefreshRateBlacklist.class);
+
+    @Before
+    public void setUp() {
+        DisplayInfo di = new DisplayInfo(mDisplayInfo);
+        Mode defaultMode = di.getDefaultMode();
+        di.supportedModes = new Mode[] {
+                new Mode(1, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90),
+                new Mode(2, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70),
+                new Mode(LOW_MODE_ID,
+                        defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
+        };
+        di.defaultModeId = 1;
+        mPolicy = new RefreshRatePolicy(mWm, di, mBlacklist);
+    }
+
+    @Test
+    public void testCamera() {
+        final WindowState cameraUsingWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "cameraUsingWindow");
+        cameraUsingWindow.mAttrs.packageName = "com.android.test";
+        assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+        mPolicy.addNonHighRefreshRatePackage("com.android.test");
+        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+        mPolicy.removeNonHighRefreshRatePackage("com.android.test");
+        assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+    }
+
+    @Test
+    public void testBlacklist() {
+        final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "blacklistedWindow");
+        blacklistedWindow.mAttrs.packageName = "com.android.test";
+        when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true);
+        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow));
+    }
+
+    @Test
+    public void testAppOverride_blacklist() {
+        final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "overrideWindow");
+        overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
+        when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true);
+        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
+    }
+
+    @Test
+    public void testAppOverride_camera() {
+        final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "overrideWindow");
+        overrideWindow.mAttrs.packageName = "com.android.test";
+        overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
+        mPolicy.addNonHighRefreshRatePackage("com.android.test");
+        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
+    }
+
+    @Test
+    public void testAnimatingAppOverride() {
+        final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "overrideWindow");
+        overrideWindow.mAttrs.packageName = "com.android.test";
+        overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
+        overrideWindow.mAppToken.mSurfaceAnimator.startAnimation(
+                overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
+                false /* hidden */);
+        mPolicy.addNonHighRefreshRatePackage("com.android.test");
+        assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
+    }
+
+    @Test
+    public void testAnimatingCamera() {
+        final WindowState cameraUsingWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "cameraUsingWindow");
+        cameraUsingWindow.mAttrs.packageName = "com.android.test";
+
+        mPolicy.addNonHighRefreshRatePackage("com.android.test");
+        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+
+        cameraUsingWindow.mAppToken.mSurfaceAnimator.startAnimation(
+                cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
+                false /* hidden */);
+        assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 427a929..366acea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -41,7 +41,6 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IntentFilter;
-import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.Uri;
@@ -176,12 +175,6 @@
         // Display creation is driven by the ActivityManagerService via
         // ActivityStackSupervisor. We emulate those steps here.
         mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
-        mWindowManagerService.displayReady();
-
-        final Configuration defaultDisplayConfig =
-                mWindowManagerService.computeNewConfiguration(DEFAULT_DISPLAY);
-        doReturn(defaultDisplayConfig).when(atms).getGlobalConfiguration();
-        doReturn(defaultDisplayConfig).when(atms).getGlobalConfigurationForPid(anyInt());
 
         mMockTracker.stopTracking();
     }
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 c9263eb..714d2f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -32,6 +32,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.InputChannel;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -41,7 +42,7 @@
  * Tests for the {@link TaskPositioningController} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskPositioningControllerTests
+ *  atest WmTests:TaskPositioningControllerTests
  */
 @SmallTest
 @Presubmit
@@ -87,6 +88,29 @@
         assertNull(mTarget.getDragWindowHandleLocked());
     }
 
+    @FlakyTest(bugId = 129507487)
+    @Test
+    public void testFinishPositioningWhenAppRequested() {
+        synchronized (mWm.mGlobalLock) {
+            assertFalse(mTarget.isPositioningLocked());
+            assertNull(mTarget.getDragWindowHandleLocked());
+        }
+
+        assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
+
+        synchronized (mWm.mGlobalLock) {
+            assertTrue(mTarget.isPositioningLocked());
+            assertNotNull(mTarget.getDragWindowHandleLocked());
+        }
+
+        mTarget.finishTaskPositioning(mWindow.mClient);
+        // Wait until the looper processes finishTaskPositioning.
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
+
+        assertFalse(mTarget.isPositioningLocked());
+        assertNull(mTarget.getDragWindowHandleLocked());
+    }
+
     @Test
     public void testHandleTapOutsideTask() {
         synchronized (mWm.mGlobalLock) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 62247d8..19fd93fe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
@@ -26,18 +28,22 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityTaskManager;
+import android.app.ActivityView;
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
+import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.uiautomator.UiDevice;
 import android.text.TextUtils;
+import android.view.ViewGroup;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
@@ -231,6 +237,40 @@
         assertTrue(activity.mOnDetachedFromWindowCalled);
     }
 
+    @Test
+    public void testTaskOnSingleTaskDisplayDrawn() throws Exception {
+        final Instrumentation instrumentation = getInstrumentation();
+
+        final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
+        final CountDownLatch singleTaskDisplayDrawnLatch = new CountDownLatch(1);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
+                singleTaskDisplayDrawnLatch.countDown();
+            }
+        });
+        final ActivityViewTestActivity activity =
+                (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
+        final ActivityView activityView = activity.getActivityView();
+        activityView.setCallback(new ActivityView.StateCallback() {
+            @Override
+            public void onActivityViewReady(ActivityView view) {
+                activityViewReadyLatch.countDown();
+            }
+
+            @Override
+            public void onActivityViewDestroyed(ActivityView view) {
+            }
+        });
+        waitForCallback(activityViewReadyLatch);
+
+        final Context context = instrumentation.getContext();
+        Intent intent = new Intent(context, ActivityInActivityView.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        activityView.startActivity(intent);
+        waitForCallback(singleTaskDisplayDrawnLatch);
+    }
+
     /**
      * Starts the provided activity and returns the started instance.
      */
@@ -369,4 +409,29 @@
             mOnDetachedFromWindowCountDownLatch = countDownLatch;
         }
     }
+
+    public static class ActivityViewTestActivity extends TestActivity {
+        private ActivityView mActivityView;
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            mActivityView = new ActivityView(this, null /* attrs */, 0 /* defStyle */,
+                    true /* singleTaskInstance */);
+            setContentView(mActivityView);
+
+            ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
+            layoutParams.width = MATCH_PARENT;
+            layoutParams.height = MATCH_PARENT;
+            mActivityView.requestLayout();
+        }
+
+        ActivityView getActivityView() {
+            return mActivityView;
+        }
+    }
+
+    // Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true}
+    public static class ActivityInActivityView extends TestActivity {}
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 7b8fba0..a7c84a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -26,9 +26,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
 import android.platform.test.annotations.Presubmit;
-import android.view.DisplayInfo;
 
 import org.junit.Test;
 
@@ -80,26 +78,6 @@
         assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
     }
 
-    @Test
-    public void testConfigurationForSecondaryScreen() {
-        final WindowProcessController wpc = new WindowProcessController(
-                mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
-        //By default, the process should not listen to any display.
-        assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
-
-        // Register to a new display as a listener.
-        final DisplayInfo info = new DisplayInfo();
-        info.logicalWidth = 100;
-        info.logicalHeight = 100;
-        TestActivityDisplay display = addNewActivityDisplayAt(info, WindowContainer.POSITION_TOP);
-        wpc.registerDisplayConfigurationListenerLocked(display);
-
-        assertEquals(display.mDisplayId, wpc.getDisplayId());
-        final Configuration expectedConfig = mService.mRootActivityContainer.getConfiguration();
-        expectedConfig.updateFrom(display.getConfiguration());
-        assertEquals(expectedConfig, wpc.getConfiguration());
-    }
-
     private TestActivityDisplay createTestActivityDisplayInContainer() {
         final TestActivityDisplay testActivityDisplay = createNewActivityDisplay();
         mRootActivityContainer.addChild(testActivityDisplay, POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index de28b5f..3a702cb9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -154,6 +154,7 @@
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
             mWm.mDisplayEnabled = true;
+            mWm.mDisplayReady = true;
 
             // Set-up some common windows.
             mCommonWindows = new HashSet<>();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d77ea6e..55159c3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -51,6 +51,8 @@
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -104,6 +106,7 @@
     final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>();
     ShortcutServiceInternal mShortcutServiceInternal;
     SoundTriggerInternal mSoundTriggerInternal;
+    private boolean mAllowInstantService;
 
     private final RemoteCallbackList<IVoiceInteractionSessionListener>
             mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
@@ -170,6 +173,14 @@
         mServiceStub.switchUser(userHandle);
     }
 
+    // Called by Shell cmd
+    void setAllowInstantService(boolean allowed) {
+        Log.i(TAG, "setAllowInstantService(): " + allowed);
+        mContext.enforceCallingPermission(Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+                "setAllowInstantService");
+        mAllowInstantService = allowed;
+    }
+
     class LocalService extends VoiceInteractionManagerInternal {
         @Override
         public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
@@ -223,6 +234,7 @@
                         new IVoiceInteractionSessionShowCallback.Stub() {
                             @Override
                             public void onFailed() {
+                                Slog.w(TAG, "startLocalVoiceInteraction() failed");
                             }
 
                             @Override
@@ -271,7 +283,10 @@
         }
 
         public void initForUser(int userHandle) {
-            if (DEBUG) Slog.d(TAG, "**************** initForUser user=" + userHandle);
+            if (DEBUG) {
+                Slog.d(TAG, "**************** initForUser user=" + userHandle
+                        + ", allowInstantService=" + mAllowInstantService);
+            }
             String curInteractorStr = Settings.Secure.getStringForUser(
                     mContext.getContentResolver(),
                     Settings.Secure.VOICE_INTERACTION_SERVICE, userHandle);
@@ -430,6 +445,7 @@
             if (!mSafeMode) {
                 String curService = Settings.Secure.getStringForUser(
                         mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
+                if (DEBUG) Log.d(TAG, "Service for user " + mCurUser + ": " + curService);
                 ComponentName serviceComponent = null;
                 ServiceInfo serviceInfo = null;
                 if (curService != null && !curService.isEmpty()) {
@@ -466,7 +482,8 @@
                     }
                     if (hasComponent) {
                         setImplLocked(new VoiceInteractionManagerServiceImpl(mContext,
-                                UiThread.getHandler(), this, mCurUser, serviceComponent));
+                                UiThread.getHandler(), this, mCurUser, serviceComponent,
+                                mAllowInstantService));
                         mImpl.startLocked();
                     } else {
                         setImplLocked(null);
@@ -1227,16 +1244,26 @@
             synchronized (this) {
                 pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)");
                 pw.println("  mEnableService: " + mEnableService);
+                pw.println("  mAllowInstantService: " + mAllowInstantService);
                 if (mImpl == null) {
                     pw.println("  (No active implementation)");
                     return;
                 }
                 mImpl.dumpLocked(fd, pw, args);
             }
+
             mSoundTriggerInternal.dump(fd, pw, args);
         }
 
         @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+                throws RemoteException {
+            new VoiceInteractionManagerServiceShellCommand(VoiceInteractionManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+
+        @Override
         public void setUiHints(IVoiceInteractionService service, Bundle hints) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService(service);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 2a5b70b..4881ade 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -29,7 +29,6 @@
 import android.app.ActivityTaskManager;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
-import android.app.IApplicationThread;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -48,7 +47,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionServiceInfo;
-import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.IWindowManager;
@@ -82,6 +80,7 @@
     final VoiceInteractionServiceInfo mInfo;
     final ComponentName mSessionComponentName;
     final IWindowManager mIWindowManager;
+    final boolean mAllowInstant;
     boolean mBound = false;
     IVoiceInteractionService mService;
 
@@ -127,7 +126,7 @@
 
     VoiceInteractionManagerServiceImpl(Context context, Handler handler,
             VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
-            int userHandle, ComponentName service) {
+            int userHandle, ComponentName service, boolean allowInstant) {
         mContext = context;
         mHandler = handler;
         mServiceStub = stub;
@@ -135,6 +134,7 @@
         mComponent = service;
         mAm = ActivityManager.getService();
         mAtm = ActivityTaskManager.getService();
+        mAllowInstant = allowInstant;
         VoiceInteractionServiceInfo info;
         try {
             info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
@@ -169,7 +169,7 @@
         if (mActiveSession == null) {
             mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
                     mSessionComponentName, mUser, mContext, this,
-                    mInfo.getServiceInfo().applicationInfo.uid, mHandler);
+                    mInfo.getServiceInfo().applicationInfo.uid, mHandler, mAllowInstant);
         }
         List<IBinder> activityTokens = null;
         if (activityToken != null) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
new file mode 100644
index 0000000..de1191f
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.voiceinteraction;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+final class VoiceInteractionManagerServiceShellCommand extends ShellCommand {
+
+    private final VoiceInteractionManagerService mService;
+
+    VoiceInteractionManagerServiceShellCommand(@NonNull VoiceInteractionManagerService service) {
+        this.mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "set bind-instant-service-allowed":
+            case "set":
+                return requestSet(pw);
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter();) {
+            pw.println("VoiceInteraction Service (voiceinteraction) commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  set bind-instant-service-allowed [true | false]");
+            pw.println("    Sets whether binding to services provided by instant apps is allowed");
+            pw.println("");
+        }
+    }
+
+    private int requestSet(@NonNull PrintWriter pw) {
+        final String what = getNextArgRequired();
+
+        switch(what) {
+            case "bind-instant-service-allowed":
+                return setBindInstantService(pw);
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
+        }
+    }
+
+    private int setBindInstantService(@NonNull PrintWriter pw) {
+        final String mode = getNextArgRequired();
+        switch (mode.toLowerCase()) {
+            case "true":
+                mService.setAllowInstantService(true);
+                return 0;
+            case "false":
+                mService.setAllowInstantService(false);
+                return 0;
+            default:
+                pw.println("Invalid mode: " + mode);
+                return -1;
+        }
+    }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index f16dec6..3c47044 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -138,7 +138,8 @@
     };
 
     public VoiceInteractionSessionConnection(Object lock, ComponentName component, int user,
-            Context context, Callback callback, int callingUid, Handler handler) {
+            Context context, Callback callback, int callingUid, Handler handler,
+            boolean allowInstant) {
         mLock = lock;
         mSessionComponentName = component;
         mUser = user;
@@ -159,10 +160,13 @@
         mPermissionOwner = permOwner;
         mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
         mBindIntent.setComponent(mSessionComponentName);
-        mBound = mContext.bindServiceAsUser(mBindIntent, this,
-                Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
-                        | Context.BIND_ALLOW_OOM_MANAGEMENT
-                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
+        int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
+                | Context.BIND_ALLOW_OOM_MANAGEMENT
+                | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
+        if (allowInstant) {
+            flags |= Context.BIND_ALLOW_INSTANT;
+        }
+        mBound = mContext.bindServiceAsUser(mBindIntent, this, flags, new UserHandle(mUser));
         if (mBound) {
             try {
                 mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY);
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 49b34b3..c66e92b 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1937,6 +1937,8 @@
             return;
         }
 
+        String callingPackage = getOpPackageName();
+
         mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() {
             @Override
             public void onResult(
@@ -1965,7 +1967,7 @@
                     }
                 }.prepare());
             }
-        });
+        }, callingPackage);
     }
 
     /**
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 6c3f4f3..3acd83a 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -316,11 +316,11 @@
     /**
      * Retrieves a list of remote connection services usable to place calls.
      */
-    void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+    void queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage) {
         // Only supported when there is only one adapter.
         if (mAdapters.size() == 1) {
             try {
-                mAdapters.iterator().next().queryRemoteConnectionServices(callback,
+                mAdapters.iterator().next().queryRemoteConnectionServices(callback, callingPackage,
                         Log.getExternalSession());
             } catch (RemoteException e) {
                 Log.e(this, e, "Exception trying to query for remote CSs");
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index f99b218..b281589 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -186,8 +186,13 @@
                     break;
                 }
                 case MSG_QUERY_REMOTE_CALL_SERVICES:
-                    mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj,
-                            null /*Session.Info*/);
+                    SomeArgs args2 = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) args2.arg1,
+                                (String) args2.arg2, null /*Session.Info*/);
+                    } finally {
+                        args2.recycle();
+                    }
                     break;
                 case MSG_SET_VIDEO_STATE:
                     mDelegate.setVideoState((String) msg.obj, msg.arg1, null /*Session.Info*/);
@@ -468,7 +473,10 @@
 
         @Override
         public void queryRemoteConnectionServices(RemoteServiceCallback callback,
-                Session.Info sessionInfo) {
+                String callingPackage, Session.Info sessionInfo) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callback;
+            args.arg2 = callingPackage;
             mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
         }
 
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 744544e..1e73bd6 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -288,7 +288,7 @@
 
         @Override
         public void queryRemoteConnectionServices(RemoteServiceCallback callback,
-                Session.Info sessionInfo) {
+                String callingPackage, Session.Info sessionInfo) {
             // Not supported from remote connection service.
         }
 
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 76ac88e..9cf098c 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -78,7 +78,8 @@
 
     void onPostDialChar(String callId, char nextChar, in Session.Info sessionInfo);
 
-    void queryRemoteConnectionServices(RemoteServiceCallback callback, in Session.Info sessionInfo);
+    void queryRemoteConnectionServices(RemoteServiceCallback callback, String callingPackage,
+    in Session.Info sessionInfo);
 
     void setVideoProvider(String callId, IVideoProvider videoProvider, in Session.Info sessionInfo);
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6ba359b..5175c1d 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2794,14 +2794,6 @@
          */
         public static final String KEY_NFW_PROXY_APPS_STRING = KEY_PREFIX + "nfw_proxy_apps";
 
-        /**
-         * Specify whether to post a notification on the status bar whenever device location is
-         * provided for non-framework location requests in user-initiated emergency use cases.
-         * 0 - Do not post notification. This is default.
-         * 1 - Post notification for all request types.
-         */
-        public static final String KEY_ES_NOTIFY_INT = KEY_PREFIX + "es_notify_int";
-
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -2816,7 +2808,6 @@
             defaults.putString(KEY_GPS_LOCK_STRING, "3");
             defaults.putString(KEY_ES_EXTENSION_SEC_STRING, "0");
             defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
-            defaults.putInt(KEY_ES_NOTIFY_INT, 0);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 1551ce3..984d8f7 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -337,7 +337,7 @@
      * Reference: 3GPP TS 36.104 5.4.3 */
     private int mLteEarfcnRsrpBoost = 0;
 
-    private List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>();
+    private final List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>();
 
     private String mOperatorAlphaLongRaw;
     private String mOperatorAlphaShortRaw;
@@ -420,8 +420,10 @@
         mCellBandwidths = s.mCellBandwidths == null ? null :
                 Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
         mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
-        mNetworkRegistrationInfos = s.mNetworkRegistrationInfos == null ? null :
-                s.getNetworkRegistrationInfoList();
+        synchronized (mNetworkRegistrationInfos) {
+            mNetworkRegistrationInfos.clear();
+            mNetworkRegistrationInfos.addAll(s.getNetworkRegistrationInfoList());
+        }
         mNrFrequencyRange = s.mNrFrequencyRange;
         mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
         mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw;
@@ -453,8 +455,9 @@
         mCdmaEriIconMode = in.readInt();
         mIsEmergencyOnly = in.readInt() != 0;
         mLteEarfcnRsrpBoost = in.readInt();
-        mNetworkRegistrationInfos = new ArrayList<>();
-        in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader());
+        synchronized (mNetworkRegistrationInfos) {
+            in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader());
+        }
         mChannelNumber = in.readInt();
         mCellBandwidths = in.createIntArray();
         mNrFrequencyRange = in.readInt();
@@ -481,7 +484,9 @@
         out.writeInt(mCdmaEriIconMode);
         out.writeInt(mIsEmergencyOnly ? 1 : 0);
         out.writeInt(mLteEarfcnRsrpBoost);
-        out.writeList(mNetworkRegistrationInfos);
+        synchronized (mNetworkRegistrationInfos) {
+            out.writeList(mNetworkRegistrationInfos);
+        }
         out.writeInt(mChannelNumber);
         out.writeIntArray(mCellBandwidths);
         out.writeInt(mNrFrequencyRange);
@@ -823,31 +828,33 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(
-                mVoiceRegState,
-                mDataRegState,
-                mChannelNumber,
-                Arrays.hashCode(mCellBandwidths),
-                mVoiceOperatorAlphaLong,
-                mVoiceOperatorAlphaShort,
-                mVoiceOperatorNumeric,
-                mDataOperatorAlphaLong,
-                mDataOperatorAlphaShort,
-                mDataOperatorNumeric,
-                mIsManualNetworkSelection,
-                mCssIndicator,
-                mNetworkId,
-                mSystemId,
-                mCdmaRoamingIndicator,
-                mCdmaDefaultRoamingIndicator,
-                mCdmaEriIconIndex,
-                mCdmaEriIconMode,
-                mIsEmergencyOnly,
-                mLteEarfcnRsrpBoost,
-                mNetworkRegistrationInfos,
-                mNrFrequencyRange,
-                mOperatorAlphaLongRaw,
-                mOperatorAlphaShortRaw);
+        synchronized (mNetworkRegistrationInfos) {
+            return Objects.hash(
+                    mVoiceRegState,
+                    mDataRegState,
+                    mChannelNumber,
+                    Arrays.hashCode(mCellBandwidths),
+                    mVoiceOperatorAlphaLong,
+                    mVoiceOperatorAlphaShort,
+                    mVoiceOperatorNumeric,
+                    mDataOperatorAlphaLong,
+                    mDataOperatorAlphaShort,
+                    mDataOperatorNumeric,
+                    mIsManualNetworkSelection,
+                    mCssIndicator,
+                    mNetworkId,
+                    mSystemId,
+                    mCdmaRoamingIndicator,
+                    mCdmaDefaultRoamingIndicator,
+                    mCdmaEriIconIndex,
+                    mCdmaEriIconMode,
+                    mIsEmergencyOnly,
+                    mLteEarfcnRsrpBoost,
+                    mNetworkRegistrationInfos,
+                    mNrFrequencyRange,
+                    mOperatorAlphaLongRaw,
+                    mOperatorAlphaShortRaw);
+        }
     }
 
     @Override
@@ -855,30 +862,31 @@
         if (!(o instanceof ServiceState)) return false;
         ServiceState s = (ServiceState) o;
 
-        return mVoiceRegState == s.mVoiceRegState
-                && mDataRegState == s.mDataRegState
-                && mIsManualNetworkSelection == s.mIsManualNetworkSelection
-                && mChannelNumber == s.mChannelNumber
-                && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
-                && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
-                && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
-                && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
-                && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
-                && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
-                && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
-                && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
-                && equalsHandlesNulls(mNetworkId, s.mNetworkId)
-                && equalsHandlesNulls(mSystemId, s.mSystemId)
-                && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
-                && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
-                        s.mCdmaDefaultRoamingIndicator)
-                && mIsEmergencyOnly == s.mIsEmergencyOnly
-                && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw)
-                && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw)
-                && (mNetworkRegistrationInfos == null
-                ? s.mNetworkRegistrationInfos == null : s.mNetworkRegistrationInfos != null
-                && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos))
-                && mNrFrequencyRange == s.mNrFrequencyRange;
+        synchronized (mNetworkRegistrationInfos) {
+            return mVoiceRegState == s.mVoiceRegState
+                    && mDataRegState == s.mDataRegState
+                    && mIsManualNetworkSelection == s.mIsManualNetworkSelection
+                    && mChannelNumber == s.mChannelNumber
+                    && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
+                    && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
+                    && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
+                    && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
+                    && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
+                    && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
+                    && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
+                    && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
+                    && equalsHandlesNulls(mNetworkId, s.mNetworkId)
+                    && equalsHandlesNulls(mSystemId, s.mSystemId)
+                    && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
+                    && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
+                    s.mCdmaDefaultRoamingIndicator)
+                    && mIsEmergencyOnly == s.mIsEmergencyOnly
+                    && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw)
+                    && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw)
+                    && mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size()
+                    && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)
+                    && mNrFrequencyRange == s.mNrFrequencyRange;
+        }
     }
 
     /**
@@ -1005,36 +1013,38 @@
 
     @Override
     public String toString() {
-        return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
-            .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
-            .append(", mDataRegState=").append(mDataRegState)
-            .append("(" + rilServiceStateToString(mDataRegState) + ")")
-            .append(", mChannelNumber=").append(mChannelNumber)
-            .append(", duplexMode()=").append(getDuplexMode())
-            .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
-            .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
-            .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
-            .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
-            .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
-            .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
-            .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
-            .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
-            .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")")
-            .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology())
-            .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")")
-            .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
-            .append(", mNetworkId=").append(mNetworkId)
-            .append(", mSystemId=").append(mSystemId)
-            .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
-            .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
-            .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
-            .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation())
-            .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
-            .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos)
-            .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
-            .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
-            .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
-            .append("}").toString();
+        synchronized (mNetworkRegistrationInfos) {
+            return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
+                    .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
+                    .append(", mDataRegState=").append(mDataRegState)
+                    .append("(" + rilServiceStateToString(mDataRegState) + ")")
+                    .append(", mChannelNumber=").append(mChannelNumber)
+                    .append(", duplexMode()=").append(getDuplexMode())
+                    .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
+                    .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
+                    .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
+                    .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
+                    .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+                    .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
+                    .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
+                    .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
+                    .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")")
+                    .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology())
+                    .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")")
+                    .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
+                    .append(", mNetworkId=").append(mNetworkId)
+                    .append(", mSystemId=").append(mSystemId)
+                    .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
+                    .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
+                    .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
+                    .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation())
+                    .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
+                    .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos)
+                    .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
+                    .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
+                    .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
+                    .append("}").toString();
+        }
     }
 
     private void init() {
@@ -1060,17 +1070,19 @@
         mIsEmergencyOnly = false;
         mLteEarfcnRsrpBoost = 0;
         mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN;
-        mNetworkRegistrationInfos.clear();
-        addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
-                .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
-                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
-                .build());
-        addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
-                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
-                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
-                .build());
+        synchronized (mNetworkRegistrationInfos) {
+            mNetworkRegistrationInfos.clear();
+            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                    .build());
+            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                    .build());
+        }
         mOperatorAlphaLongRaw = null;
         mOperatorAlphaShortRaw = null;
     }
@@ -1913,10 +1925,13 @@
      */
     public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
         ServiceState state = new ServiceState(this);
-        if (state.mNetworkRegistrationInfos != null) {
-            state.mNetworkRegistrationInfos = state.mNetworkRegistrationInfos.stream()
-                    .map(NetworkRegistrationInfo::sanitizeLocationInfo)
-                    .collect(Collectors.toList());
+        synchronized (state.mNetworkRegistrationInfos) {
+            List<NetworkRegistrationInfo> networkRegistrationInfos =
+                    state.mNetworkRegistrationInfos.stream()
+                            .map(NetworkRegistrationInfo::sanitizeLocationInfo)
+                            .collect(Collectors.toList());
+            state.mNetworkRegistrationInfos.clear();
+            state.mNetworkRegistrationInfos.addAll(networkRegistrationInfos);
         }
         if (!removeCoarseLocation) return state;
 
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index f31ac2e..1e6cd47 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -735,7 +735,7 @@
         return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " carrierId=" + mCarrierId + " displayName=" + mDisplayName
                 + " carrierName=" + mCarrierName + " nameSource=" + mNameSource
-                + " iconTint=" + mIconTint + " mNumber=" + mNumber
+                + " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber)
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 18e7530..9912ece 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -250,6 +250,8 @@
         out.writeInt(mStatus);
         out.writeString(mIcbNum);
         out.writeInt(mProvisionStatus);
+        out.writeInt(mClirInterrogationStatus);
+        out.writeInt(mClirOutgoingState);
     }
 
     @Override
@@ -273,6 +275,8 @@
         mStatus = in.readInt();
         mIcbNum = in.readString();
         mProvisionStatus = in.readInt();
+        mClirInterrogationStatus = in.readInt();
+        mClirOutgoingState = in.readInt();
     }
 
     public static final @android.annotation.NonNull Creator<ImsSsInfo> CREATOR =
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index a9f10b1..9f22d0a 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -549,4 +549,22 @@
      */
     public void onAppCallbackDied(int uid, int subscriptionId) {
     }
+
+    // Following two methods exist to workaround b/124210145
+    /** @hide */
+    @SystemApi
+    @TestApi
+    @Override
+    public android.os.IBinder asBinder() {
+        return super.asBinder();
+    }
+
+    /** @hide */
+    @SystemApi
+    @TestApi
+    @Override
+    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
+            int flags) throws RemoteException {
+        return super.onTransact(code, data, reply, flags);
+    }
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 5ce612d..cced447 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -294,4 +294,23 @@
      */
     public void onAppCallbackDied(int uid, int subscriptionId) {
     }
+
+
+    // Following two methods exist to workaround b/124210145
+    /** @hide */
+    @SystemApi
+    @TestApi
+    @Override
+    public android.os.IBinder asBinder() {
+        return super.asBinder();
+    }
+
+    /** @hide */
+    @SystemApi
+    @TestApi
+    @Override
+    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
+            int flags) throws RemoteException {
+        return super.onTransact(code, data, reply, flags);
+    }
 }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 3b0e2a5..b3a9f49 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -21,13 +21,19 @@
 import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
 
 import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.pm.VersionedPackage;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 
+import androidx.test.InstrumentationRegistry;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,6 +56,7 @@
     private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
     private static final String TEST_APP_A_V1 = "RollbackTestAppAv1.apk";
     private static final String TEST_APP_A_V2 = "RollbackTestAppAv2.apk";
+    private static final String TEST_APP_A_CRASHING_V2 = "RollbackTestAppACrashingV2.apk";
     private static final String TEST_APEX_PKG = "com.android.tests.rollback.testapex";
     private static final String TEST_APEX_V1 =
             "com.android.tests.rollback.testapex.RollbackTestApexV1.apex";
@@ -66,7 +73,8 @@
         RollbackTestUtils.adoptShellPermissionIdentity(
                 Manifest.permission.INSTALL_PACKAGES,
                 Manifest.permission.DELETE_PACKAGES,
-                Manifest.permission.TEST_MANAGE_ROLLBACKS);
+                Manifest.permission.TEST_MANAGE_ROLLBACKS,
+                Manifest.permission.KILL_BACKGROUND_PROCESSES);
     }
 
     /**
@@ -112,11 +120,12 @@
         assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
         assertTrue(rollback.isStaged());
 
-        RollbackTestUtils.rollback(rollback.getRollbackId());
+        RollbackTestUtils.rollback(rollback.getRollbackId(),
+                new VersionedPackage(TEST_APP_A, 2));
 
         rollback = getUniqueRollbackInfoForPackage(
                 rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
-        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, new VersionedPackage(TEST_APP_A, 2));
         assertTrue(rollback.isStaged());
         assertNotEquals(-1, rollback.getCommittedSessionId());
 
@@ -130,6 +139,72 @@
     }
 
     /**
+     * Test rollbacks of staged installs involving only apks with bad update.
+     * Enable rollback phase.
+     */
+    @Test
+    public void testBadApkOnlyEnableRollback() throws Exception {
+        RollbackTestUtils.uninstall(TEST_APP_A);
+        assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+        RollbackTestUtils.install(TEST_APP_A_V1, false);
+        assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+        RollbackTestUtils.processUserData(TEST_APP_A);
+
+        RollbackTestUtils.installStaged(true, TEST_APP_A_CRASHING_V2);
+
+        // At this point, the host test driver will reboot the device and run
+        // testBadApkOnlyConfirmEnableRollback().
+    }
+
+    /**
+     * Test rollbacks of staged installs involving only apks with bad update.
+     * Confirm that rollback was successfully enabled.
+     */
+    @Test
+    public void testBadApkOnlyConfirmEnableRollback() throws Exception {
+        assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+        RollbackTestUtils.processUserData(TEST_APP_A);
+
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+        RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                rm.getAvailableRollbacks(), TEST_APP_A);
+        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+        assertTrue(rollback.isStaged());
+
+        // At this point, the host test driver will run
+        // testBadApkOnlyTriggerRollback().
+    }
+
+    /**
+     * Test rollbacks of staged installs involving only apks with bad update.
+     * Trigger rollback phase. This is expected to fail due to watchdog
+     * rebooting the test out from under it.
+     */
+    @Test
+    public void testBadApkOnlyTriggerRollback() throws Exception {
+        BroadcastReceiver crashCountReceiver = null;
+        Context context = InstrumentationRegistry.getContext();
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+        try {
+            // Crash TEST_APP_A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
+            crashCountReceiver = RollbackTestUtils.sendCrashBroadcast(context, TEST_APP_A, 5);
+        } finally {
+            if (crashCountReceiver != null) {
+                context.unregisterReceiver(crashCountReceiver);
+            }
+        }
+
+        // We expect the device to be rebooted automatically. Wait for that to
+        // happen. At that point, the host test driver will wait for the
+        // device to come back up and run testApkOnlyConfirmRollback().
+        Thread.sleep(30 * 1000);
+
+        fail("watchdog did not trigger reboot");
+    }
+
+    /**
      * Test rollbacks of staged installs involving only apks.
      * Confirm rollback phase.
      */
@@ -141,7 +216,7 @@
         RollbackManager rm = RollbackTestUtils.getRollbackManager();
         RollbackInfo rollback = getUniqueRollbackInfoForPackage(
                 rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
-        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+        assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback, new VersionedPackage(TEST_APP_A, 2));
         assertTrue(rollback.isStaged());
         assertNotEquals(-1, rollback.getCommittedSessionId());
     }
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 1f87ed8..2433434 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -18,7 +18,9 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.ddmlib.Log.LogLevel;
 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;
 
@@ -58,6 +60,29 @@
     }
 
     /**
+     * Tests watchdog triggered staged rollbacks involving only apks.
+     */
+    @Test
+    public void testBadApkOnly() throws Exception {
+        runPhase("testBadApkOnlyEnableRollback");
+        getDevice().reboot();
+        runPhase("testBadApkOnlyConfirmEnableRollback");
+        try {
+            // This is expected to fail due to the device being rebooted out
+            // from underneath the test. If this fails for reasons other than
+            // the device reboot, those failures should result in failure of
+            // the testApkOnlyConfirmRollback phase.
+            CLog.logAndDisplay(LogLevel.INFO, "testBadApkOnlyTriggerRollback is expected to fail");
+            runPhase("testBadApkOnlyTriggerRollback");
+        } catch (AssertionError e) {
+            // AssertionError is expected.
+        }
+
+        getDevice().waitForDeviceAvailable();
+        runPhase("testApkOnlyConfirmRollback");
+    }
+
+    /**
      * Tests staged rollbacks involving only apex.
      */
     @Test
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 07525a6..db1ccb4 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -24,6 +24,7 @@
         "frameworks-net-testutils",
         "junit",
         "mockito-target-minus-junit4",
+        "platform-test-annotations",
     ],
     libs: [
         "android.test.base.stubs",
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java
index bef66b2..38bc744 100644
--- a/tests/net/common/java/android/net/NetworkTest.java
+++ b/tests/net/common/java/android/net/NetworkTest.java
@@ -25,6 +25,7 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.net.Network;
+import android.platform.test.annotations.AppModeFull;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -74,6 +75,7 @@
     }
 
     @Test
+    @AppModeFull(reason = "Socket cannot bind in instant app mode")
     public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception {
         final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY);
         mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53);
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
index 17a3c7a..974166a 100644
--- a/tools/incident_report/main.cpp
+++ b/tools/incident_report/main.cpp
@@ -292,6 +292,7 @@
     fprintf(out, "Take an incident report over adb (which must be in the PATH).\n");
     fprintf(out, "  -b          output the incident report raw protobuf format\n");
     fprintf(out, "  -o OUTPUT   the output file. OUTPUT may be '-' or omitted to use stdout\n");
+    fprintf(out, "  -r REASON   human readable description of why the report is taken.\n");
     fprintf(out, "  -s SERIAL   sent to adb to choose which device, instead of $ANDROID_SERIAL\n");
     fprintf(out, "  -t          output the incident report in pretty-printed text format\n");
     fprintf(out, "\n");
@@ -307,13 +308,14 @@
     enum { OUTPUT_TEXT, OUTPUT_PROTO } outputFormat = OUTPUT_TEXT;
     const char* inFilename = NULL;
     const char* outFilename = NULL;
+    const char* reason = NULL;
     const char* adbSerial = NULL;
     pid_t childPid = -1;
     vector<string> sections;
     const char* privacy = NULL;
 
     int opt;
-    while ((opt = getopt(argc, argv, "bhi:o:s:twp:")) != -1) {
+    while ((opt = getopt(argc, argv, "bhi:o:r:s:twp:")) != -1) {
         switch (opt) {
             case 'b':
                 outputFormat = OUTPUT_PROTO;
@@ -324,6 +326,9 @@
             case 'o':
                 outFilename = optarg;
                 break;
+            case 'r':
+                reason = optarg;
+                break;
             case 's':
                 adbSerial = optarg;
                 break;
@@ -376,7 +381,7 @@
             dup2(pfd[1], STDOUT_FILENO);
             close(pfd[0]);
             close(pfd[1]);
-            char const** args = (char const**)malloc(sizeof(char*) * (8 + sections.size()));
+            char const** args = (char const**)malloc(sizeof(char*) * (10 + sections.size()));
             int argpos = 0;
             args[argpos++] = "adb";
             if (adbSerial != NULL) {
@@ -389,6 +394,10 @@
                 args[argpos++] = "-p";
                 args[argpos++] = privacy;
             }
+            if (reason != NULL) {
+                args[argpos++] = "-r";
+                args[argpos++] = reason;
+            }
             for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) {
                 args[argpos++] = it->c_str();
             }