Merge "Revert "Fix drag and drop (1/3)"" into qt-qpr1-dev
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1c7180f..b665a8b 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -266,7 +266,9 @@
                     IResultReceiver::asInterface(data.readStrongBinder());
 
             err = command(in, out, err, args, resultReceiver);
-            resultReceiver->send(err);
+            if (resultReceiver != nullptr) {
+                resultReceiver->send(err);
+            }
             return NO_ERROR;
         }
         default: { return BnStatsManager::onTransact(code, data, reply, flags); }
@@ -411,13 +413,20 @@
             return cmd_trigger_active_config_broadcast(out, args);
         }
         if (!args[0].compare(String8("data-subscribe"))) {
-            if (mShellSubscriber == nullptr) {
-                mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
+            {
+                std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
+                if (mShellSubscriber == nullptr) {
+                    mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
+                }
             }
             int timeoutSec = -1;
             if (argCount >= 2) {
                 timeoutSec = atoi(args[1].c_str());
             }
+            if (resultReceiver == nullptr) {
+                ALOGI("Null resultReceiver given, no subscription will be started");
+                return UNEXPECTED_NULL;
+            }
             mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
             return NO_ERROR;
         }
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 53b6ce9..9490948 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -432,6 +432,10 @@
 
     sp<ShellSubscriber> mShellSubscriber;
 
+    /**
+     * Mutex for setting the shell subscriber
+     */
+    mutable mutex mShellSubscriberMutex;
     std::shared_ptr<LogEventQueue> mEventQueue;
 
     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d7aedab..993db51 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -325,7 +325,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10062
+    // Next: 10067
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -390,6 +390,7 @@
         AppOps app_ops = 10060;
         ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
         VmsClientStats vms_client_stats = 10065;
+        NotificationRemoteViews notification_remote_views = 10066;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -4751,6 +4752,24 @@
     optional ProcessStatsSectionProto proc_stats_section = 1;
 }
 
+// Next Tag: 2
+message PackageRemoteViewInfoProto {
+    optional string package_name = 1;
+    // add per-package additional info here (like channels)
+}
+
+// Next Tag: 2
+message NotificationRemoteViewsProto {
+    repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
+}
+
+/**
+ * Pulled from NotificationManagerService.java
+ */
+message NotificationRemoteViews {
+    optional NotificationRemoteViewsProto notification_remote_views = 1;
+}
+
 message PowerProfileProto {
     optional double cpu_suspend = 1;
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index f430250..893378c 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -271,6 +271,9 @@
         {android::util::VMS_CLIENT_STATS,
          {.additiveFields = {5, 6, 7, 8, 9, 10},
           .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
+        // NotiifcationRemoteViews.
+        {android::util::NOTIFICATION_REMOTE_VIEWS,
+         {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9f51db8..3266898 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -202,4 +202,6 @@
 
     void setPrivateNotificationsAllowed(boolean allow);
     boolean getPrivateNotificationsAllowed();
+
+    long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats);
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2fff595..45e777c 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -44,70 +44,10 @@
  * <p>
  * <b>Device battery life will be significantly affected by the use of this API.</b>
  * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels
- * possible, and be sure to release them as soon as possible.
- * </p><p>
- * The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}.
- * This will create a {@link PowerManager.WakeLock} object.  You can then use methods
- * on the wake lock object to control the power state of the device.
- * </p><p>
- * In practice it's quite simple:
- * {@samplecode
- * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
- * wl.acquire();
- *   ..screen will stay on during this section..
- * wl.release();
- * }
- * </p><p>
- * The following wake lock levels are defined, with varying effects on system power.
- * <i>These levels are mutually exclusive - you may only specify one of them.</i>
+ * possible, and be sure to release them as soon as possible. In most cases,
+ * you'll want to use
+ * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead.
  *
- * <table>
- *     <tr><th>Flag Value</th>
- *     <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
- *
- *     <tr><td>{@link #PARTIAL_WAKE_LOCK}</td>
- *         <td>On*</td> <td>Off</td> <td>Off</td>
- *     </tr>
- *
- *     <tr><td>{@link #SCREEN_DIM_WAKE_LOCK}</td>
- *         <td>On</td> <td>Dim</td> <td>Off</td>
- *     </tr>
- *
- *     <tr><td>{@link #SCREEN_BRIGHT_WAKE_LOCK}</td>
- *         <td>On</td> <td>Bright</td> <td>Off</td>
- *     </tr>
- *
- *     <tr><td>{@link #FULL_WAKE_LOCK}</td>
- *         <td>On</td> <td>Bright</td> <td>Bright</td>
- *     </tr>
- * </table>
- * </p><p>
- * *<i>If you hold a partial wake lock, the CPU will continue to run, regardless of any
- * display timeouts or the state of the screen and even after the user presses the power button.
- * In all other wake locks, the CPU will run, but the user can still put the device to sleep
- * using the power button.</i>
- * </p><p>
- * In addition, you can add two more flags, which affect behavior of the screen only.
- * <i>These flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i></p>
- *
- * <table>
- *     <tr><th>Flag Value</th> <th>Description</th></tr>
- *
- *     <tr><td>{@link #ACQUIRE_CAUSES_WAKEUP}</td>
- *         <td>Normal wake locks don't actually turn on the illumination.  Instead, they cause
- *         the illumination to remain on once it turns on (e.g. from user activity).  This flag
- *         will force the screen and/or keyboard to turn on immediately, when the WakeLock is
- *         acquired.  A typical use would be for notifications which are important for the user to
- *         see immediately.</td>
- *     </tr>
- *
- *     <tr><td>{@link #ON_AFTER_RELEASE}</td>
- *         <td>If this flag is set, the user activity timer will be reset when the WakeLock is
- *         released, causing the illumination to remain on a bit longer.  This can be used to
- *         reduce flicker if you are cycling between wake lock conditions.</td>
- *     </tr>
- * </table>
  * <p>
  * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK}
  * permission in an {@code <uses-permission>} element of the application's manifest.
@@ -914,7 +854,8 @@
      * {@link #FULL_WAKE_LOCK}, {@link #SCREEN_DIM_WAKE_LOCK}
      * and {@link #SCREEN_BRIGHT_WAKE_LOCK}.  Exactly one wake lock level must be
      * specified as part of the {@code levelAndFlags} parameter.
-     * </p><p>
+     * </p>
+     * <p>
      * The wake lock flags are: {@link #ACQUIRE_CAUSES_WAKEUP}
      * and {@link #ON_AFTER_RELEASE}.  Multiple flags can be combined as part of the
      * {@code levelAndFlags} parameters.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b9f53480..eb6a539 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -661,6 +661,22 @@
             "android.settings.NIGHT_DISPLAY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Dark theme.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DARK_THEME_SETTINGS =
+            "android.settings.DARK_THEME_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of locale.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -9884,14 +9900,35 @@
          * List of ISO country codes in which eUICC UI is shown. Country codes should be separated
          * by comma.
          *
-         * <p>Used to hide eUICC UI from users who are currently in countries no carriers support
-         * eUICC.
+         * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link
+         * #EUICC_UNSUPPORTED_COUNTRIES} is used.
+         *
+         * <p>Used to hide eUICC UI from users who are currently in countries where no carriers
+         * support eUICC.
+         *
          * @hide
          */
         //TODO(b/77914569) Changes this to System Api.
         public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
 
         /**
+         * List of ISO country codes in which eUICC UI is not shown. Country codes should be
+         * separated by comma.
+         *
+         * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link
+         * #EUICC_UNSUPPORTED_COUNTRIES} is used.
+         *
+         * <p>Used to hide eUICC UI from users who are currently in countries where no carriers
+         * support eUICC.
+         *
+         * @hide
+         */
+        //TODO(b/77914569) Changes this to System Api.
+        public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
+        private static final Validator EUICC_UNSUPPORTED_COUNTRIES_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(",");
+
+        /**
          * Whether any activity can be resized. When this is true, any
          * activity, regardless of manifest values, can be resized for multi-window.
          * (0 = false, 1 = true)
@@ -13953,6 +13990,7 @@
             VALIDATORS.put(DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                     DYNAMIC_POWER_SAVINGS_VALIDATOR);
             VALIDATORS.put(BLUETOOTH_ON, BLUETOOTH_ON_VALIDATOR);
+            VALIDATORS.put(EUICC_UNSUPPORTED_COUNTRIES, EUICC_UNSUPPORTED_COUNTRIES_VALIDATOR);
             VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR);
             VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR);
             VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 6a29d48..5d00370 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -242,6 +242,7 @@
         synchronized (mLock) {
             if (mDestroyed) return;
             if (mUpdateCalled) {
+                mFillView.setOnClickListener(null);
                 hide();
                 mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
             }
diff --git a/core/java/android/view/CompositionSamplingListener.java b/core/java/android/view/CompositionSamplingListener.java
index e4309a6..f23bb40 100644
--- a/core/java/android/view/CompositionSamplingListener.java
+++ b/core/java/android/view/CompositionSamplingListener.java
@@ -29,7 +29,7 @@
  */
 public abstract class CompositionSamplingListener {
 
-    private final long mNativeListener;
+    private long mNativeListener;
     private final Executor mExecutor;
 
     public CompositionSamplingListener(Executor executor) {
@@ -37,13 +37,19 @@
         mNativeListener = nativeCreate(this);
     }
 
+    public void destroy() {
+        if (mNativeListener == 0) {
+            return;
+        }
+        unregister(this);
+        nativeDestroy(mNativeListener);
+        mNativeListener = 0;
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
-            if (mNativeListener != 0) {
-                unregister(this);
-                nativeDestroy(mNativeListener);
-            }
+            destroy();
         } finally {
             super.finalize();
         }
@@ -59,6 +65,9 @@
      */
     public static void register(CompositionSamplingListener listener,
             int displayId, IBinder stopLayer, Rect samplingArea) {
+        if (listener.mNativeListener == 0) {
+            return;
+        }
         Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
                 "default display only for now");
         nativeRegister(listener.mNativeListener, stopLayer, samplingArea.left, samplingArea.top,
@@ -69,6 +78,9 @@
      * Unregisters a sampling listener.
      */
     public static void unregister(CompositionSamplingListener listener) {
+        if (listener.mNativeListener == 0) {
+            return;
+        }
         nativeUnregister(listener.mNativeListener);
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ce3f403..1ee6120 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16131,7 +16131,7 @@
      * by the most recent call to {@link #measure(int, int)}.  This result is a bit mask
      * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}.
      * This should be used during measurement and layout calculations only. Use
-     * {@link #getHeight()} to see how wide a view is after layout.
+     * {@link #getHeight()} to see how high a view is after layout.
      *
      * @return The measured height of this view as a bit mask.
      */
diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
index 6b90588..3164567 100644
--- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
+++ b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
@@ -60,7 +60,9 @@
     private boolean mParsed = true;
 
     public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) {
-        mAppContext = Preconditions.checkNotNull(context).getApplicationContext();
+        final Context appContext = Preconditions.checkNotNull(context).getApplicationContext();
+        // Some contexts don't have an app context.
+        mAppContext = appContext != null ? appContext : context;
         mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener;
         mSettingsObserver = new SettingsObserver(mAppContext, () -> {
             synchronized (mLock) {
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index aeb99b8..7c527ba 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -21,10 +21,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.UserIdInt;
 import android.app.Person;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.SpannedString;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -316,6 +318,8 @@
         private final List<String> mHints;
         @Nullable
         private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
         @NonNull
         private Bundle mExtras;
 
@@ -340,6 +344,7 @@
             List<String> hints = new ArrayList<>();
             in.readStringList(hints);
             String callingPackageName = in.readString();
+            int userId = in.readInt();
             Bundle extras = in.readBundle();
             Request request = new Request(
                     conversation,
@@ -348,6 +353,7 @@
                     hints,
                     extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
@@ -358,6 +364,7 @@
             parcel.writeInt(mMaxSuggestions);
             parcel.writeStringList(mHints);
             parcel.writeString(mCallingPackageName);
+            parcel.writeInt(mUserId);
             parcel.writeBundle(mExtras);
         }
 
@@ -428,6 +435,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data related to this request.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 9ae0c65..ae9d5c6 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -19,8 +19,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.WidgetType;
 
@@ -127,6 +129,7 @@
     private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
     private @InvocationMethod int mInvocationMethod;
     @Nullable private String mWidgetVersion;
+    private @UserIdInt int mUserId = UserHandle.USER_NULL;
     @Nullable private String mResultId;
     private long mEventTime;
     private long mDurationSinceSessionStart;
@@ -171,6 +174,7 @@
         mEnd = in.readInt();
         mSmartStart = in.readInt();
         mSmartEnd = in.readInt();
+        mUserId = in.readInt();
     }
 
     @Override
@@ -199,6 +203,7 @@
         dest.writeInt(mEnd);
         dest.writeInt(mSmartStart);
         dest.writeInt(mSmartEnd);
+        dest.writeInt(mUserId);
     }
 
     @Override
@@ -401,6 +406,24 @@
     }
 
     /**
+     * Sets the id of this event's user.
+     * <p>
+     * Package-private for SystemTextClassifier's use.
+     */
+    void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
+    /**
+     * Returns the id of this event's user.
+     * @hide
+     */
+    @UserIdInt
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
      * Returns the type of widget that was involved in triggering this event.
      */
     @WidgetType
@@ -426,6 +449,7 @@
         mPackageName = context.getPackageName();
         mWidgetType = context.getWidgetType();
         mWidgetVersion = context.getWidgetVersion();
+        mUserId = context.getUserId();
     }
 
     /**
@@ -612,7 +636,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
-                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
+                mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
                 mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
                 mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
     }
@@ -633,6 +657,7 @@
                 && Objects.equals(mEntityType, other.mEntityType)
                 && Objects.equals(mWidgetVersion, other.mWidgetVersion)
                 && Objects.equals(mPackageName, other.mPackageName)
+                && mUserId == other.mUserId
                 && Objects.equals(mWidgetType, other.mWidgetType)
                 && mInvocationMethod == other.mInvocationMethod
                 && Objects.equals(mResultId, other.mResultId)
@@ -652,12 +677,12 @@
         return String.format(Locale.US,
                 "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
                         + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
-                        + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+                        + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
                         + "durationSincePreviousEvent=%d, eventIndex=%d,"
                         + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
                 mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                 mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
-                mResultId, mEventTime, mDurationSinceSessionStart,
+                mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
                 mDurationSincePreviousEvent, mEventIndex,
                 mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
     }
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 8f8766e..a97c330 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.os.Bundle;
@@ -50,6 +51,10 @@
     private final TextClassificationConstants mSettings;
     private final TextClassifier mFallback;
     private final String mPackageName;
+    // NOTE: Always set this before sending a request to the manager service otherwise the manager
+    // service will throw a remote exception.
+    @UserIdInt
+    private final int mUserId;
     private TextClassificationSessionId mSessionId;
 
     public SystemTextClassifier(Context context, TextClassificationConstants settings)
@@ -60,6 +65,7 @@
         mFallback = context.getSystemService(TextClassificationManager.class)
                 .getTextClassifier(TextClassifier.LOCAL);
         mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
+        mUserId = context.getUserId();
     }
 
     /**
@@ -72,6 +78,7 @@
         Utils.checkMainThread();
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextSelection> callback =
                     new BlockingCallback<>("textselection");
             mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -95,6 +102,7 @@
         Utils.checkMainThread();
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextClassification> callback =
                     new BlockingCallback<>("textclassification");
             mManagerService.onClassifyText(mSessionId, request, callback);
@@ -123,6 +131,7 @@
 
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextLinks> callback =
                     new BlockingCallback<>("textlinks");
             mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -142,6 +151,7 @@
         Utils.checkMainThread();
 
         try {
+            event.setUserId(mUserId);
             mManagerService.onSelectionEvent(mSessionId, event);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -154,6 +164,12 @@
         Utils.checkMainThread();
 
         try {
+            final TextClassificationContext tcContext = event.getEventContext() == null
+                    ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN)
+                            .build()
+                    : event.getEventContext();
+            tcContext.setUserId(mUserId);
+            event.setEventContext(tcContext);
             mManagerService.onTextClassifierEvent(mSessionId, event);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error reporting textclassifier event.", e);
@@ -167,6 +183,7 @@
 
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextLanguage> callback =
                     new BlockingCallback<>("textlanguage");
             mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -187,6 +204,7 @@
 
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<ConversationActions> callback =
                     new BlockingCallback<>("conversation-actions");
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -228,6 +246,7 @@
         printWriter.printPair("mFallback", mFallback);
         printWriter.printPair("mPackageName", mPackageName);
         printWriter.printPair("mSessionId", mSessionId);
+        printWriter.printPair("mUserId", mUserId);
         printWriter.decreaseIndent();
         printWriter.println();
     }
@@ -243,6 +262,7 @@
             @NonNull TextClassificationSessionId sessionId) {
         mSessionId = Preconditions.checkNotNull(sessionId);
         try {
+            classificationContext.setUserId(mUserId);
             mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error starting a new classification session.", e);
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 6321051..975f3ba 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -21,6 +21,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.content.Context;
@@ -35,6 +36,7 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.View.OnClickListener;
@@ -551,6 +553,8 @@
         @Nullable private final ZonedDateTime mReferenceTime;
         @NonNull private final Bundle mExtras;
         @Nullable private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(
                 CharSequence text,
@@ -631,6 +635,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -730,6 +752,7 @@
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
         }
 
@@ -742,11 +765,13 @@
             final ZonedDateTime referenceTime = referenceTimeString == null
                     ? null : ZonedDateTime.parse(referenceTimeString);
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
 
             final Request request = new Request(text, startIndex, endIndex,
                     defaultLocales, referenceTime, extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index 3bf8e9b..db07685 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -18,8 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.view.textclassifier.TextClassifier.WidgetType;
 
 import com.android.internal.util.Preconditions;
@@ -35,6 +37,8 @@
     private final String mPackageName;
     private final String mWidgetType;
     @Nullable private final String mWidgetVersion;
+    @UserIdInt
+    private int mUserId = UserHandle.USER_NULL;
 
     private TextClassificationContext(
             String packageName,
@@ -54,6 +58,24 @@
     }
 
     /**
+     * Sets the id of this context's user.
+     * <p>
+     * Package-private for SystemTextClassifier's use.
+     */
+    void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
+    /**
+     * Returns the id of this context's user.
+     * @hide
+     */
+    @UserIdInt
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
      * Returns the widget type for this classification context.
      */
     @NonNull
@@ -75,8 +97,8 @@
     @Override
     public String toString() {
         return String.format(Locale.US, "TextClassificationContext{"
-                + "packageName=%s, widgetType=%s, widgetVersion=%s}",
-                mPackageName, mWidgetType, mWidgetVersion);
+                + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}",
+                mPackageName, mWidgetType, mWidgetVersion, mUserId);
     }
 
     /**
@@ -133,12 +155,14 @@
         parcel.writeString(mPackageName);
         parcel.writeString(mWidgetType);
         parcel.writeString(mWidgetVersion);
+        parcel.writeInt(mUserId);
     }
 
     private TextClassificationContext(Parcel in) {
         mPackageName = in.readString();
         mWidgetType = in.readString();
         mWidgetVersion = in.readString();
+        mUserId = in.readInt();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index 57da829..a041296 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -139,7 +139,7 @@
     @Nullable
     private final String[] mEntityTypes;
     @Nullable
-    private final TextClassificationContext mEventContext;
+    private TextClassificationContext mEventContext;
     @Nullable
     private final String mResultId;
     private final int mEventIndex;
@@ -289,6 +289,15 @@
     }
 
     /**
+     * Sets the event context.
+     * <p>
+     * Package-private for SystemTextClassifier's use.
+     */
+    void setEventContext(@Nullable TextClassificationContext eventContext) {
+        mEventContext = eventContext;
+    }
+
+    /**
      * Returns the id of the text classifier result related to this event.
      */
     @Nullable
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 6c75ffb..3e3dc72 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -20,10 +20,12 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.icu.util.ULocale;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -226,6 +228,8 @@
         private final CharSequence mText;
         private final Bundle mExtra;
         @Nullable private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(CharSequence text, Bundle bundle) {
             mText = text;
@@ -260,6 +264,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns a bundle containing non-structured extra information about this request.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -278,16 +300,19 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeCharSequence(mText);
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtra);
         }
 
         private static Request readFromParcel(Parcel in) {
             final CharSequence text = in.readCharSequence();
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extra = in.readBundle();
 
             final Request request = new Request(text, extra);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index f3e0dc1..d7ac524 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,11 +20,13 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.Spannable;
 import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
@@ -339,6 +341,8 @@
         private final boolean mLegacyFallback;
         @Nullable private String mCallingPackageName;
         private final Bundle mExtras;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(
                 CharSequence text,
@@ -410,6 +414,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -509,6 +531,7 @@
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeParcelable(mEntityConfig, flags);
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
         }
 
@@ -517,11 +540,13 @@
             final LocaleList defaultLocales = in.readParcelable(null);
             final EntityConfig entityConfig = in.readParcelable(null);
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
 
             final Request request = new Request(text, defaultLocales, entityConfig,
                     /* legacyFallback= */ true, extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 75c27bd..94e0bc3 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -20,10 +20,12 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.textclassifier.TextClassifier.EntityType;
@@ -211,6 +213,8 @@
         private final boolean mDarkLaunchAllowed;
         private final Bundle mExtras;
         @Nullable private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(
                 CharSequence text,
@@ -292,6 +296,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -394,6 +416,7 @@
             dest.writeInt(mEndIndex);
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
         }
 
@@ -403,11 +426,13 @@
             final int endIndex = in.readInt();
             final LocaleList defaultLocales = in.readParcelable(null);
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
 
             final Request request = new Request(text, startIndex, endIndex, defaultLocales,
                     /* darkLaunchAllowed= */ false, extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0918c5f..944bdcd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11283,6 +11283,12 @@
     }
 
     @Nullable
+    final TextClassificationManager getTextClassificationManagerForUser() {
+        return getServiceManagerForUser(
+                getContext().getPackageName(), TextClassificationManager.class);
+    }
+
+    @Nullable
     final <T> T getServiceManagerForUser(String packageName, Class<T> managerClazz) {
         if (mTextOperationUser == null) {
             return getContext().getSystemService(managerClazz);
@@ -12383,8 +12389,7 @@
     @NonNull
     public TextClassifier getTextClassifier() {
         if (mTextClassifier == null) {
-            final TextClassificationManager tcm =
-                    mContext.getSystemService(TextClassificationManager.class);
+            final TextClassificationManager tcm = getTextClassificationManagerForUser();
             if (tcm != null) {
                 return tcm.getTextClassifier();
             }
@@ -12400,8 +12405,7 @@
     @NonNull
     TextClassifier getTextClassificationSession() {
         if (mTextClassificationSession == null || mTextClassificationSession.isDestroyed()) {
-            final TextClassificationManager tcm =
-                    mContext.getSystemService(TextClassificationManager.class);
+            final TextClassificationManager tcm = getTextClassificationManagerForUser();
             if (tcm != null) {
                 final String widgetType;
                 if (isTextEditable()) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index efa0432..d34174c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -54,6 +54,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
@@ -815,12 +816,19 @@
         return new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
-                mAdapter.handlePackagesChanged();
-                bindProfileView();
+                handlePackagesChanged();
             }
         };
     }
 
+    /**
+     * Update UI to reflect changes in data.
+     */
+    public void handlePackagesChanged() {
+        mAdapter.handlePackagesChanged();
+        bindProfileView();
+    }
+
     private void onCopyButtonClicked(View v) {
         Intent targetIntent = getTargetIntent();
         if (targetIntent == null) {
@@ -924,20 +932,38 @@
 
         final Intent resolveIntent = new Intent();
         resolveIntent.setComponent(cn);
-        final ResolveInfo ri = getPackageManager().resolveActivity(resolveIntent, 0);
-        if (ri == null) {
+        final ResolveInfo ri = getPackageManager().resolveActivity(
+                resolveIntent, PackageManager.GET_META_DATA);
+        if (ri == null || ri.activityInfo == null) {
             Log.e(TAG, "Device-specified nearby sharing component (" + cn
                     + ") not available");
             return null;
         }
 
-        // TODO(b/144290152): CHIP_LABEL_METADATA_KEY / CHIP_ICON_METADATA_KEY
-
-        CharSequence name = ri.loadLabel(getPackageManager());
+        // Allow the nearby sharing component to provide a more appropriate icon and label
+        // for the chip.
+        CharSequence name = null;
+        Drawable icon = null;
+        final Bundle metaData = ri.activityInfo.metaData;
+        if (metaData != null) {
+            try {
+                final Resources pkgRes = getPackageManager().getResourcesForActivity(cn);
+                final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY);
+                name = pkgRes.getString(nameResId);
+                final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY);
+                icon = pkgRes.getDrawable(resId);
+            } catch (NameNotFoundException ex) { }
+        }
+        if (TextUtils.isEmpty(name)) {
+            name = ri.loadLabel(getPackageManager());
+        }
+        if (icon == null) {
+            icon = ri.loadIcon(getPackageManager());
+        }
 
         final DisplayResolveInfo dri = new DisplayResolveInfo(
                 originalIntent, ri, name, "", null);
-        dri.setDisplayIcon(ri.loadIcon(getPackageManager()));
+        dri.setDisplayIcon(icon);
         return dri;
     }
 
@@ -969,7 +995,10 @@
         return createActionButton(
                 ti.getDisplayIcon(),
                 ti.getDisplayLabel(),
-                (View unused) -> safelyStartActivity(ti)
+                (View unused) -> {
+                    safelyStartActivity(ti);
+                    finish();
+                }
         );
     }
 
@@ -2562,6 +2591,12 @@
         }
     }
 
+    @Override
+    protected boolean shouldAddFooterView() {
+        // To accommodate for window insets
+        return true;
+    }
+
     public class ChooserListAdapter extends ResolveListAdapter {
         public static final int TARGET_BAD = -1;
         public static final int TARGET_CALLER = 0;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f82c28e..bef60d9 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -382,14 +382,30 @@
         finish();
     }
 
+    /**
+     * Numerous layouts are supported, each with optional ViewGroups.
+     * Make sure the inset gets added to the correct View, using
+     * a footer for Lists so it can properly scroll under the navbar.
+     */
+    protected boolean shouldAddFooterView() {
+        if (useLayoutWithDefault()) return true;
+
+        View buttonBar = findViewById(R.id.button_bar);
+        if (buttonBar == null || buttonBar.getVisibility() == View.GONE) return true;
+
+        return false;
+    }
+
     protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
         mSystemWindowInsets = insets.getSystemWindowInsets();
 
         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
                 mSystemWindowInsets.right, 0);
 
+        resetButtonBar();
+
         // Need extra padding so the list can fully scroll up
-        if (useLayoutWithDefault()) {
+        if (shouldAddFooterView()) {
             if (mFooterSpacer == null) {
                 mFooterSpacer = new Space(getApplicationContext());
             } else {
@@ -398,16 +414,14 @@
             mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
                                                                        mSystemWindowInsets.bottom));
             ((ListView) mAdapterView).addFooterView(mFooterSpacer);
-        } else {
-            View emptyView = findViewById(R.id.empty);
-            if (emptyView != null) {
-                emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                                     + getResources().getDimensionPixelSize(
-                                             R.dimen.chooser_edge_margin_normal) * 2);
-            }
         }
 
-        resetButtonBar();
+        View emptyView = findViewById(R.id.empty);
+        if (emptyView != null) {
+            emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+                                 + getResources().getDimensionPixelSize(
+                                         R.dimen.chooser_edge_margin_normal) * 2);
+        }
 
         return insets.consumeSystemWindowInsets();
     }
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index df91c4a..aec4bfa 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -84,7 +84,7 @@
                 }
 
                 // Force the chooser to requery and resort things
-                getActivity().recreate();
+                ((ChooserActivity) getActivity()).handlePackagesChanged();
                 break;
             case APP_INFO_INDEX:
                 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4573084..3113004 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1868,6 +1868,7 @@
             mCount = computeCurrentCountLocked();
             mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = 0;
             mUnpluggedReportedCount = mCurrentReportedCount = 0;
+            mTrackingReportedValues = false;
         }
 
         public void setUpdateVersion(int version) {
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index ca71314..3d99b72 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -385,6 +385,7 @@
 
         optional SettingProto provisioned = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto factory_reset_timeout_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto unsupported_countries = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Euicc euicc = 52;
 
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index 1ec05fb..ecb4193 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -264,3 +264,14 @@
     optional Sender priority_calls = 16;
     optional Sender priority_messages = 17;
 }
+
+// Next Tag: 2
+message PackageRemoteViewInfoProto {
+    optional string package_name = 1;
+    // add per-package additional info here (like channels)
+}
+
+// Next Tag: 2
+message NotificationRemoteViewsProto {
+    repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
+}
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_menu_copy_material.xml b/core/res/res/drawable/ic_menu_copy_material.xml
index ee58738..8c9f1c5 100644
--- a/core/res/res/drawable/ic_menu_copy_material.xml
+++ b/core/res/res/drawable/ic_menu_copy_material.xml
@@ -18,8 +18,10 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:autoMirrored="true"
+    android:tint="?attr/colorControlNormal">
   <path
-      android:fillColor="?android:attr/textColorSecondary"
+      android:fillColor="@color/white"
       android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
 </vector>
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 562f188..119b2e9 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -25,4 +25,6 @@
     android:singleLine="true"
     android:clickable="true"
     android:background="@drawable/chooser_action_button_bg"
+    android:drawableTint="?android:attr/colorControlNormal"
+    android:drawableTintMode="src_in"
     />
diff --git a/core/res/res/layout/chooser_grid_preview_file.xml b/core/res/res/layout/chooser_grid_preview_file.xml
index 8605443..2a39215 100644
--- a/core/res/res/layout/chooser_grid_preview_file.xml
+++ b/core/res/res/layout/chooser_grid_preview_file.xml
@@ -73,6 +73,7 @@
       android:layout_width="@dimen/chooser_preview_width"
       android:layout_height="wrap_content"
       android:layout_marginBottom="@dimen/chooser_view_spacing"
+      android:layout_gravity="center"
   />
 </LinearLayout>
 
diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml
index 29b7fb4..62df165 100644
--- a/core/res/res/layout/chooser_grid_preview_image.xml
+++ b/core/res/res/layout/chooser_grid_preview_image.xml
@@ -85,6 +85,7 @@
       android:layout_width="@dimen/chooser_preview_width"
       android:layout_height="wrap_content"
       android:layout_marginBottom="@dimen/chooser_view_spacing"
+      android:layout_gravity="center"
   />
 
 </LinearLayout>
diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml
index 5998cff..0029174 100644
--- a/core/res/res/layout/chooser_grid_preview_text.xml
+++ b/core/res/res/layout/chooser_grid_preview_text.xml
@@ -44,7 +44,8 @@
         android:ellipsize="end"
         android:fontFamily="@android:string/config_headlineFontFamily"
         android:textColor="?android:attr/textColorPrimary"
-        android:maxLines="2"/>
+        android:maxLines="2"
+        android:focusable="true"/>
 
   </RelativeLayout>
 
@@ -54,6 +55,7 @@
       android:layout_width="@dimen/chooser_preview_width"
       android:layout_height="wrap_content"
       android:layout_marginBottom="@dimen/chooser_view_spacing"
+      android:layout_gravity="center"
       />
 
   <!-- Required sub-layout so we can get the nice rounded corners-->
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 704151e..b414164 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -2019,7 +2019,7 @@
     <string name="mime_type_audio" msgid="4933450584432509875">"ઑડિયો"</string>
     <string name="mime_type_audio_ext" msgid="2615491023840514797">"<xliff:g id="EXTENSION">%1$s</xliff:g> ઑડિયો"</string>
     <string name="mime_type_video" msgid="7071965726609428150">"વીડિયો"</string>
-    <string name="mime_type_video_ext" msgid="185438149044230136">"<xliff:g id="EXTENSION">%1$s</xliff:g> વીડિઓ"</string>
+    <string name="mime_type_video_ext" msgid="185438149044230136">"<xliff:g id="EXTENSION">%1$s</xliff:g> વીડિયો"</string>
     <string name="mime_type_image" msgid="2134307276151645257">"છબી"</string>
     <string name="mime_type_image_ext" msgid="5743552697560999471">"<xliff:g id="EXTENSION">%1$s</xliff:g> છબી"</string>
     <string name="mime_type_compressed" msgid="8737300936080662063">"આર્કાઇવ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index fd1ef77..1606652 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1586,8 +1586,8 @@
     <string name="launchBrowserDefault" msgid="6328349989932924119">"Luncurkan Browser?"</string>
     <string name="SetupCallDefault" msgid="5581740063237175247">"Terima panggilan?"</string>
     <string name="activity_resolver_use_always" msgid="5575222334666843269">"Selalu"</string>
-    <string name="activity_resolver_set_always" msgid="4142825808921411476">"Setel untuk selalu membuka"</string>
-    <string name="activity_resolver_use_once" msgid="948462794469672658">"Hanya sekali"</string>
+    <string name="activity_resolver_set_always" msgid="4142825808921411476">"Selalu gunakan"</string>
+    <string name="activity_resolver_use_once" msgid="948462794469672658">"Sekali ini saja"</string>
     <string name="activity_resolver_app_settings" msgid="6758823206817748026">"Setelan"</string>
     <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s tidak mendukung profil kerja"</string>
     <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Tablet"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6a499dd..095615d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -142,7 +142,7 @@
     <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string>
     <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Chiamate Wi-Fi"</string>
     <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
-    <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Off"</string>
+    <string name="wifi_calling_off_summary" msgid="5626710010766902560">"OFF"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Chiamata tramite Wi-Fi"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Chiamata su rete mobile"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a77e7d6..43b2eea 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -550,7 +550,7 @@
     <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"פעולת טביעת האצבע בוטלה בידי המשתמש."</string>
     <string name="fingerprint_error_lockout" msgid="7853461265604738671">"יותר מדי ניסיונות. נסה שוב מאוחר יותר."</string>
     <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"יותר מדי ניסיונות. חיישן טביעות האצבע הושבת."</string>
-    <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"נסה שוב."</string>
+    <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"כדאי לנסות שוב."</string>
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נרשמו טביעות אצבע."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר זה אין חיישן טביעות אצבע."</string>
     <string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string>
@@ -821,8 +821,8 @@
     <string name="lockscreen_emergency_call" msgid="7500692654885445299">"חירום"</string>
     <string name="lockscreen_return_to_call" msgid="3156883574692006382">"חזרה לשיחה"</string>
     <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"נכון!"</string>
-    <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"נסה שוב"</string>
-    <string name="lockscreen_password_wrong" msgid="8605355913868947490">"נסה שוב"</string>
+    <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"כדאי לנסות שוב"</string>
+    <string name="lockscreen_password_wrong" msgid="8605355913868947490">"כדאי לנסות שוב"</string>
     <string name="lockscreen_storage_locked" msgid="634993789186443380">"בטל את הנעילה לכל התכונות והנתונים"</string>
     <string name="faceunlock_multiple_failures" msgid="681991538434031708">"חרגת ממספר הניסיונות המרבי של זיהוי פנים"</string>
     <string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"‏אין כרטיס SIM"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 699ea2e..f028ea8 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1297,7 +1297,7 @@
     <string name="wifi_p2p_failed_message" msgid="6296397512378755690">"ವೈ-ಫೈ ಡೈರೆಕ್ಟ್ ಪ್ರಾರಂಭಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
     <string name="wifi_p2p_enabled_notification_title" msgid="9128862563403191596">"ವೈ-ಫೈ ಡೈರೆಕ್ಟ್ ಆನ್ ಆಗಿದೆ"</string>
     <string name="wifi_p2p_enabled_notification_message" msgid="5571179544720861510">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
-    <string name="accept" msgid="5447154347815825107">"ಸ್ವೀಕರಿಸು"</string>
+    <string name="accept" msgid="5447154347815825107">"ಸ್ವೀಕರಿಸಿ"</string>
     <string name="decline" msgid="6490507610282145874">"ನಿರಾಕರಿಸಿ"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="355410493918793111">"ಆಹ್ವಾನವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string>
     <string name="wifi_p2p_invitation_to_connect_title" msgid="8082524320754820762">"ಸಂಪರ್ಕಗೊಳ್ಳಲು ಆಹ್ವಾನ"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 96d0163..cc7fa89 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -612,8 +612,8 @@
     <string name="permdesc_register_call_provider" msgid="4201429251459068613">"Колдонмого жаңы телеком туташууларын каттоо мүмкүнчүлүгүн берет."</string>
     <string name="permlab_connection_manager" msgid="3179365584691166915">"телеком туташууларын башкаруу"</string>
     <string name="permdesc_connection_manager" msgid="1426093604238937733">"Колдонмого телеком туташууларын башкаруу мүмкүнчүлүгүн берет."</string>
-    <string name="permlab_bind_incall_service" msgid="5990625112603493016">"чалуу экраны менен байланыштыруу"</string>
-    <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Колдонмого чалуу экраны качан жана кандай көрүнө тургандыгын башкаруу мүмкүнчүлүгүн берет."</string>
+    <string name="permlab_bind_incall_service" msgid="5990625112603493016">"сүйлөшүп жатканда экранды башкара аласыз"</string>
+    <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Сүйлөшүп жатканда экранды башкара аласыз."</string>
     <string name="permlab_bind_connection_service" msgid="5409268245525024736">"телефония кызматтары"</string>
     <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Колдонмого чалууларды жасоо/кабыл алуу үчүн телефония кызматтары менен байланышууга уруксат берет."</string>
     <string name="permlab_control_incall_experience" msgid="6436863486094352987">"чалуу ичиндеги колдонуучу тажрыйбасын камсыз кылуу"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 56702dd..bfa9541 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -853,7 +853,7 @@
     <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2373955520011165432">"Du har gjort feil <xliff:g id="NUMBER">%d</xliff:g> ganger i forsøk på å låse opp TV-en. TV-en blir nå tilbakestilt til fabrikkstandard."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Du har foretatt <xliff:g id="NUMBER">%d</xliff:g> mislykkede opplåsinger av telefonen. Telefonen blir nå tilbakestilt til fabrikkinnstillingene."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Prøv igjen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
-    <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Glemt mønsteret?"</string>
+    <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Har du glemt mønsteret?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Opplåsing av konto"</string>
     <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"For mange forsøk på tegning av mønster"</string>
     <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Logg på med Google-kontoen din for å låse opp."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 86f80b7..4a5d00a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -435,7 +435,7 @@
     <string name="permdesc_camera" msgid="1354600178048761499">"यस अनुप्रयोगले जुनसुकै समय क्यामेराको प्रयोग गरी तस्बिर खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string>
     <string name="permlab_vibrate" msgid="8596800035791962017">"कम्पन नियन्त्रण गर्नुहोस्"</string>
     <string name="permdesc_vibrate" msgid="8733343234582083721">"अनुप्रयोगलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string>
-    <string name="permlab_callPhone" msgid="1798582257194643320">"फोन नम्बरहरूमा सिधै कल गर्नुहोस्"</string>
+    <string name="permlab_callPhone" msgid="1798582257194643320">"फोन नम्बरहरूमा सीधै कल गर्नुहोस्"</string>
     <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न अनुप्रयोगलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले अनुप्रयोगलाई आपतकालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब अनुप्रयोगहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS कल सेवा पहुँच गर्नुहोस्"</string>
     <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 045f4bb..a0d2d84 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1035,47 +1035,26 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readContacts">read your contacts</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readContacts" product="tablet">Allows the app to read
-      data about your contacts stored on your tablet, including the frequency
-      with which you\'ve called, emailed, or communicated in other ways with
-      specific individuals. This permission allows apps to save your contact
-      data, and malicious apps may share contact data without your
-      knowledge.</string>
+    <string name="permdesc_readContacts" product="tablet">Allows the app to read data about your contacts stored on your tablet.
+      This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readContacts" product="tv">Allows the app to read
-      data about your contacts stored on your TV, including the frequency
-      with which you\'ve called, emailed, or communicated in other ways with
-      specific individuals. This permission allows apps to save your contact
-      data, and malicious apps may share contact data without your
-      knowledge.</string>
+    <string name="permdesc_readContacts" product="tv">Allows the app to read data about your contacts stored on your TV.
+      This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readContacts" product="default">Allows the app to
-      read data about your contacts stored on your phone, including the
-      frequency with which you\'ve called, emailed, or communicated in other ways
-      with specific individuals. This permission allows apps to save your
-      contact data, and malicious apps may share contact data without your
-      knowledge.</string>
+    <string name="permdesc_readContacts" product="default">Allows the app to read data about your contacts stored on your phone.
+      This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeContacts">modify your contacts</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeContacts" product="tablet">Allows the app to
-      modify the data about your contacts stored on your tablet, including the
-      frequency with which you\'ve called, emailed, or communicated in other ways
-      with specific contacts. This permission allows apps to delete contact
-      data.</string>
+    <string name="permdesc_writeContacts" product="tablet">Allows the app to modify the data about your contacts stored on your tablet.
+      This permission allows apps to delete contact data.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeContacts" product="tv">Allows the app to
-      modify the data about your contacts stored on your TV, including the
-      frequency with which you\'ve called, emailed, or communicated in other ways
-      with specific contacts. This permission allows apps to delete contact
-      data.</string>
+    <string name="permdesc_writeContacts" product="tv">Allows the app to modify the data about your contacts stored on your TV.
+      This permission allows apps to delete contact data.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeContacts" product="default">Allows the app to
-    modify the data about your contacts stored on your phone, including the
-    frequency with which you\'ve called, emailed, or communicated in other ways
-    with specific contacts. This permission allows apps to delete contact
-    data.</string>
+    <string name="permdesc_writeContacts" product="default">Allows the app to modify the data about your contacts stored on your phone.
+      This permission allows apps to delete contact data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCallLog">read call log</string>
@@ -1138,6 +1117,8 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your TV for the app to be able to use them.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessCoarseLocation" product="automotive">This app can get your approximate location only when it is in the foreground. These location services must be turned on and available on your car for the app to be able to use them.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when the app is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3361,6 +3342,26 @@
     <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead.  For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. -->
     <string name="ringtone_unknown">Unknown</string>
 
+    <!-- Start of string constants used to inform the user that the current network may be failing to connect due to not supporting randomized MAC-->
+    <!-- Notification title [CHAR_LIMIT=NONE]-->
+    <string name="wifi_cannot_connect_with_randomized_mac_title">Can\u2019t connect to <xliff:g id="ssid" example="SSID_1">%1$s</xliff:g></string>
+    <!-- Notification text [CHAR_LIMIT=NONE]-->
+    <string name="wifi_cannot_connect_with_randomized_mac_message">Tap to change privacy settings and retry</string>
+    <!-- Title of the dialog which pops up when the notification is tapped. [CHAR_LIMIT=NONE]-->
+    <string name="wifi_disable_mac_randomization_dialog_title">Change privacy setting?</string>
+    <!-- Dialog text [CHAR_LIMIT=NONE]-->
+    <string name="wifi_disable_mac_randomization_dialog_message"><xliff:g id="ssid" example="SSID_1">%1$s</xliff:g> may want to connect using your device MAC address, a unique identifier. This may allow your device\u2019s location to be tracked by nearby devices.
+        \n\nIf you continue, <xliff:g id="ssid" example="SSID_1">%1$s</xliff:g> will change your privacy setting and try to connect again.</string>
+    <!-- Text of the button that will disable MAC randomization for the network when tapped. [CHAR_LIMIT=NONE]-->
+    <string name="wifi_disable_mac_randomization_dialog_confirm_text">Change setting</string>
+    <!-- Toast message which shows up after MAC randomization for the network is disabled. [CHAR_LIMIT=NONE]-->
+    <string name="wifi_disable_mac_randomization_dialog_success">Setting updated. Try connecting again.</string>
+    <!-- Toast message which shows up if the operation failed. [CHAR_LIMIT=NONE]-->
+    <string name="wifi_disable_mac_randomization_dialog_failure">Can\u2019t change privacy setting</string>
+    <!-- Toast message which shows up if the network no longer exists on the device. [CHAR_LIMIT=NONE]-->
+    <string name="wifi_disable_mac_randomization_dialog_network_not_found">Network not found</string>
+    <!-- End of string constants used to inform the user that the current network may be failing to connect due to not supporting randomized MAC-->
+
     <!-- A notification is shown when there are open wireless networks nearby.  This is the notification's title. -->
     <plurals name="wifi_available">
         <item quantity="one">Wi-Fi network available</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4c13076..aff7f61 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2059,6 +2059,14 @@
   <java-symbol type="layout" name="safe_mode" />
   <java-symbol type="layout" name="simple_list_item_2_single_choice" />
   <java-symbol type="layout" name="app_error_dialog" />
+  <java-symbol type="string" name="wifi_cannot_connect_with_randomized_mac_title" />
+  <java-symbol type="string" name="wifi_cannot_connect_with_randomized_mac_message" />
+  <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_title" />
+  <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_message" />
+  <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_confirm_text" />
+  <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_success" />
+  <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_failure" />
+  <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_network_not_found" />
   <java-symbol type="plurals" name="wifi_available" />
   <java-symbol type="plurals" name="wifi_available_detailed" />
   <java-symbol type="string" name="wifi_available_title" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index b7b02a3..874d99e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -732,6 +732,7 @@
                  Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
                  Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                  Settings.Secure.TAP_GESTURE,
+                 Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL);
 
     @Test
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2d6cd24..6890270 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -908,6 +908,7 @@
 
     /** @hide */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void setMasterMute(boolean mute, int flags) {
         final IAudioService service = getService();
         try {
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index 261b9f5..c5da135 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -25,4 +25,6 @@
     <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/>
     <!-- This permission is required to get bluetooth broadcast. -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
+    <!--  Allows us to defer some actions until after the BOOT_COMPLETED event -->
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 </manifest>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SUWProgressController.java b/packages/CarSystemUI/src/com/android/systemui/car/SUWProgressController.java
new file mode 100644
index 0000000..d952939
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/SUWProgressController.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car;
+
+import android.app.ActivityManager;
+import android.car.settings.CarSettings;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import com.android.systemui.Dependency;
+
+import java.util.ArrayList;
+
+/**
+ * A controller that monitors the status of SUW progress for each user.
+ */
+public class SUWProgressController {
+    private static final Uri USER_SETUP_IN_PROGRESS_URI = Settings.Secure.getUriFor(
+            CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS);
+    private final ArrayList<SUWProgressListener> mListeners = new ArrayList<>();
+    private final ContentObserver mCarSettingsObserver = new ContentObserver(
+            Dependency.get(Dependency.MAIN_HANDLER)) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (USER_SETUP_IN_PROGRESS_URI.equals(uri)) {
+                notifyUserSetupInProgressChanged();
+            }
+        }
+    };
+    private final ContentResolver mContentResolver;
+
+    public SUWProgressController(Context context) {
+        mContentResolver = context.getContentResolver();
+    }
+
+    /**
+     * Returns {@code true} then SUW is in progress for the given user.
+     */
+    public boolean isUserSetupInProgress(int user) {
+        return Settings.Secure.getIntForUser(mContentResolver,
+                CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS, /* def= */ 0, user) != 0;
+    }
+
+    /**
+     * Returns {@code true} then SUW is in progress for the current user.
+     */
+    public boolean isCurrentUserSetupInProgress() {
+        return isUserSetupInProgress(ActivityManager.getCurrentUser());
+    }
+
+    /**
+     * Adds a {@link SUWProgressListener} callback.
+     */
+    public void addCallback(SUWProgressListener listener) {
+        mListeners.add(listener);
+        if (mListeners.size() == 1) {
+            startListening(ActivityManager.getCurrentUser());
+        }
+        listener.onUserSetupInProgressChanged();
+    }
+
+    /**
+     * Removes a {@link SUWProgressListener} callback.
+     */
+    public void removeCallback(SUWProgressListener listener) {
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            stopListening();
+        }
+    }
+
+    private void startListening(int user) {
+        mContentResolver.registerContentObserver(
+                USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver,
+                user);
+    }
+
+    private void stopListening() {
+        mContentResolver.unregisterContentObserver(mCarSettingsObserver);
+    }
+
+    /**
+     * Allows SUWProgressController to switch its listeners to observe SUW progress for new user.
+     */
+    public void onUserSwitched() {
+        if (mListeners.size() == 0) {
+            return;
+        }
+
+        mContentResolver.unregisterContentObserver(mCarSettingsObserver);
+        mContentResolver.registerContentObserver(
+                USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver,
+                ActivityManager.getCurrentUser());
+    }
+
+    private void notifyUserSetupInProgressChanged() {
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            mListeners.get(i).onUserSetupInProgressChanged();
+        }
+    }
+
+    /**
+     * A listener that listens for changes in SUW progress for a user.
+     */
+    public interface SUWProgressListener {
+        /**
+         * A callback for when a change occurs in SUW progress for a user.
+         */
+        default void onUserSetupInProgressChanged() {
+        }
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index a371a1d8..90d20ba 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -110,24 +110,32 @@
                 mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
             }
 
-            setOnClickListener(v -> {
-                intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
-                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-            });
+            intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
+            setOnClickListener(getButtonClickListener(intent));
 
             if (longPressIntentString != null) {
                 final Intent longPressIntent = Intent.parseUri(longPressIntentString,
                         Intent.URI_INTENT_SCHEME);
-                setOnLongClickListener(v -> {
-                    mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT);
-                    return true;
-                });
+                setOnLongClickListener(getButtonLongClickListener(longPressIntent));
             }
         } catch (Exception e) {
             throw new RuntimeException("Failed to attach intent", e);
         }
     }
 
+    /** Defines the behavior of a button click. */
+    protected OnClickListener getButtonClickListener(Intent toSend) {
+        return v -> mContext.startActivityAsUser(toSend, UserHandle.CURRENT);
+    }
+
+    /** Defines the behavior of a long click. */
+    protected OnLongClickListener getButtonLongClickListener(Intent toSend) {
+        return v -> {
+            mContext.startActivityAsUser(toSend, UserHandle.CURRENT);
+            return true;
+        };
+    }
+
     private void setupIcons(TypedArray styledAttributes) {
         mSelectedAlpha = styledAttributes.getFloat(
                 R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 8879742..bdf23c5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -90,17 +90,7 @@
         try {
             if (mIntent != null) {
                 final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
-                setOnClickListener(v -> {
-                    try {
-                        if (mBroadcastIntent) {
-                            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-                            return;
-                        }
-                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-                    } catch (Exception e) {
-                        Log.e(TAG, "Failed to launch intent", e);
-                    }
-                });
+                setOnClickListener(getButtonClickListener(intent));
             }
         } catch (URISyntaxException e) {
             throw new RuntimeException("Failed to attach intent", e);
@@ -109,21 +99,41 @@
         try {
             if (mLongIntent != null) {
                 final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
-                setOnLongClickListener(v -> {
-                    try {
-                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-                    } catch (Exception e) {
-                        Log.e(TAG, "Failed to launch intent", e);
-                    }
-                    // consume event either way
-                    return true;
-                });
+                setOnLongClickListener(getButtonLongClickListener(intent));
             }
         } catch (URISyntaxException e) {
             throw new RuntimeException("Failed to attach long press intent", e);
         }
     }
 
+    /** Defines the behavior of a button click. */
+    protected OnClickListener getButtonClickListener(Intent toSend) {
+        return v -> {
+            try {
+                if (mBroadcastIntent) {
+                    mContext.sendBroadcastAsUser(toSend, UserHandle.CURRENT);
+                    return;
+                }
+                mContext.startActivityAsUser(toSend, UserHandle.CURRENT);
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to launch intent", e);
+            }
+        };
+    }
+
+    /** Defines the behavior of a long click. */
+    protected OnLongClickListener getButtonLongClickListener(Intent toSend) {
+        return v -> {
+            try {
+                mContext.startActivityAsUser(toSend, UserHandle.CURRENT);
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to launch intent", e);
+            }
+            // consume event either way
+            return true;
+        };
+    }
+
     /**
      * @param selected true if should indicate if this is a selected state, false otherwise
      */
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 db98c36..aaa621f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -28,12 +28,16 @@
 import android.car.drivingstate.CarUxRestrictionsManager;
 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
 import android.car.media.CarAudioManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -69,6 +73,7 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
+import com.android.systemui.car.SUWProgressController;
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.fragments.FragmentHostManager;
@@ -102,6 +107,7 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.volume.VolumeUI;
@@ -152,7 +158,9 @@
     private CarFacetButtonController mCarFacetButtonController;
     private ActivityManagerWrapper mActivityManagerWrapper;
     private DeviceProvisionedController mDeviceProvisionedController;
+    private SUWProgressController mSUWProgressController;
     private boolean mDeviceIsSetUpForUser = true;
+    private boolean mIsUserSetupInProgress = false;
     private HvacController mHvacController;
     private DrivingStateHelper mDrivingStateHelper;
     private PowerManagerHelper mPowerManagerHelper;
@@ -246,16 +254,33 @@
                 }
             };
 
+    private boolean mBootCompleted = false;
+    private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mBootCompleted = true;
+            String action = intent.getAction();
+            if (action != null && action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+                initHvac();
+            }
+        }
+    };
+
     @Override
     public void start() {
         // Non blocking call to connect to car service. Call this early so that we'll be connected
         // asap.
         ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext);
 
+        // Defer some actions for CarStatusBar initialization until after boot complete event
+        registerBootCompletedReceiver();
+
         // get the provisioned state before calling the parent class since it's that flow that
         // builds the nav bar
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mSUWProgressController = new SUWProgressController(mContext);
         mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
+        mIsUserSetupInProgress = mSUWProgressController.isCurrentUserSetupInProgress();
 
         // Keyboard related setup, before nav bars are created.
         mHideNavBarForKeyboard = mContext.getResources().getBoolean(
@@ -308,6 +333,13 @@
 
         mHvacController.connectToCarService();
 
+        mSUWProgressController.addCallback(
+                new SUWProgressController.SUWProgressListener() {
+                    @Override
+                    public void onUserSetupInProgressChanged() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+                });
         mDeviceProvisionedController.addCallback(
                 new DeviceProvisionedController.DeviceProvisionedListener() {
                     @Override
@@ -317,6 +349,7 @@
 
                     @Override
                     public void onUserSwitched() {
+                        mSUWProgressController.onUserSwitched();
                         mHandler.post(() -> restartNavBarsIfNecessary());
                     }
                 });
@@ -349,14 +382,6 @@
                 });
     }
 
-    private void restartNavBarsIfNecessary() {
-        boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
-        if (mDeviceIsSetUpForUser != currentUserSetup) {
-            mDeviceIsSetUpForUser = currentUserSetup;
-            restartNavBars();
-        }
-    }
-
     @Override
     protected void getDependencies() {
         // Keyguard
@@ -366,6 +391,9 @@
 
         // Policy
         mZenController = Dependency.get(ZenModeController.class);
+        if (Build.IS_USERDEBUG) {
+            mNetworkController = Dependency.get(NetworkController.class);
+        }
 
         // Icon
         mIconController = Dependency.get(StatusBarIconController.class);
@@ -396,6 +424,27 @@
         // ignore.
     }
 
+    private void registerBootCompletedReceiver() {
+        IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+        bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mContext.registerReceiver(mBootCompletedReceiver, bootCompletedFilter);
+    }
+
+    private void unregisterBootCompletedListener() {
+        mContext.unregisterReceiver(mBootCompletedReceiver);
+    }
+
+    private void restartNavBarsIfNecessary() {
+        boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+        boolean currentUserSetupInProgress = mSUWProgressController.isCurrentUserSetupInProgress();
+        if (mIsUserSetupInProgress != currentUserSetupInProgress
+                || mDeviceIsSetUpForUser != currentUserSetup) {
+            mDeviceIsSetUpForUser = currentUserSetup;
+            mIsUserSetupInProgress = currentUserSetupInProgress;
+            restartNavBars();
+        }
+    }
+
     /**
      * Remove all content from navbars and rebuild them. Used to allow for different nav bars
      * before and after the device is provisioned. . Also for change of density and font size.
@@ -403,7 +452,9 @@
     private void restartNavBars() {
         // remove and reattach all hvac components such that we don't keep a reference to unused
         // ui elements
-        mHvacController.removeAllComponents();
+        if (mHvacController != null) {
+            mHvacController.removeAllComponents();
+        }
         mCarFacetButtonController.removeAll();
 
         if (mNavigationBarWindow != null) {
@@ -422,6 +473,9 @@
         }
 
         buildNavBarContent();
+        if (mBootCompleted) {
+            initHvac();
+        }
         // If the UI was rebuilt (day/night change) while the keyguard was up we need to
         // correctly respect that state.
         if (mIsKeyguard) {
@@ -433,6 +487,8 @@
     }
 
     private void addTemperatureViewToController(View v) {
+        if (v == null) return;
+
         if (v instanceof TemperatureView) {
             mHvacController.addHvacTextView((TemperatureView) v);
         } else if (v instanceof ViewGroup) {
@@ -442,7 +498,6 @@
             }
         }
     }
-
     /**
      * Allows for showing or hiding just the navigation bars. This is indented to be used when
      * the full screen user selector is shown.
@@ -558,7 +613,7 @@
                 new HandleBarCloseNotificationGestureListener());
 
         mTopNavBarNotificationTouchListener = (v, event) -> {
-            if (!mDeviceIsSetUpForUser) {
+            if (!mDeviceIsSetUpForUser || mIsUserSetupInProgress) {
                 return true;
             }
             boolean consumed = openGestureDetector.onTouchEvent(event);
@@ -592,7 +647,11 @@
         mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();
 
         mNotificationDataManager = new NotificationDataManager();
-        mNotificationDataManager.setOnUnseenCountUpdateListener(this::onUnseenCountUpdate);
+        mNotificationDataManager.setOnUnseenCountUpdateListener(() -> {
+            if (mNavigationBarView != null && mNotificationDataManager != null) {
+                onUseenCountUpdate(mNotificationDataManager.getUnseenNotificationCount());
+            }
+        });
 
         mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
                 R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
@@ -718,25 +777,22 @@
     }
 
     /**
-     * This method is called whenever there is an update to the number of unseen notifications.
-     * This method can be extended by OEMs to customize the desired logic.
+     * This method is automatically called whenever there is an update to the number of unseen
+     * notifications. This method can be extended by OEMs to customize the desired logic.
      */
-    protected void onUnseenCountUpdate() {
-        if (mNavigationBarView != null && mNotificationDataManager != null) {
-            Boolean hasUnseen =
-                    mNotificationDataManager.getUnseenNotificationCount() > 0;
+    protected void onUseenCountUpdate(int unseenNotificationCount) {
+        boolean hasUnseen = unseenNotificationCount > 0;
 
-            if (mNavigationBarView != null) {
-                mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
-            }
+        if (mNavigationBarView != null) {
+            mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+        }
 
-            if (mLeftNavigationBarView != null) {
-                mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
-            }
+        if (mLeftNavigationBarView != null) {
+            mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
+        }
 
-            if (mRightNavigationBarView != null) {
-                mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
-            }
+        if (mRightNavigationBarView != null) {
+            mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen);
         }
     }
 
@@ -898,22 +954,24 @@
     }
 
     private void buildNavBarContent() {
+        boolean shouldBuildNavBarContent = mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
+
         // Always build top bar.
-        buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar :
+        buildTopBar(shouldBuildNavBarContent ? R.layout.car_top_navigation_bar :
                 R.layout.car_top_navigation_bar_unprovisioned);
 
         if (mShowBottom) {
-            buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar :
+            buildBottomBar(shouldBuildNavBarContent ? R.layout.car_navigation_bar :
                     R.layout.car_navigation_bar_unprovisioned);
         }
 
         if (mShowLeft) {
-            buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar :
+            buildLeft(shouldBuildNavBarContent ? R.layout.car_left_navigation_bar :
                     R.layout.car_left_navigation_bar_unprovisioned);
         }
 
         if (mShowRight) {
-            buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar :
+            buildRight(shouldBuildNavBarContent ? R.layout.car_right_navigation_bar :
                     R.layout.car_right_navigation_bar_unprovisioned);
         }
     }
@@ -1034,7 +1092,6 @@
             throw new RuntimeException("Unable to build top nav bar due to missing layout");
         }
         mTopNavigationBarView.setStatusBar(this);
-        addTemperatureViewToController(mTopNavigationBarView);
         mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
     }
 
@@ -1049,7 +1106,6 @@
             throw new RuntimeException("Unable to build bottom nav bar due to missing layout");
         }
         mNavigationBarView.setStatusBar(this);
-        addTemperatureViewToController(mNavigationBarView);
         mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
     }
 
@@ -1061,7 +1117,6 @@
             throw new RuntimeException("Unable to build left nav bar due to missing layout");
         }
         mLeftNavigationBarView.setStatusBar(this);
-        addTemperatureViewToController(mLeftNavigationBarView);
         mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
     }
 
@@ -1074,10 +1129,20 @@
             throw new RuntimeException("Unable to build right nav bar due to missing layout");
         }
         mRightNavigationBarView.setStatusBar(this);
-        addTemperatureViewToController(mRightNavigationBarView);
         mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
     }
 
+    private void initHvac() {
+        if (mHvacController == null) {
+            mHvacController = Dependency.get(HvacController.class);
+            mHvacController.connectToCarService();
+        }
+        addTemperatureViewToController(mTopNavigationBarView);
+        addTemperatureViewToController(mNavigationBarView);
+        addTemperatureViewToController(mLeftNavigationBarView);
+        addTemperatureViewToController(mRightNavigationBarView);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         //When executing dump() function simultaneously, we need to serialize them
@@ -1276,6 +1341,13 @@
         mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
     }
 
+    @Override
+    public void onLocaleListChanged() {
+        // TODO - We should not have to reload sysUI on locale change
+        // Passing null since result is only needed to notify the IME window of current state
+        makeStatusBarView(/* result= */ null);
+    }
+
     /**
      * Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
      */
@@ -1529,8 +1601,10 @@
 
         @Override
         protected void setHeadsUpVisible() {
-            // if the Notifications panel is showing don't show the Heads up
-            if (!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) {
+            // if the Notifications panel is showing or SUW for user is in progress then don't show
+            // heads up notifications
+            if ((!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded)
+                    || !mDeviceIsSetUpForUser || mIsUserSetupInProgress) {
                 return;
             }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index f74dbe9..92834e8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -34,6 +34,7 @@
 
 import androidx.recyclerview.widget.GridLayoutManager;
 
+import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.CarSystemUIFactory;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
@@ -117,8 +118,9 @@
                 /* isAddUser= */ false,
                 /* isForeground= */ true);
 
-        // If the initial user has trusted device, display the unlock dialog on the keyguard.
-        if (hasTrustedDevice(initialUser)) {
+        // If the initial user has screen lock and trusted device, display the unlock dialog on the
+        // keyguard.
+        if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) {
             mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
                     mOnHideListener);
         } else {
@@ -160,7 +162,7 @@
      */
     private void onUserSelected(UserGridRecyclerView.UserRecord record) {
         mSelectedUser = record;
-        if (hasTrustedDevice(record.mInfo.id)) {
+        if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
             mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
             return;
         }
@@ -198,6 +200,11 @@
 
     }
 
+    private boolean hasScreenLock(int uid) {
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+        return lockPatternUtils.isSecure(uid);
+    }
+
     private boolean hasTrustedDevice(int uid) {
         if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
             return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 6b1ceae..1d9e4ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -176,6 +176,8 @@
     static final String KEY_CARRIER_AP_EAP_TYPE = "key_carrier_ap_eap_type";
     static final String KEY_CARRIER_NAME = "key_carrier_name";
     static final String KEY_EAPTYPE = "eap_psktype";
+    static final String KEY_IS_PSK_SAE_TRANSITION_MODE = "key_is_psk_sae_transition_mode";
+    static final String KEY_IS_OWE_TRANSITION_MODE = "key_is_owe_transition_mode";
     static final AtomicInteger sLastId = new AtomicInteger(0);
 
     /*
@@ -190,15 +192,12 @@
     public static final int SECURITY_OWE = 4;
     public static final int SECURITY_SAE = 5;
     public static final int SECURITY_EAP_SUITE_B = 6;
-    public static final int SECURITY_PSK_SAE_TRANSITION = 7;
-    public static final int SECURITY_OWE_TRANSITION = 8;
-    public static final int SECURITY_MAX_VAL = 9; // Has to be the last
+    public static final int SECURITY_MAX_VAL = 7; // Has to be the last
 
     private static final int PSK_UNKNOWN = 0;
     private static final int PSK_WPA = 1;
     private static final int PSK_WPA2 = 2;
     private static final int PSK_WPA_WPA2 = 3;
-    private static final int PSK_SAE = 4;
 
     private static final int EAP_UNKNOWN = 0;
     private static final int EAP_WPA = 1; // WPA-EAP
@@ -259,6 +258,9 @@
     private String mOsuFailure;
     private boolean mOsuProvisioningComplete = false;
 
+    private boolean mIsPskSaeTransitionMode = false;
+    private boolean mIsOweTransitionMode = false;
+
     /**
      * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
      */
@@ -322,6 +324,13 @@
         if (savedState.containsKey(KEY_CARRIER_NAME)) {
             mCarrierName = savedState.getString(KEY_CARRIER_NAME);
         }
+        if (savedState.containsKey(KEY_IS_PSK_SAE_TRANSITION_MODE)) {
+            mIsPskSaeTransitionMode = savedState.getBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE);
+        }
+        if (savedState.containsKey(KEY_IS_OWE_TRANSITION_MODE)) {
+            mIsOweTransitionMode = savedState.getBoolean(KEY_IS_OWE_TRANSITION_MODE);
+        }
+
         update(mConfig, mInfo, mNetworkInfo);
 
         // Calculate required fields
@@ -647,8 +656,15 @@
         return oldMetering == mIsScoredNetworkMetered;
     }
 
-    public static String getKey(ScanResult result) {
-        return getKey(result.SSID, result.BSSID, getSecurity(result));
+    /**
+     * Generates an AccessPoint key for a given scan result
+     *
+     * @param context
+     * @param result Scan result
+     * @return AccessPoint key
+     */
+    public static String getKey(Context context, ScanResult result) {
+        return getKey(result.SSID, result.BSSID, getSecurity(context, result));
     }
 
     /**
@@ -706,7 +722,42 @@
      * Determines if the other AccessPoint represents the same network as this AccessPoint
      */
     public boolean matches(AccessPoint other) {
-        return getKey().equals(other.getKey());
+        if (isPasspoint() || isPasspointConfig() || isOsuProvider()) {
+            return getKey().equals(other.getKey());
+        }
+
+        if (!isSameSsidOrBssid(other)) {
+            return false;
+        }
+
+        final int otherApSecurity = other.getSecurity();
+        if (mIsPskSaeTransitionMode) {
+            if (otherApSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) {
+                return true;
+            } else if (otherApSecurity == SECURITY_PSK) {
+                return true;
+            }
+        } else {
+            if ((security == SECURITY_SAE || security == SECURITY_PSK)
+                    && other.isPskSaeTransitionMode()) {
+                return true;
+            }
+        }
+
+        if (mIsOweTransitionMode) {
+            if (otherApSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) {
+                return true;
+            } else if (otherApSecurity == SECURITY_NONE) {
+                return true;
+            }
+        } else {
+            if ((security == SECURITY_OWE || security == SECURITY_NONE)
+                    && other.isOweTransitionMode()) {
+                return true;
+            }
+        }
+
+        return security == other.getSecurity();
     }
 
     public boolean matches(WifiConfiguration config) {
@@ -720,18 +771,77 @@
         }
 
         final int configSecurity = getSecurity(config);
-        final WifiManager wifiManager = getWifiManager();
-        switch (security) {
-            case SECURITY_PSK_SAE_TRANSITION:
-                return configSecurity == SECURITY_PSK
-                        || (wifiManager.isWpa3SaeSupported() && configSecurity == SECURITY_SAE);
-            case SECURITY_OWE_TRANSITION:
-                return configSecurity == SECURITY_NONE
-                        || (wifiManager.isEnhancedOpenSupported()
-                                && configSecurity == SECURITY_OWE);
-            default:
-                return security == configSecurity;
+        if (mIsPskSaeTransitionMode) {
+            if (configSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) {
+                return true;
+            } else if (configSecurity == SECURITY_PSK) {
+                return true;
+            }
         }
+
+        if (mIsOweTransitionMode) {
+            if (configSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) {
+                return true;
+            } else if (configSecurity == SECURITY_NONE) {
+                return true;
+            }
+        }
+
+        return security == getSecurity(config);
+    }
+
+    private boolean matches(WifiConfiguration config, WifiInfo wifiInfo) {
+        if (config == null || wifiInfo == null) {
+            return false;
+        }
+        if (!config.isPasspoint() && !isSameSsidOrBssid(wifiInfo)) {
+            return false;
+        }
+        return matches(config);
+    }
+
+    @VisibleForTesting
+    boolean matches(ScanResult scanResult) {
+        if (scanResult == null) {
+            return false;
+        }
+        if (isPasspoint() || isOsuProvider()) {
+            throw new IllegalStateException("Should not matches a Passpoint by ScanResult");
+        }
+
+        if (!isSameSsidOrBssid(scanResult)) {
+            return false;
+        }
+
+        if (mIsPskSaeTransitionMode) {
+            if (scanResult.capabilities.contains("SAE")
+                    && getWifiManager().isWpa3SaeSupported()) {
+                return true;
+            } else if (scanResult.capabilities.contains("PSK")) {
+                return true;
+            }
+        } else {
+            if ((security == SECURITY_SAE || security == SECURITY_PSK)
+                    && AccessPoint.isPskSaeTransitionMode(scanResult)) {
+                return true;
+            }
+        }
+
+        if (mIsOweTransitionMode) {
+            final int scanResultSccurity = getSecurity(mContext, scanResult);
+            if (scanResultSccurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) {
+                return true;
+            } else if (scanResultSccurity == SECURITY_NONE) {
+                return true;
+            }
+        } else {
+            if ((security == SECURITY_OWE || security == SECURITY_NONE)
+                    && AccessPoint.isOweTransitionMode(scanResult)) {
+                return true;
+            }
+        }
+
+        return security == getSecurity(mContext, scanResult);
     }
 
     public WifiConfiguration getConfig() {
@@ -818,14 +928,17 @@
         if (bestResult != null) {
             ssid = bestResult.SSID;
             bssid = bestResult.BSSID;
-            security = getSecurity(bestResult);
-            if (security == SECURITY_PSK || security == SECURITY_SAE
-                    || security == SECURITY_PSK_SAE_TRANSITION) {
+            security = getSecurity(mContext, bestResult);
+            if (security == SECURITY_PSK || security == SECURITY_SAE) {
                 pskType = getPskType(bestResult);
             }
             if (security == SECURITY_EAP) {
                 mEapType = getEapType(bestResult);
             }
+
+            mIsPskSaeTransitionMode = AccessPoint.isPskSaeTransitionMode(bestResult);
+            mIsOweTransitionMode = AccessPoint.isOweTransitionMode(bestResult);
+
             mIsCarrierAp = bestResult.isCarrierAp;
             mCarrierApEapType = bestResult.carrierApEapType;
             mCarrierName = bestResult.carrierName;
@@ -858,6 +971,12 @@
             return concise ? context.getString(R.string.wifi_security_short_eap) :
                 context.getString(R.string.wifi_security_eap);
         }
+
+        if (mIsPskSaeTransitionMode) {
+            return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
+                    context.getString(R.string.wifi_security_psk_sae);
+        }
+
         switch(security) {
             case SECURITY_EAP:
                 switch (mEapType) {
@@ -897,20 +1016,8 @@
                 return concise ? context.getString(R.string.wifi_security_short_wep) :
                     context.getString(R.string.wifi_security_wep);
             case SECURITY_SAE:
-            case SECURITY_PSK_SAE_TRANSITION:
-                if (pskType == PSK_SAE) {
-                    return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
-                            context.getString(R.string.wifi_security_psk_sae);
-                } else {
-                    return concise ? context.getString(R.string.wifi_security_short_sae) :
-                            context.getString(R.string.wifi_security_sae);
-                }
-            case SECURITY_OWE_TRANSITION:
-                if (mConfig != null && getSecurity(mConfig) == SECURITY_OWE) {
-                    return concise ? context.getString(R.string.wifi_security_short_owe) :
-                            context.getString(R.string.wifi_security_owe);
-                }
-                return concise ? "" : context.getString(R.string.wifi_security_none);
+                return concise ? context.getString(R.string.wifi_security_short_sae) :
+                        context.getString(R.string.wifi_security_sae);
             case SECURITY_OWE:
                 return concise ? context.getString(R.string.wifi_security_short_owe) :
                     context.getString(R.string.wifi_security_owe);
@@ -1194,7 +1301,7 @@
         if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
             return networkId == info.getNetworkId();
         } else if (config != null) {
-            return isKeyEqual(getKey(config));
+            return matches(config, info);
         } else {
             // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID.
             // (Note that we only do this if the WifiConfiguration explicitly equals INVALID).
@@ -1220,8 +1327,7 @@
      * Can only be called for unsecured networks.
      */
     public void generateOpenNetworkConfig() {
-        if ((security != SECURITY_NONE) && (security != SECURITY_OWE)
-                && (security != SECURITY_OWE_TRANSITION)) {
+        if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) {
             throw new IllegalStateException();
         }
         if (mConfig != null)
@@ -1264,43 +1370,14 @@
         savedState.putBoolean(KEY_IS_CARRIER_AP, mIsCarrierAp);
         savedState.putInt(KEY_CARRIER_AP_EAP_TYPE, mCarrierApEapType);
         savedState.putString(KEY_CARRIER_NAME, mCarrierName);
+        savedState.putBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE, mIsPskSaeTransitionMode);
+        savedState.putBoolean(KEY_IS_OWE_TRANSITION_MODE, mIsOweTransitionMode);
     }
 
     public void setListener(AccessPointListener listener) {
         mAccessPointListener = listener;
     }
 
-    private static final String sPskSuffix = "," + String.valueOf(SECURITY_PSK);
-    private static final String sSaeSuffix = "," + String.valueOf(SECURITY_SAE);
-    private static final String sPskSaeSuffix = "," + String.valueOf(SECURITY_PSK_SAE_TRANSITION);
-    private static final String sOweSuffix = "," + String.valueOf(SECURITY_OWE);
-    private static final String sOpenSuffix = "," + String.valueOf(SECURITY_NONE);
-    private static final String sOweTransSuffix = "," + String.valueOf(SECURITY_OWE_TRANSITION);
-
-    private boolean isKeyEqual(String compareTo) {
-        if (mKey == null) {
-            return false;
-        }
-
-        if (compareTo.endsWith(sPskSuffix) || compareTo.endsWith(sSaeSuffix)) {
-            if (mKey.endsWith(sPskSaeSuffix)) {
-                // Special handling for PSK-SAE transition mode. If the AP has advertised both,
-                // we compare the key with both PSK and SAE for a match.
-                return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
-                        compareTo.substring(0, compareTo.lastIndexOf(',')));
-            }
-        }
-        if (compareTo.endsWith(sOpenSuffix) || compareTo.endsWith(sOweSuffix)) {
-            if (mKey.endsWith(sOweTransSuffix)) {
-                // Special handling for OWE/Open networks. If AP advertises OWE in transition mode
-                // and we have an Open network saved, allow this connection to be established.
-                return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')),
-                        compareTo.substring(0, compareTo.lastIndexOf(',')));
-            }
-        }
-        return mKey.equals(compareTo);
-    }
-
     /**
      * Sets {@link #mScanResults} to the given collection and updates info based on the best RSSI
      * scan result.
@@ -1317,11 +1394,10 @@
         // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
         if (mKey != null && !isPasspoint() && !isOsuProvider()) {
             for (ScanResult result : scanResults) {
-                String scanResultKey = AccessPoint.getKey(result);
-                if (!isKeyEqual(scanResultKey)) {
+                if (!matches(result)) {
                     Log.d(TAG, String.format(
-                                    "ScanResult %s\nkey of %s did not match current AP key %s",
-                                    result, scanResultKey, mKey));
+                            "ScanResult %s\nkey of %s did not match current AP key %s",
+                            result, getKey(mContext, result), mKey));
                     return;
                 }
             }
@@ -1594,11 +1670,8 @@
     private static int getPskType(ScanResult result) {
         boolean wpa = result.capabilities.contains("WPA-PSK");
         boolean wpa2 = result.capabilities.contains("RSN-PSK");
-        boolean wpa3TransitionMode = result.capabilities.contains("PSK+SAE");
         boolean wpa3 = result.capabilities.contains("RSN-SAE");
-        if (wpa3TransitionMode) {
-            return PSK_SAE;
-        } else if (wpa2 && wpa) {
+        if (wpa2 && wpa) {
             return PSK_WPA_WPA2;
         } else if (wpa2) {
             return PSK_WPA2;
@@ -1625,22 +1698,37 @@
         return EAP_UNKNOWN;
     }
 
-    private static int getSecurity(ScanResult result) {
-        if (result.capabilities.contains("WEP")) {
+    private static int getSecurity(Context context, ScanResult result) {
+        final boolean isWep = result.capabilities.contains("WEP");
+        final boolean isSae = result.capabilities.contains("SAE");
+        final boolean isPsk = result.capabilities.contains("PSK");
+        final boolean isEapSuiteB192 = result.capabilities.contains("EAP_SUITE_B_192");
+        final boolean isEap = result.capabilities.contains("EAP");
+        final boolean isOwe = result.capabilities.contains("OWE");
+        final boolean isOweTransition = result.capabilities.contains("OWE_TRANSITION");
+
+        if (isSae && isPsk) {
+            final WifiManager wifiManager = (WifiManager)
+                    context.getSystemService(Context.WIFI_SERVICE);
+            return wifiManager.isWpa3SaeSupported() ? SECURITY_SAE : SECURITY_PSK;
+        }
+        if (isOweTransition) {
+            final WifiManager wifiManager = (WifiManager)
+                    context.getSystemService(Context.WIFI_SERVICE);
+            return wifiManager.isEnhancedOpenSupported() ? SECURITY_OWE : SECURITY_NONE;
+        }
+
+        if (isWep) {
             return SECURITY_WEP;
-        } else if (result.capabilities.contains("PSK+SAE")) {
-            return SECURITY_PSK_SAE_TRANSITION;
-        } else if (result.capabilities.contains("SAE")) {
+        } else if (isSae) {
             return SECURITY_SAE;
-        } else if (result.capabilities.contains("PSK")) {
+        } else if (isPsk) {
             return SECURITY_PSK;
-        } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
+        } else if (isEapSuiteB192) {
             return SECURITY_EAP_SUITE_B;
-        } else if (result.capabilities.contains("EAP")) {
+        } else if (isEap) {
             return SECURITY_EAP;
-        } else if (result.capabilities.contains("OWE_TRANSITION")) {
-            return SECURITY_OWE_TRANSITION;
-        } else if (result.capabilities.contains("OWE")) {
+        } else if (isOwe) {
             return SECURITY_OWE;
         }
         return SECURITY_NONE;
@@ -1686,10 +1774,6 @@
             return "SUITE_B";
         } else if (security == SECURITY_OWE) {
             return "OWE";
-        } else if (security == SECURITY_PSK_SAE_TRANSITION) {
-            return "PSK+SAE";
-        } else if (security == SECURITY_OWE_TRANSITION) {
-            return "OWE_TRANSITION";
         }
         return "NONE";
     }
@@ -1859,4 +1943,61 @@
             }
         }
     }
+
+    public boolean isPskSaeTransitionMode() {
+        return mIsPskSaeTransitionMode;
+    }
+
+    public boolean isOweTransitionMode() {
+        return mIsOweTransitionMode;
+    }
+
+    private static boolean isPskSaeTransitionMode(ScanResult scanResult) {
+        return scanResult.capabilities.contains("PSK")
+                && scanResult.capabilities.contains("SAE");
+    }
+
+    private static boolean isOweTransitionMode(ScanResult scanResult) {
+        return scanResult.capabilities.contains("OWE_TRANSITION");
+    }
+
+    private boolean isSameSsidOrBssid(ScanResult scanResult) {
+        if (scanResult == null) {
+            return false;
+        }
+
+        if (TextUtils.equals(ssid, scanResult.SSID)) {
+            return true;
+        } else if (scanResult.BSSID != null && TextUtils.equals(bssid, scanResult.BSSID)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isSameSsidOrBssid(WifiInfo wifiInfo) {
+        if (wifiInfo == null) {
+            return false;
+        }
+
+        if (TextUtils.equals(ssid, removeDoubleQuotes(wifiInfo.getSSID()))) {
+            return true;
+        } else if (wifiInfo.getBSSID() != null && TextUtils.equals(bssid, wifiInfo.getBSSID())) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isSameSsidOrBssid(AccessPoint accessPoint) {
+        if (accessPoint == null) {
+            return false;
+        }
+
+        if (TextUtils.equals(ssid, accessPoint.getSsid())) {
+            return true;
+        } else if (accessPoint.getBssid() != null
+                && TextUtils.equals(bssid, accessPoint.getBssid())) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index dae5464..6269a71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -201,8 +201,7 @@
             return;
         }
         if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
-                && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)
-                && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
+                && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
             mFrictionSld.setState(STATE_SECURED);
         } else if (mAccessPoint.isMetered()) {
             mFrictionSld.setState(STATE_METERED);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 3e359d2..f1c5f46 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -70,8 +70,10 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 /**
  * Tracks saved or available wifi networks and their state.
@@ -475,7 +477,7 @@
                 continue;
             }
 
-            String apKey = AccessPoint.getKey(result);
+            String apKey = AccessPoint.getKey(mContext, result);
             List<ScanResult> resultList;
             if (scanResultsByApKey.containsKey(apKey)) {
                 resultList = scanResultsByApKey.get(apKey);
@@ -548,14 +550,6 @@
     private void updateAccessPoints(final List<ScanResult> newScanResults,
             List<WifiConfiguration> configs) {
 
-        // Map configs and scan results necessary to make AccessPoints
-        final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
-        if (configs != null) {
-            for (WifiConfiguration config : configs) {
-                configsByKey.put(AccessPoint.getKey(config), config);
-            }
-        }
-
         WifiConfiguration connectionConfig = null;
         if (mLastInfo != null) {
             connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs);
@@ -587,7 +581,26 @@
                         getCachedOrCreate(entry.getValue(), cachedAccessPoints);
 
                 // Update the matching config if there is one, to populate saved network info
-                accessPoint.update(configsByKey.get(entry.getKey()));
+                final List<WifiConfiguration> matchedConfigs = configs.stream()
+                        .filter(config -> accessPoint.matches(config))
+                        .collect(Collectors.toList());
+
+                final int matchedConfigCount = matchedConfigs.size();
+                if (matchedConfigCount == 0) {
+                    accessPoint.update(null);
+                } else if (matchedConfigCount == 1) {
+                    accessPoint.update(matchedConfigs.get(0));
+                } else {
+                    // We may have 2 matched configured WifiCongiguration if the AccessPoint is
+                    // of PSK/SAE transition mode or open/OWE transition mode.
+                    Optional<WifiConfiguration> preferredConfig = matchedConfigs.stream()
+                            .filter(config -> isSaeOrOwe(config)).findFirst();
+                    if (preferredConfig.isPresent()) {
+                        accessPoint.update(preferredConfig.get());
+                    } else {
+                        accessPoint.update(matchedConfigs.get(0));
+                    }
+                }
 
                 accessPoints.add(accessPoint);
             }
@@ -653,6 +666,11 @@
         conditionallyNotifyListeners();
     }
 
+    private static boolean isSaeOrOwe(WifiConfiguration config) {
+        final int security = AccessPoint.getSecurity(config);
+        return security == AccessPoint.SECURITY_SAE || security == AccessPoint.SECURITY_OWE;
+    }
+
     @VisibleForTesting
     List<AccessPoint> updatePasspointAccessPoints(
             List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans,
@@ -701,7 +719,8 @@
     private AccessPoint getCachedOrCreate(
             List<ScanResult> scanResults,
             List<AccessPoint> cache) {
-        AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0)));
+        AccessPoint accessPoint = getCachedByKey(cache,
+                AccessPoint.getKey(mContext, scanResults.get(0)));
         if (accessPoint == null) {
             accessPoint = new AccessPoint(mContext, scanResults);
         } else {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index af4704c..77473f5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -39,6 +39,7 @@
 import android.net.WifiKey;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
@@ -1272,7 +1273,7 @@
     @Test
     public void testGetKey_matchesKeysCorrectly() {
         AccessPoint ap = new AccessPoint(mContext, mScanResults);
-        assertThat(ap.getKey()).isEqualTo(AccessPoint.getKey(mScanResults.get(0)));
+        assertThat(ap.getKey()).isEqualTo(AccessPoint.getKey(mContext, mScanResults.get(0)));
 
         WifiConfiguration spyConfig = spy(new WifiConfiguration());
         when(spyConfig.isPasspoint()).thenReturn(true);
@@ -1294,6 +1295,44 @@
     }
 
     /**
+     * Test that getKey returns a key of SAE type for a PSK/SAE transition mode ScanResult.
+     */
+    @Test
+    public void testGetKey_supportSaeTransitionMode_shouldGetSaeKey() {
+        ScanResult scanResult = createScanResult(TEST_SSID, TEST_BSSID, DEFAULT_RSSI);
+        scanResult.capabilities =
+                "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        StringBuilder key = new StringBuilder();
+        key.append(AccessPoint.KEY_PREFIX_AP);
+        key.append(TEST_SSID);
+        key.append(',');
+        key.append(AccessPoint.SECURITY_SAE);
+
+        assertThat(AccessPoint.getKey(mMockContext, scanResult)).isEqualTo(key.toString());
+    }
+
+    /**
+     * Test that getKey returns a key of PSK type for a PSK/SAE transition mode ScanResult.
+     */
+    @Test
+    public void testGetKey_notSupportSaeTransitionMode_shouldGetPskKey() {
+        ScanResult scanResult = createScanResult(TEST_SSID, TEST_BSSID, DEFAULT_RSSI);
+        scanResult.capabilities =
+                "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        StringBuilder key = new StringBuilder();
+        key.append(AccessPoint.KEY_PREFIX_AP);
+        key.append(TEST_SSID);
+        key.append(',');
+        key.append(AccessPoint.SECURITY_PSK);
+
+        assertThat(AccessPoint.getKey(mMockContext, scanResult)).isEqualTo(key.toString());
+    }
+
+    /**
      * Verifies that the Passpoint AccessPoint constructor creates AccessPoints whose isPasspoint()
      * returns true.
      */
@@ -1522,4 +1561,113 @@
 
         verify(mMockConnectListener).onFailure(anyInt());
     }
+
+    /**
+     * Verifies that matches(AccessPoint other) matches a PSK/SAE transition mode AP to a PSK or a
+     * SAE AP.
+     */
+    @Test
+    public void testMatches1_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() {
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+        AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+        // Transition mode AP matches a SAE AP.
+        AccessPoint saeAccessPoint = new TestAccessPointBuilder(mContext)
+                .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID))
+                .setSecurity(AccessPoint.SECURITY_SAE)
+                .build();
+        assertThat(pskSaeTransitionModeAp.matches(saeAccessPoint)).isTrue();
+
+        // Transition mode AP matches a PSK AP.
+        AccessPoint pskAccessPoint = new TestAccessPointBuilder(mContext)
+                .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID))
+                .setSecurity(AccessPoint.SECURITY_PSK)
+                .build();
+
+        assertThat(pskSaeTransitionModeAp.matches(pskAccessPoint)).isTrue();
+
+        // Transition mode AP does not match a SAE AP if the device does not support SAE.
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+        pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+        saeAccessPoint = new TestAccessPointBuilder(mContext)
+            .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID))
+            .setSecurity(AccessPoint.SECURITY_SAE)
+            .build();
+
+        assertThat(pskSaeTransitionModeAp.matches(saeAccessPoint)).isFalse();
+    }
+
+    /**
+     * Verifies that matches(WifiConfiguration config) matches a PSK/SAE transition mode AP to a PSK
+     * or a SAE WifiConfiguration.
+     */
+    @Test
+    public void testMatches2_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() {
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+        AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+        // Transition mode AP matches a SAE WifiConfiguration.
+        WifiConfiguration saeConfig = new WifiConfiguration();
+        saeConfig.SSID = TEST_SSID;
+        saeConfig.allowedKeyManagement.set(KeyMgmt.SAE);
+
+        assertThat(pskSaeTransitionModeAp.matches(saeConfig)).isTrue();
+
+        // Transition mode AP matches a PSK WifiConfiguration.
+        WifiConfiguration pskConfig = new WifiConfiguration();
+        pskConfig.SSID = TEST_SSID;
+        pskConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+
+        assertThat(pskSaeTransitionModeAp.matches(pskConfig)).isTrue();
+
+        // Transition mode AP does not matches a SAE WifiConfiguration if the device does not
+        // support SAE.
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+        pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+        assertThat(pskSaeTransitionModeAp.matches(saeConfig)).isFalse();
+    }
+
+    /**
+     * Verifies that matches(ScanResult scanResult) matches a PSK/SAE transition mode AP to a PSK
+     * or a SAE ScanResult.
+     */
+    @Test
+    public void testMatches3_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() {
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true);
+        AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+        // Transition mode AP matches a SAE ScanResult.
+        ScanResult saeScanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+                TEST_BSSID, DEFAULT_RSSI);
+        saeScanResult.capabilities = "[SAE-CCMP][ESS][WPS]";
+
+        assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isTrue();
+
+        // Transition mode AP matches a PSK ScanResult.
+        ScanResult pskScanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+                TEST_BSSID, DEFAULT_RSSI);
+        pskScanResult.capabilities = "[RSN-PSK-CCMP][ESS][WPS]";
+
+        assertThat(pskSaeTransitionModeAp.matches(pskScanResult)).isTrue();
+
+        // Transition mode AP does not matches a SAE ScanResult if the device does not support SAE.
+        when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false);
+        pskSaeTransitionModeAp = getPskSaeTransitionModeAp();
+
+        assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isFalse();
+    }
+
+    private AccessPoint getPskSaeTransitionModeAp() {
+        ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+                TEST_BSSID, DEFAULT_RSSI);
+        scanResult.capabilities =
+                "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]";
+        return new TestAccessPointBuilder(mMockContext)
+                .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult)))
+                .build();
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 894ba9c..21129f9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -641,6 +641,9 @@
         dumpSetting(s, p,
                 Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
                 GlobalSettingsProto.Euicc.FACTORY_RESET_TIMEOUT_MILLIS);
+        dumpSetting(s, p,
+                Settings.Global.EUICC_UNSUPPORTED_COUNTRIES,
+                GlobalSettingsProto.Euicc.UNSUPPORTED_COUNTRIES);
         p.end(euiccToken);
 
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c05c4cd..33085a1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -64,6 +64,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -716,6 +718,23 @@
                 }
             } catch (Throwable t) {
                 Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
+                if (t instanceof IOException) {
+                    // we failed to create a directory, so log the permissions and existence
+                    // state for the settings file and directory
+                    logSettingsDirectoryInformation(destination.getBaseFile());
+                    if (t.getMessage().contains("Couldn't create directory")) {
+                        // attempt to create the directory with Files.createDirectories, which
+                        // throws more informative errors than File.mkdirs.
+                        Path parentPath = destination.getBaseFile().getParentFile().toPath();
+                        try {
+                            Files.createDirectories(parentPath);
+                            Slog.i(LOG_TAG, "Successfully created " + parentPath);
+                        } catch (Throwable t2) {
+                            Slog.e(LOG_TAG, "Failed to write " + parentPath
+                                    + " with Files.writeDirectories", t2);
+                        }
+                    }
+                }
                 destination.failWrite(out);
             } finally {
                 IoUtils.closeQuietly(out);
@@ -729,6 +748,33 @@
         }
     }
 
+    private static void logSettingsDirectoryInformation(File settingsFile) {
+        File parent = settingsFile.getParentFile();
+        Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile
+                + " with stacktrace ", new Exception());
+        File ancestorDir = parent;
+        while (ancestorDir != null) {
+            if (!ancestorDir.exists()) {
+                Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+                        + " does not exist");
+                ancestorDir = ancestorDir.getParentFile();
+            } else {
+                Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+                        + " exists");
+                Slog.i(LOG_TAG, "ancestor directory " + ancestorDir
+                        + " permissions: r: " + ancestorDir.canRead() + " w: "
+                        + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute());
+                File ancestorParent = ancestorDir.getParentFile();
+                if (ancestorParent != null) {
+                    Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent
+                            + " permissions: r: " + ancestorParent.canRead() + " w: "
+                            + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute());
+                }
+                break;
+            }
+        }
+    }
+
     static void writeSingleSetting(int version, XmlSerializer serializer, String id,
             String name, String value, String defaultValue, String packageName,
             String tag, boolean defaultSysSet) throws IOException {
@@ -803,6 +849,7 @@
             in = new AtomicFile(mStatePersistFile).openRead();
         } catch (FileNotFoundException fnfe) {
             Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+            logSettingsDirectoryInformation(mStatePersistFile);
             addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
             return;
         }
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 1bfc4c0..c2788a8 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -24,7 +24,35 @@
     android:outlineProvider="none"
     android:elevation="5dp" > <!-- Put it above the status bar header -->
 
-    <include layout="@layout/keyguard_indication_area_overlay" />
+    <LinearLayout
+        android:id="@+id/keyguard_indication_area"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
+        android:layout_gravity="bottom|center_horizontal"
+        android:orientation="vertical">
+
+        <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+            android:id="@+id/keyguard_indication_enterprise_disclosure"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:paddingStart="@dimen/keyguard_indication_text_padding"
+            android:paddingEnd="@dimen/keyguard_indication_text_padding"
+            android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+            android:visibility="gone"/>
+
+        <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+            android:id="@+id/keyguard_indication_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:paddingStart="@dimen/keyguard_indication_text_padding"
+            android:paddingEnd="@dimen/keyguard_indication_text_padding"
+            android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+            android:accessibilityLiveRegion="polite"/>
+
+    </LinearLayout>
 
     <FrameLayout
         android:id="@+id/preview_container"
diff --git a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml b/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml
deleted file mode 100644
index cc30a68..0000000
--- a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/keyguard_indication_area"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
-    android:layout_gravity="bottom|center_horizontal"
-    android:orientation="vertical">
-
-  <include layout="@layout/keyguard_indication_text_view" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
deleted file mode 100644
index 2b2100c..0000000
--- a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
-        android:id="@+id/keyguard_indication_enterprise_disclosure"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:paddingStart="@dimen/keyguard_indication_text_padding"
-        android:paddingEnd="@dimen/keyguard_indication_text_padding"
-        android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
-        android:visibility="gone"/>
-
-    <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
-        android:id="@+id/keyguard_indication_text"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:paddingStart="@dimen/keyguard_indication_text_padding"
-        android:paddingEnd="@dimen/keyguard_indication_text_padding"
-        android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
-        android:accessibilityLiveRegion="polite"/>
-</merge>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index be1623b..72c0ba8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -892,8 +892,8 @@
     <string name="quick_settings_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
     <!-- QuickSettings: Label for the toggle to activate dark theme (A.K.A Dark Mode). [CHAR LIMIT=20] -->
     <string name="quick_settings_ui_mode_night_label">Dark theme</string>
-    <!-- QuickSettings: Label for the dark theme tile when enabled by battery saver. [CHAR LIMIT=40] -->
-    <string name="quick_settings_ui_mode_night_label_battery_saver">Dark theme\nBattery saver</string>
+    <!-- QuickSettings: Secondary text for the dark theme tile when enabled by battery saver. [CHAR LIMIT=20] -->
+    <string name="quick_settings_dark_mode_secondary_label_battery_saver">Battery Saver</string>
     <!-- QuickSettings: Secondary text for when the Dark Mode will be enabled at sunset. [CHAR LIMIT=20] -->
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset">On at sunset</string>
     <!-- QuickSettings: Secondary text for when the Dark Mode will be on until sunrise. [CHAR LIMIT=20] -->
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
new file mode 100644
index 0000000..05acdd0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Extends the lifetime of foreground notification services such that they show for at least
+ * five seconds
+ */
+public class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
+
+    private static final String TAG = "FGSLifetimeExtender";
+    @VisibleForTesting
+    static final int MIN_FGS_TIME_MS = 5000;
+
+    private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
+    private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
+    private Handler mHandler = new Handler(Looper.getMainLooper());
+
+    public ForegroundServiceLifetimeExtender() {
+    }
+
+    @Override
+    public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
+        mNotificationSafeToRemoveCallback = callback;
+    }
+
+    @Override
+    public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
+        if ((entry.notification.getNotification().flags
+                & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
+            return false;
+        }
+
+        long currentTime = System.currentTimeMillis();
+        return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS;
+    }
+
+    @Override
+    public boolean shouldExtendLifetimeForPendingNotification(
+            @NonNull NotificationEntry entry) {
+        return shouldExtendLifetime(entry);
+    }
+
+    @Override
+    public void setShouldManageLifetime(
+            @NonNull NotificationEntry entry, boolean shouldManage) {
+        if (!shouldManage) {
+            mManagedEntries.remove(entry);
+            return;
+        }
+
+        mManagedEntries.add(entry);
+
+        Runnable r = () -> {
+            if (mManagedEntries.contains(entry)) {
+                mManagedEntries.remove(entry);
+                if (mNotificationSafeToRemoveCallback != null) {
+                    mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
+                }
+            }
+        };
+        long delayAmt = MIN_FGS_TIME_MS
+                - (System.currentTimeMillis() - entry.notification.getPostTime());
+        mHandler.postDelayed(r, delayAmt);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 96b62ac..0162deb 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -66,6 +66,9 @@
                 removeNotification(entry.notification);
             }
         });
+
+        notificationEntryManager.addNotificationLifetimeExtender(
+                new ForegroundServiceLifetimeExtender());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 35c8b74..b409129 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.doze;
 
+import android.annotation.Nullable;
 import android.app.IWallpaperManager;
 import android.content.Context;
 import android.os.RemoteException;
@@ -37,6 +38,7 @@
     private static final String TAG = "DozeWallpaperState";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    @Nullable
     private final IWallpaperManager mWallpaperManagerService;
     private final DozeParameters mDozeParameters;
     private final BiometricUnlockController mBiometricUnlockController;
@@ -88,16 +90,18 @@
 
         if (isAmbientMode != mIsAmbientMode) {
             mIsAmbientMode = isAmbientMode;
-            try {
-                long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
-                if (DEBUG) {
-                    Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
+            if (mWallpaperManagerService != null) {
+                try {
+                    long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
+                    if (DEBUG) {
+                        Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
                             + ", animationDuration: " + duration);
+                    }
+                    mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
+                } catch (RemoteException e) {
+                    // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
+                    Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
                 }
-                mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
-            } catch (RemoteException e) {
-                // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
-                Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
             }
         }
     }
@@ -106,5 +110,6 @@
     public void dump(PrintWriter pw) {
         pw.println("DozeWallpaperState:");
         pw.println(" isAmbientMode: " + mIsAmbientMode);
+        pw.println(" hasWallpaperService: " + (mWallpaperManagerService != null));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index dc9a2ce..107958f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -91,7 +91,10 @@
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
-        if (isAuto) {
+        if (powerSave) {
+            state.secondaryLabel = mContext.getResources().getString(
+                    R.string.quick_settings_dark_mode_secondary_label_battery_saver);
+        } else if (isAuto) {
             state.secondaryLabel = mContext.getResources().getString(nightMode
                     ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
                     : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
@@ -99,9 +102,7 @@
             state.secondaryLabel = null;
         }
         state.value = nightMode;
-        state.label = mContext.getString(powerSave
-                ? R.string.quick_settings_ui_mode_night_label_battery_saver
-                : R.string.quick_settings_ui_mode_night_label);
+        state.label = mContext.getString(R.string.quick_settings_ui_mode_night_label);
         state.icon = mIcon;
         state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
                 ? state.label
@@ -121,7 +122,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_DISPLAY_SETTINGS);
+        return new Intent(Settings.ACTION_DARK_THEME_SETTINGS);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index 0f295ba..48e2923 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -27,6 +27,18 @@
     boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
 
     /**
+     * It's possible that a notification was canceled before it ever became visible. This callback
+     * gives lifetime extenders a chance to make sure it shows up. For example if a foreground
+     * service is canceled too quickly but we still want to make sure a FGS notification shows.
+     * @param pendingEntry the canceled (but pending) entry
+     * @return true if the notification lifetime should be extended
+     */
+    default boolean shouldExtendLifetimeForPendingNotification(
+            @NonNull NotificationEntry pendingEntry) {
+        return false;
+    }
+
+    /**
      * Sets whether or not the lifetime should be managed by the extender.  In practice, if
      * shouldManage is true, this is where the extender starts managing the entry internally and is
      * now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f8fef7d..da0f83d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -89,7 +89,6 @@
     private NotificationRowBinder mNotificationRowBinder;
 
     private NotificationPresenter mPresenter;
-    private NotificationListenerService.RankingMap mLatestRankingMap;
     @VisibleForTesting
     protected NotificationData mNotificationData;
 
@@ -168,8 +167,7 @@
     /** Adds a {@link NotificationLifetimeExtender}. */
     public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
         mNotificationLifetimeExtenders.add(extender);
-        extender.setCallback(key -> removeNotification(key, mLatestRankingMap,
-                UNDEFINED_DISMISS_REASON));
+        extender.setCallback(key -> removeNotification(key, null, UNDEFINED_DISMISS_REASON));
     }
 
     public NotificationData getNotificationData() {
@@ -281,11 +279,25 @@
         }
 
         final NotificationEntry entry = mNotificationData.get(key);
-
-        abortExistingInflation(key);
-
         boolean lifetimeExtended = false;
 
+        // Notification was canceled before it got inflated
+        if (entry == null) {
+            NotificationEntry pendingEntry = mPendingNotifications.get(key);
+            if (pendingEntry != null) {
+                for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+                    if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
+                        extendLifetime(pendingEntry, extender);
+                        lifetimeExtended = true;
+                    }
+                }
+            }
+        }
+
+        if (!lifetimeExtended) {
+            abortExistingInflation(key);
+        }
+
         if (entry != null) {
             // If a manager needs to keep the notification around for whatever reason, we
             // keep the notification
@@ -293,7 +305,6 @@
             if (!forceRemove && !entryDismissed) {
                 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
                     if (extender.shouldExtendLifetime(entry)) {
-                        mLatestRankingMap = ranking;
                         extendLifetime(entry, extender);
                         lifetimeExtended = true;
                         break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 299511c..7b4ed3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -200,6 +200,9 @@
             removed = mEntries.remove(key);
         }
         if (removed == null) return null;
+        // NEM may pass us a null ranking map if removing a lifetime-extended notification,
+        // so use the most recent ranking
+        if (ranking == null) ranking = mRankingMap;
         mGroupManager.onEntryRemoved(removed);
         updateRankingAndSort(ranking);
         return removed;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
index bfd17b9..68ea81d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -107,6 +107,11 @@
         requestUpdateSamplingListener();
     }
 
+    void stopAndDestroy() {
+        stop();
+        mSamplingListener.destroy();
+    }
+
     @Override
     public void onViewAttachedToWindow(View view) {
         requestUpdateSamplingListener();
@@ -114,8 +119,7 @@
 
     @Override
     public void onViewDetachedFromWindow(View view) {
-        // Defer calling updateSamplingListener the attach info has not yet been reset
-        requestUpdateSamplingListener();
+        stopAndDestroy();
     }
 
     @Override
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 e9731c5..9a5f35a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -137,6 +137,7 @@
     private final MetricsLogger mMetricsLogger;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final StatusBarStateController mStatusBarStateController;
+    private final NavigationModeController mNavigationModeController;
 
     protected NavigationBarView mNavigationBarView = null;
 
@@ -253,6 +254,7 @@
         mAssistManager = assistManager;
         mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
         mOverviewProxyService = overviewProxyService;
+        mNavigationModeController = navigationModeController;
         mNavBarMode = navigationModeController.addListener(this);
     }
 
@@ -292,6 +294,7 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+        mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mMagnificationObserver);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
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 17c200e..a0e7d02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -379,8 +379,11 @@
                 mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
         boolean bluetoothVisible = false;
         if (mBluetooth != null) {
-            if (mBluetooth.isBluetoothConnected()) {
-                contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected);
+            if (mBluetooth.isBluetoothConnected()
+                    && (mBluetooth.isBluetoothAudioActive()
+                    || !mBluetooth.isBluetoothAudioProfileOnly())) {
+                contentDescription = mContext.getString(
+                        R.string.accessibility_bluetooth_connected);
                 bluetoothVisible = mBluetooth.isBluetoothEnabled();
             }
         }
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 2b0bb21..26cc74d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
@@ -127,6 +127,11 @@
         updateSamplingListener();
     }
 
+    void stopAndDestroy() {
+        stop();
+        mSamplingListener.destroy();
+    }
+
     @Override
     public void onViewAttachedToWindow(View view) {
         updateSamplingListener();
@@ -134,9 +139,7 @@
 
     @Override
     public void onViewDetachedFromWindow(View view) {
-        // isAttachedToWindow is only changed after this call to the listeners, so let's post it
-        // instead
-        postUpdateSamplingListener();
+        stopAndDestroy();
     }
 
     @Override
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 7f11759..c6dee5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -344,6 +344,7 @@
     private boolean mBrightnessMirrorVisible;
     protected BiometricUnlockController mBiometricUnlockController;
     protected LightBarController mLightBarController;
+    @Nullable
     protected LockscreenWallpaper mLockscreenWallpaper;
     @VisibleForTesting
     protected AutoHideController mAutoHideController;
@@ -693,6 +694,8 @@
 
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mFalsingManager = Dependency.get(FalsingManager.class);
+        mWallpaperSupported =
+                mContext.getSystemService(WallpaperManager.class).isWallpaperSupported();
 
         // Connect in to the status bar manager service
         mCommandQueue = getComponent(CommandQueue.class);
@@ -707,9 +710,6 @@
 
         createAndAddWindows(result);
 
-        mWallpaperSupported =
-                mContext.getSystemService(WallpaperManager.class).isWallpaperSupported();
-
         if (mWallpaperSupported) {
             // Make sure we always have the most current wallpaper info.
             IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
@@ -891,7 +891,7 @@
 
         createNavigationBar(result);
 
-        if (ENABLE_LOCKSCREEN_WALLPAPER) {
+        if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) {
             mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
         }
 
@@ -2748,7 +2748,9 @@
 
     @Override
     public void setLockscreenUser(int newUserId) {
-        mLockscreenWallpaper.setCurrentUser(newUserId);
+        if (mLockscreenWallpaper != null) {
+            mLockscreenWallpaper.setCurrentUser(newUserId);
+        }
         mScrimController.setCurrentUser(newUserId);
         if (mWallpaperSupported) {
             mWallpaperChangedReceiver.onReceive(mContext, null);
@@ -3052,10 +3054,21 @@
         return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
     }
 
+    private boolean isAutomotive() {
+        return mContext != null
+                && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
     private boolean updateIsKeyguard() {
         boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
                 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 
+        if (mScreenLifecycle == null && isAutomotive()) {
+            // TODO(b/146144370): workaround to avoid NPE when device goes into STR (Suspend to RAM)
+            Log.w(TAG, "updateIsKeyguard(): mScreenLifeCycle not set yet");
+            mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
+        }
+
         // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
         // there's no surface we can show to the user. Note that the device goes fully interactive
         // late in the transition, so we also allow the device to start dozing once the screen has
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index a870590..06921980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -224,7 +224,7 @@
             mVisualStabilityManager.setUpWithPresenter(this);
             mGutsManager.setUpWithPresenter(this,
                     notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
-            // ForegroundServiceControllerListener adds its listener in its constructor
+            // ForegroundServiceNotificationListener adds its listener in its constructor
             // but we need to request it here in order for it to be instantiated.
             // TODO: figure out how to do this correctly once Dependency.get() is gone.
             Dependency.get(ForegroundServiceNotificationListener.class);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 42e02d5..0c5b851 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -31,6 +31,8 @@
 
     boolean isBluetoothConnected();
     boolean isBluetoothConnecting();
+    boolean isBluetoothAudioProfileOnly();
+    boolean isBluetoothAudioActive();
     String getConnectedDeviceName();
     void setBluetoothEnabled(boolean enabled);
     Collection<CachedBluetoothDevice> getDevices();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 78e845a..351579a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -34,6 +34,7 @@
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
 import java.io.FileDescriptor;
@@ -66,6 +67,8 @@
 
     private boolean mEnabled;
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+    private boolean mAudioProfileOnly;
+    private boolean mIsActive;
 
     private final H mHandler = new H(Looper.getMainLooper());
     private int mState;
@@ -103,6 +106,8 @@
         }
         pw.print("  mEnabled="); pw.println(mEnabled);
         pw.print("  mConnectionState="); pw.println(stateToString(mConnectionState));
+        pw.print("  mAudioProfileOnly="); pw.println(mAudioProfileOnly);
+        pw.print("  mIsActive="); pw.println(mIsActive);
         pw.print("  mConnectedDevices="); pw.println(mConnectedDevices);
         pw.print("  mCallbacks.size="); pw.println(mHandler.mCallbacks.size());
         pw.println("  Bluetooth Devices:");
@@ -176,6 +181,16 @@
     }
 
     @Override
+    public boolean isBluetoothAudioProfileOnly() {
+        return mAudioProfileOnly;
+    }
+
+    @Override
+    public boolean isBluetoothAudioActive() {
+        return mIsActive;
+    }
+
+    @Override
     public void setBluetoothEnabled(boolean enabled) {
         if (mLocalBluetoothManager != null) {
             mLocalBluetoothManager.getBluetoothAdapter().setBluetoothEnabled(enabled);
@@ -239,6 +254,48 @@
             mConnectionState = state;
             mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
         }
+        updateAudioProfile();
+    }
+
+    private void updateActive() {
+        boolean isActive = false;
+
+        for (CachedBluetoothDevice device : getDevices()) {
+            isActive |= device.isActiveDevice(BluetoothProfile.HEADSET)
+                    || device.isActiveDevice(BluetoothProfile.A2DP)
+                    || device.isActiveDevice(BluetoothProfile.HEARING_AID);
+        }
+
+        if (mIsActive != isActive) {
+            mIsActive = isActive;
+            mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+        }
+    }
+
+    private void updateAudioProfile() {
+        boolean audioProfileConnected = false;
+        boolean otherProfileConnected = false;
+
+        for (CachedBluetoothDevice device : getDevices()) {
+            for (LocalBluetoothProfile profile : device.getProfiles()) {
+                int profileId = profile.getProfileId();
+                boolean isConnected = device.isConnectedProfile(profile);
+                if (profileId == BluetoothProfile.HEADSET
+                        || profileId == BluetoothProfile.A2DP
+                        || profileId == BluetoothProfile.HEARING_AID) {
+                    audioProfileConnected |= isConnected;
+                } else {
+                    otherProfileConnected |= isConnected;
+                }
+            }
+        }
+
+        boolean audioProfileOnly = (audioProfileConnected && !otherProfileConnected);
+        if (audioProfileOnly != mAudioProfileOnly) {
+            mAudioProfileOnly = audioProfileOnly;
+            mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+        }
+
     }
 
     @Override
@@ -294,6 +351,16 @@
     }
 
     @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+        if (DEBUG) {
+            Log.d(TAG, "ActiveDeviceChanged=" + activeDevice.getAddress()
+                    + " profileId=" + bluetoothProfile);
+        }
+        updateActive();
+        mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+    }
+
+    @Override
     public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
         if (DEBUG) {
             Log.d(TAG, "ACLConnectionStateChanged=" + cachedDevice.getAddress() + " "
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java
new file mode 100644
index 0000000..b1dabdd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceLifetimeExtenderTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.service.notification.StatusBarNotification;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ForegroundServiceLifetimeExtenderTest extends SysuiTestCase {
+    private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
+    private StatusBarNotification mSbn;
+    private NotificationEntry mEntry;
+    private Notification mNotif;
+
+    @Before
+    public void setup() {
+        mNotif = new Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text")
+                .build();
+
+        mSbn = mock(StatusBarNotification.class);
+        when(mSbn.getNotification()).thenReturn(mNotif);
+
+        mEntry = new NotificationEntry(mSbn);
+    }
+
+    @Test
+    public void testShouldExtendLifetime_should_foreground() {
+        // Extend the lifetime of a FGS notification iff it has not been visible
+        // for the minimum time
+        mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis());
+        assertTrue(mExtender.shouldExtendLifetime(mEntry));
+    }
+
+    @Test
+    public void testShouldExtendLifetime_shouldNot_foreground() {
+        mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+        when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
+        assertFalse(mExtender.shouldExtendLifetime(mEntry));
+    }
+
+    @Test
+    public void testShouldExtendLifetime_shouldNot_notForeground() {
+        mNotif.flags = 0;
+        when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
+        assertFalse(mExtender.shouldExtendLifetime(mEntry));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 766ad97..7fa094b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -39,6 +39,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.systemui.SysuiTestCase;
 
@@ -229,4 +230,28 @@
         assertTrue(mBluetoothControllerImpl.isBluetoothConnected());
         verify(callback, atLeastOnce()).onBluetoothStateChange(anyBoolean());
     }
+
+    @Test
+    public void testOnActiveDeviceChanged_updatesAudioActive() {
+        assertFalse(mBluetoothControllerImpl.isBluetoothAudioActive());
+        assertFalse(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
+
+        CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
+        mDevices.add(device);
+        when(device.isActiveDevice(BluetoothProfile.HEADSET)).thenReturn(true);
+
+        List<LocalBluetoothProfile> profiles = new ArrayList<>();
+        LocalBluetoothProfile profile = mock(LocalBluetoothProfile.class);
+        profiles.add(profile);
+        when(profile.getProfileId()).thenReturn(BluetoothProfile.HEADSET);
+        when(device.getProfiles()).thenReturn(profiles);
+        when(device.isConnectedProfile(profile)).thenReturn(true);
+
+        mBluetoothControllerImpl.onAclConnectionStateChanged(device,
+                BluetoothProfile.STATE_CONNECTED);
+        mBluetoothControllerImpl.onActiveDeviceChanged(device, BluetoothProfile.HEADSET);
+
+        assertTrue(mBluetoothControllerImpl.isBluetoothAudioActive());
+        assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index cac6bf7..6cbd175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -57,6 +57,16 @@
     }
 
     @Override
+    public boolean isBluetoothAudioProfileOnly() {
+        return false;
+    }
+
+    @Override
+    public boolean isBluetoothAudioActive() {
+        return false;
+    }
+
+    @Override
     public String getConnectedDeviceName() {
         return null;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 05b937a..9bad734c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -217,6 +217,8 @@
 
     private final AppOpsManager mAppOpsManager;
 
+    private final ActivityTaskManagerInternal mActivityTaskManagerService;
+
     private final MainHandler mMainHandler;
 
     private final GlobalActionPerformer mGlobalActionPerformer;
@@ -308,6 +310,7 @@
         mMainHandler = new MainHandler(mContext.getMainLooper());
         mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+        mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
 
         registerBroadcastReceivers();
         new AccessibilityContentObserver(mMainHandler).register(
@@ -1635,7 +1638,8 @@
                 if (service == null) {
                     service = new AccessibilityServiceConnection(userState, mContext, componentName,
                             installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
-                            this, mWindowManagerService, mGlobalActionPerformer);
+                            this, mWindowManagerService, mGlobalActionPerformer,
+                            mActivityTaskManagerService);
                 } else if (userState.mBoundServices.contains(service)) {
                     continue;
                 }
@@ -3028,7 +3032,7 @@
                     userState, mContext,
                     COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
                     AccessibilityManagerService.this, mWindowManagerService,
-                    mGlobalActionPerformer) {
+                    mGlobalActionPerformer, mActivityTaskManagerService) {
                 @Override
                 public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
                     return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index b66caa5..91031e6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -34,6 +34,7 @@
 
 import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy;
 import com.android.server.accessibility.AccessibilityManagerService.UserState;
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.lang.ref.WeakReference;
@@ -58,6 +59,7 @@
     */
     final WeakReference<UserState> mUserStateWeakReference;
     final Intent mIntent;
+    final ActivityTaskManagerInternal mActivityTaskManagerService;
 
     private final Handler mMainHandler;
 
@@ -69,7 +71,8 @@
             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
             Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport,
             WindowManagerInternal windowManagerInternal,
-            GlobalActionPerformer globalActionPerfomer) {
+            GlobalActionPerformer globalActionPerfomer,
+            ActivityTaskManagerInternal activityTaskManagerService) {
         super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
                 securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer);
         mUserStateWeakReference = new WeakReference<UserState>(userState);
@@ -77,6 +80,7 @@
         mMainHandler = mainHandler;
         mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                 com.android.internal.R.string.accessibility_binding_label);
+        mActivityTaskManagerService = activityTaskManagerService;
         final long identity = Binder.clearCallingIdentity();
         try {
             mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, mSystemSupport.getPendingIntentActivity(
@@ -103,6 +107,9 @@
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
+        mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
+                mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
+                userState.mUserId);
     }
 
     public void unbindLocked() {
@@ -111,6 +118,9 @@
         if (userState == null) return;
         userState.removeServiceLocked(this);
         mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
+        // Set uid to -1 to clear allowing app switches.
+        mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
+                /* uid= */ -1, userState.mUserId);
         resetLocked();
     }
 
@@ -208,6 +218,12 @@
     @Override
     public void onServiceDisconnected(ComponentName componentName) {
         binderDied();
+        UserState userState = mUserStateWeakReference.get();
+        if (userState != null) {
+            // Set uid to -1 to clear allowing app switches.
+            mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
+                    /* uid= */ -1, userState.mUserId);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 56db8898..978cc57 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -53,6 +53,7 @@
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.util.Slog;
+
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.DisableCarModeActivity;
@@ -67,8 +68,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -90,10 +89,15 @@
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
     private boolean mPowerSave = false;
+    // Do not change configuration now. wait until screen turns off.
+    // This prevents jank and activity restart when the user
+    // is actively using the device
+    private boolean mWaitForScreenOff = false;
     private int mDefaultUiModeType;
     private boolean mCarModeKeepsScreenOn;
     private boolean mDeskModeKeepsScreenOn;
     private boolean mTelevision;
+    private boolean mCar;
     private boolean mWatch;
     private boolean mVrHeadset;
     private boolean mComputedNightMode;
@@ -201,24 +205,27 @@
         public void onTwilightStateChanged(@Nullable TwilightState state) {
             synchronized (mLock) {
                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                    final IntentFilter intentFilter =
-                            new IntentFilter(ACTION_SCREEN_OFF);
-                    getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+                    if (mCar) {
+                        updateLocked(0, 0);
+                    } else {
+                        registerScreenOffEvent();
+                    }
                 }
             }
         }
     };
 
+    /**
+     *  DO NOT USE DIRECTLY
+     *  see register registerScreenOffEvent and unregisterScreenOffEvent
+     */
     private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
+                // must unregister first before updating
+                unregisterScreenOffEvent();
                 updateLocked(0, 0);
-                try {
-                    getContext().unregisterReceiver(mOnScreenOffHandler);
-                } catch (IllegalArgumentException e) {
-                    // we ignore this exception if the receiver is unregistered already.
-                }
             }
         }
     };
@@ -257,7 +264,7 @@
             int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
                     mNightMode, 0);
             mode = mode == UiModeManager.MODE_NIGHT_AUTO
-                    ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
+                    ? UiModeManager.MODE_NIGHT_YES : mode;
             SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
         }
     };
@@ -320,6 +327,7 @@
         final PackageManager pm = context.getPackageManager();
         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
         mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
 
         updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
@@ -328,7 +336,7 @@
         SystemServerInitThreadPool.get().submit(() -> {
             synchronized (mLock) {
                 updateConfigurationLocked();
-                sendConfigurationLocked();
+                applyConfigurationExternallyLocked();
             }
 
         }, TAG + ".onStart");
@@ -397,6 +405,22 @@
         return oldNightMode != mNightMode;
     }
 
+    private void registerScreenOffEvent() {
+        mWaitForScreenOff = true;
+        final IntentFilter intentFilter =
+                new IntentFilter(Intent.ACTION_SCREEN_OFF);
+        getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+    }
+
+    private void unregisterScreenOffEvent() {
+        mWaitForScreenOff = false;
+        try {
+            getContext().unregisterReceiver(mOnScreenOffHandler);
+        } catch (IllegalArgumentException e) {
+            // we ignore this exception if the receiver is unregistered already.
+        }
+    }
+
     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
         @Override
         public void enableCarMode(int flags) {
@@ -475,28 +499,21 @@
                 synchronized (mLock) {
                     if (mNightMode != mode) {
                         if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                            try {
-                                getContext().unregisterReceiver(mOnScreenOffHandler);
-                            } catch (IllegalArgumentException e) {
-                                // we ignore this exception if the receiver is unregistered already.
-                            }
-                        }
-                        // Only persist setting if not in car mode
-                        if (!mCarModeEnabled) {
-                            Secure.putIntForUser(getContext().getContentResolver(),
-                                    Secure.UI_NIGHT_MODE, mode, user);
-                            Secure.putIntForUser(getContext().getContentResolver(),
-                                    OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+                            unregisterScreenOffEvent();
                         }
 
                         mNightMode = mode;
                         mNightModeOverride = mode;
-                        //on screen off will update configuration instead
-                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO) {
+
+                        // Only persist setting if not in car mode
+                        if (!mCarModeEnabled) {
+                            persistNightMode(user);
+                        }
+                        // on screen off will update configuration instead
+                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) {
                             updateLocked(0, 0);
                         } else {
-                            getContext().registerReceiver(
-                                    mOnScreenOffHandler, new IntentFilter(ACTION_SCREEN_OFF));
+                            registerScreenOffEvent();
                         }
                     }
                 }
@@ -541,13 +558,11 @@
         @Override
         public boolean setNightModeActivated(boolean active) {
             synchronized (mLock) {
+                final int user = UserHandle.getCallingUserId();
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                        try {
-                            getContext().unregisterReceiver(mOnScreenOffHandler);
-                        } catch (IllegalArgumentException e) {
-                        }
+                        unregisterScreenOffEvent();
                         mNightModeOverride = active
                                 ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
@@ -558,7 +573,8 @@
                         mNightMode = UiModeManager.MODE_NIGHT_NO;
                     }
                     updateConfigurationLocked();
-                    sendConfigurationLocked();
+                    applyConfigurationExternallyLocked();
+                    persistNightMode(user);
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -644,6 +660,13 @@
         }
     }
 
+    private void persistNightMode(int user) {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.UI_NIGHT_MODE, mNightMode, user);
+        Secure.putIntForUser(getContext().getContentResolver(),
+                OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+    }
+
     private void updateConfigurationLocked() {
         int uiMode = mDefaultUiModeType;
         if (mUiModeLocked) {
@@ -689,15 +712,14 @@
         }
 
         mCurUiMode = uiMode;
-        if (!mHoldingConfiguration) {
+        if (!mHoldingConfiguration || !mWaitForScreenOff) {
             mConfiguration.uiMode = uiMode;
         }
     }
 
-    private void sendConfigurationLocked() {
+    private void applyConfigurationExternallyLocked() {
         if (mSetUiMode != mConfiguration.uiMode) {
             mSetUiMode = mConfiguration.uiMode;
-
             try {
                 ActivityTaskManager.getService().updateConfiguration(mConfiguration);
             } catch (RemoteException e) {
@@ -877,7 +899,7 @@
         }
 
         // Send the new configuration.
-        sendConfigurationLocked();
+        applyConfigurationExternallyLocked();
 
         // If we did not start a dock app, then start dreaming if supported.
         if (category != null && !dockAppStarted) {
@@ -955,7 +977,6 @@
             final int user = UserHandle.getCallingUserId();
             Secure.putIntForUser(getContext().getContentResolver(),
                     OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
-
         }
     }
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1432f57..0d28822 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1289,6 +1289,33 @@
     }
 
     protected UserAccounts getUserAccounts(int userId) {
+        try {
+            return getUserAccountsNotChecked(userId);
+        } catch (RuntimeException e) {
+            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+                // Let it go...
+                throw e;
+            }
+            // User accounts database is corrupted, we must wipe out the whole user, otherwise the
+            // system will crash indefinitely
+            Slog.wtf(TAG, "Removing user " + userId + " due to exception (" + e + ") reading its "
+                    + "account database");
+            if (userId == ActivityManager.getCurrentUser() && userId != UserHandle.USER_SYSTEM) {
+                Slog.i(TAG, "Switching to system user first");
+                try {
+                    ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+                } catch (RemoteException re) {
+                    Slog.e(TAG, "Could not switch to " + UserHandle.USER_SYSTEM + ": " + re);
+                }
+            }
+            if (!getUserManager().removeUserEvenWhenDisallowed(userId)) {
+                Slog.e(TAG, "could not remove user " + userId);
+            }
+            throw e;
+        }
+    }
+
+    private UserAccounts getUserAccountsNotChecked(int userId) {
         synchronized (mUsers) {
             UserAccounts accounts = mUsers.get(userId);
             boolean validateAccounts = false;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9b911aa..b5dbe9c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2790,6 +2790,7 @@
     }
 
     public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) {
+        enforceModifyAudioRoutingPermission();
         setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(),
                 userId);
     }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 31632dc..177e2d8 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -41,6 +41,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
 
@@ -215,7 +216,9 @@
     private IActivityTaskManager mActivityTaskManager;
     private PackageManager mPackageManager;
 
-    public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+    private final Injector mInjector;
+
+    AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
             int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
@@ -223,6 +226,24 @@
             HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
             PackageManager packageManager) {
+        this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
+                lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
+                lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
+                darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
+                ambientBrightnessThresholds, screenBrightnessThresholds, shortTermModelTimeout,
+                packageManager);
+    }
+
+    @VisibleForTesting
+    AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
+            SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+            int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
+            int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
+            long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
+            HysteresisLevels ambientBrightnessThresholds,
+            HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
+            PackageManager packageManager) {
+        mInjector = injector;
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
         mBrightnessMapper = mapper;
@@ -725,8 +746,8 @@
         float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
                 mForegroundAppCategory);
 
-        int newScreenAutoBrightness =
-                clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
+        int newScreenAutoBrightness = Math.round(clampScreenBrightness(
+                value * PowerManager.BRIGHTNESS_ON));
 
         // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
         // in which case we ignore the new screen brightness if it doesn't differ enough from the
@@ -750,10 +771,10 @@
             }
 
             mScreenAutoBrightness = newScreenAutoBrightness;
-            mScreenBrighteningThreshold =
-                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness);
-            mScreenDarkeningThreshold =
-                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness);
+            mScreenBrighteningThreshold = clampScreenBrightness(
+                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
+            mScreenDarkeningThreshold = clampScreenBrightness(
+                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
 
             if (sendUpdate) {
                 mCallbacks.updateBrightness();
@@ -761,7 +782,7 @@
         }
     }
 
-    private int clampScreenBrightness(int value) {
+    private float clampScreenBrightness(float value) {
         return MathUtils.constrain(value,
                 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
     }
@@ -839,7 +860,7 @@
         }
         // The ActivityTaskManager's lock tends to get contended, so this is done in a background
         // thread and applied via this thread's handler synchronously.
-        BackgroundThread.getHandler().post(new Runnable() {
+        mInjector.getBackgroundThreadHandler().post(new Runnable() {
             public void run() {
                 try {
                     // The foreground app is the top activity of the focused tasks stack.
@@ -965,6 +986,9 @@
         private int mCount;
 
         public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+            if (lightSensorRate <= 0) {
+                throw new IllegalArgumentException("lightSensorRate must be above 0");
+            }
             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
             mRingLux = new float[mCapacity];
             mRingTime = new long[mCapacity];
@@ -1076,4 +1100,10 @@
             return index;
         }
     }
+
+    public static class Injector {
+        public Handler getBackgroundThreadHandler() {
+            return BackgroundThread.getHandler();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 2db1d03..f0a505d 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,13 +18,16 @@
 
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 
 /**
  * A helper class for handling access to illuminance hysteresis level values.
  */
-final class HysteresisLevels {
+@VisibleForTesting
+public class HysteresisLevels {
     private static final String TAG = "HysteresisLevels";
 
     // Default hysteresis constraints for brightening or darkening.
@@ -60,7 +63,7 @@
     /**
      * Return the brightening hysteresis threshold for the given value level.
      */
-    float getBrighteningThreshold(float value) {
+    public float getBrighteningThreshold(float value) {
         float brightConstant = getReferenceLevel(value, mBrighteningThresholds);
         float brightThreshold = value * (1.0f + brightConstant);
         if (DEBUG) {
@@ -73,7 +76,7 @@
     /**
      * Return the darkening hysteresis threshold for the given value level.
      */
-    float getDarkeningThreshold(float value) {
+    public float getDarkeningThreshold(float value) {
         float darkConstant = getReferenceLevel(value, mDarkeningThresholds);
         float darkThreshold = value * (1.0f - darkConstant);
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index ea4f9c4..dd522b9 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -637,7 +637,7 @@
         return new Notification.Builder(context, SystemNotificationChannels.NETWORK_ALERTS)
                 .setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on)
                 .setWhen(0)
-                .setOngoing(true)
+                .setOngoing(false)
                 .setAutoCancel(true)
                 .setColor(context.getColor(
                         com.android.internal.R.color.system_notification_accent_color))
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f20957a..09a7943 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -163,6 +163,7 @@
 import android.os.IInterface;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -281,6 +282,9 @@
     public static final boolean ENABLE_CHILD_NOTIFICATIONS
             = SystemProperties.getBoolean("debug.child_notifs", true);
 
+    // pullStats report request: undecorated remote view stats
+    public static final int REPORT_REMOTE_VIEWS = 0x01;
+
     static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
             "debug.notification.interruptiveness", false);
 
@@ -3734,6 +3738,8 @@
             try {
                 if (filter.stats) {
                     dumpJson(pw, filter);
+                } else if (filter.rvStats) {
+                    dumpRemoteViewStats(pw, filter);
                 } else if (filter.proto) {
                     dumpProto(fd, filter);
                 } else if (filter.criticalPriority) {
@@ -4210,6 +4216,49 @@
             new NotificationShellCmd(NotificationManagerService.this)
                     .exec(this, in, out, err, args, callback, resultReceiver);
         }
+
+        /**
+         * Get stats committed after startNs
+         *
+         * @param startNs Report stats committed after this time in nanoseconds.
+         * @param report  Indicatess which section to include in the stats.
+         * @param doAgg   Whether to aggregate the stats or keep them separated.
+         * @param out   List of protos of individual commits or one representing the
+         *                aggregate.
+         * @return the report time in nanoseconds, or 0 on error.
+         */
+        @Override
+        public long pullStats(long startNs, int report, boolean doAgg,
+                List<ParcelFileDescriptor> out) {
+            checkCallerIsSystemOrShell();
+            long startMs = TimeUnit.MILLISECONDS.convert(startNs, TimeUnit.NANOSECONDS);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                switch (report) {
+                    case REPORT_REMOTE_VIEWS:
+                        Slog.e(TAG, "pullStats REPORT_REMOTE_VIEWS from: "
+                                + startMs + "  wtih " + doAgg);
+                        PulledStats stats = mUsageStats.remoteViewStats(startMs, doAgg);
+                        if (stats != null) {
+                            out.add(stats.toParcelFileDescriptor(report));
+                            Slog.e(TAG, "exiting pullStats with: " + out.size());
+                            long endNs = TimeUnit.NANOSECONDS
+                                    .convert(stats.endTimeMs(), TimeUnit.MILLISECONDS);
+                            return endNs;
+                        }
+                        Slog.e(TAG, "null stats for: " + report);
+                }
+            } catch (IOException e) {
+
+                Slog.e(TAG, "exiting pullStats: on error", e);
+                return 0;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            Slog.e(TAG, "exiting pullStats: bad request");
+            return 0;
+        }
     };
 
     @VisibleForTesting
@@ -4425,6 +4474,15 @@
         pw.println(dump);
     }
 
+    private void dumpRemoteViewStats(PrintWriter pw, @NonNull DumpFilter filter) {
+        PulledStats stats = mUsageStats.remoteViewStats(filter.since, true);
+        if (stats == null) {
+            pw.println("no remote view stats reported.");
+            return;
+        }
+        stats.dump(REPORT_REMOTE_VIEWS, pw, filter);
+    }
+
     private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
         synchronized (mNotificationLock) {
@@ -5357,8 +5415,16 @@
             }
 
             synchronized (mNotificationLock) {
-                // Look for the notification, searching both the posted and enqueued lists.
-                NotificationRecord r = findNotificationLocked(mPkg, mTag, mId, mUserId);
+                // If the notification is currently enqueued, repost this runnable so it has a
+                // chance to notify listeners
+                if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
+                        != null) {
+                    mHandler.post(this);
+                    return;
+                }
+                // Look for the notification in the posted list, since we already checked enqueued.
+                NotificationRecord r =
+                        findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
                 if (r != null) {
                     // The notification was found, check if it should be removed.
 
@@ -8559,6 +8625,7 @@
         public boolean zen;
         public long since;
         public boolean stats;
+        public boolean rvStats;
         public boolean redact = true;
         public boolean proto = false;
         public boolean criticalPriority = false;
@@ -8594,6 +8661,14 @@
                     } else {
                         filter.since = 0;
                     }
+                } else if ("--remote-view-stats".equals(a)) {
+                    filter.rvStats = true;
+                    if (ai < args.length-1) {
+                        ai++;
+                        filter.since = Long.parseLong(args[ai]);
+                    } else {
+                        filter.since = 0;
+                    }
                 } else if (PRIORITY_ARG.equals(a)) {
                     // Bugreport will call the service twice with priority arguments, first to dump
                     // critical sections and then non critical ones. Set approriate filters
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 3c2169a..c46aad9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1289,6 +1289,18 @@
         return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
     }
 
+    public boolean hasUndecoratedRemoteView() {
+        Notification notification = getNotification();
+        Class<? extends Notification.Style> style = notification.getNotificationStyle();
+        boolean hasDecoratedStyle = style != null
+                && (Notification.DecoratedCustomViewStyle.class.equals(style)
+                || Notification.DecoratedMediaCustomViewStyle.class.equals(style));
+        boolean hasCustomRemoteView = notification.contentView != null
+                || notification.bigContentView != null
+                || notification.headsUpContentView != null;
+        return hasCustomRemoteView && !hasDecoratedStyle;
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d630b9a..d81a6ae 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -149,6 +149,7 @@
             stats.numPostedByApp++;
             stats.updateInterarrivalEstimate(now);
             stats.countApiUse(notification);
+            stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0);
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
         if (ENABLE_SQLITE_LOG) {
@@ -326,6 +327,15 @@
         return dump;
     }
 
+    public PulledStats remoteViewStats(long startMs, boolean aggregate) {
+        if (ENABLE_SQLITE_LOG) {
+            if (aggregate) {
+                return mSQLiteLog.remoteViewAggStats(startMs);
+            }
+        }
+        return null;
+    }
+
     public synchronized void dump(PrintWriter pw, String indent, DumpFilter filter) {
         if (ENABLE_AGGREGATED_IN_MEMORY_STATS) {
             for (AggregatedStats as : mStats.values()) {
@@ -403,6 +413,7 @@
         public int numRateViolations;
         public int numAlertViolations;
         public int numQuotaViolations;
+        public int numUndecoratedRemoteViews;
         public long mLastAccessTime;
 
         public AggregatedStats(Context context, String key) {
@@ -669,6 +680,8 @@
             output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
             output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
             output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
+            output.append(indentPlusTwo);
+            output.append("numUndecorateRVs=").append(numUndecoratedRemoteViews).append("\n");
             output.append(indent).append("}");
             return output.toString();
         }
@@ -1027,7 +1040,7 @@
         private static final int MSG_DISMISS = 4;
 
         private static final String DB_NAME = "notification_log.db";
-        private static final int DB_VERSION = 5;
+        private static final int DB_VERSION = 7;
 
         /** Age in ms after which events are pruned from the DB. */
         private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L;  // 1 week
@@ -1060,6 +1073,7 @@
         private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms";
         private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms";
         private static final String COL_EXPAND_COUNT = "expansion_count";
+        private static final String COL_UNDECORATED = "undecorated";
 
 
         private static final int EVENT_TYPE_POST = 1;
@@ -1085,12 +1099,20 @@
                 "COUNT(*) AS cnt, " +
                 "SUM(" + COL_MUTED + ") as muted, " +
                 "SUM(" + COL_NOISY + ") as noisy, " +
-                "SUM(" + COL_DEMOTED + ") as demoted " +
+                "SUM(" + COL_DEMOTED + ") as demoted, " +
+                "SUM(" + COL_UNDECORATED + ") as undecorated " +
                 "FROM " + TAB_LOG + " " +
                 "WHERE " +
                 COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
                 " AND " + COL_EVENT_TIME + " > %d " +
                 " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+        private static final String UNDECORATED_QUERY = "SELECT " +
+                COL_PKG + ", " +
+                "MAX(" + COL_EVENT_TIME + ") as max_time " +
+                "FROM " + TAB_LOG + " " +
+                "WHERE " + COL_UNDECORATED + "> 0 " +
+                " AND " + COL_EVENT_TIME + " > %d " +
+                "GROUP BY " + COL_PKG;
 
         public SQLiteLog(Context context) {
             HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
@@ -1146,7 +1168,8 @@
                             COL_AIRTIME_MS + " INT," +
                             COL_FIRST_EXPANSIONTIME_MS + " INT," +
                             COL_AIRTIME_EXPANDED_MS + " INT," +
-                            COL_EXPAND_COUNT + " INT" +
+                            COL_EXPAND_COUNT + " INT," +
+                            COL_UNDECORATED + " INT" +
                             ")");
                 }
 
@@ -1256,6 +1279,7 @@
             } else {
                 putPosttimeVisibility(r, cv);
             }
+            cv.put(COL_UNDECORATED, (r.hasUndecoratedRemoteView() ? 1 : 0));
             SQLiteDatabase db = mHelper.getWritableDatabase();
             if (db.insert(TAB_LOG, null, cv) < 0) {
                 Log.wtf(TAG, "Error while trying to insert values: " + cv);
@@ -1332,5 +1356,22 @@
             }
             return dump;
         }
+
+        public PulledStats remoteViewAggStats(long startMs) {
+            PulledStats stats = new PulledStats(startMs);
+            SQLiteDatabase db = mHelper.getReadableDatabase();
+            String q = String.format(UNDECORATED_QUERY, startMs);
+            Cursor cursor = db.rawQuery(q, null);
+            try {
+                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+                    String pkg = cursor.getString(0);
+                    long maxTimeMs = cursor.getLong(1);
+                    stats.addUndecoratedPackage(pkg, maxTimeMs);
+                }
+            } finally {
+                cursor.close();
+            }
+            return stats;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/PulledStats.java b/services/core/java/com/android/server/notification/PulledStats.java
new file mode 100644
index 0000000..ada890a
--- /dev/null
+++ b/services/core/java/com/android/server/notification/PulledStats.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.notification;
+
+import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS;
+
+import android.os.ParcelFileDescriptor;
+import android.service.notification.NotificationRemoteViewsProto;
+import android.service.notification.PackageRemoteViewInfoProto;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class PulledStats {
+    static final String TAG = "PulledStats";
+
+    private final long mTimePeriodStartMs;
+    private long mTimePeriodEndMs;
+    private List<String> mUndecoratedPackageNames;
+
+    public PulledStats(long startMs) {
+        mTimePeriodEndMs = mTimePeriodStartMs = startMs;
+        mUndecoratedPackageNames = new ArrayList<>();
+    }
+
+    ParcelFileDescriptor toParcelFileDescriptor(int report)
+            throws IOException {
+        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+        switch(report) {
+            case REPORT_REMOTE_VIEWS:
+                Thread thr = new Thread("NotificationManager pulled metric output") {
+                    public void run() {
+                        try {
+                            FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(
+                                    fds[1]);
+                            final ProtoOutputStream proto = new ProtoOutputStream(fout);
+                            writeToProto(report, proto);
+                            proto.flush();
+                            fout.close();
+                        } catch (IOException e) {
+                            Slog.w(TAG, "Failure writing pipe", e);
+                        }
+                    }
+                };
+                thr.start();
+                break;
+
+            default:
+                Slog.w(TAG, "Unknown pulled stats request: " + report);
+                break;
+        }
+        return fds[0];
+    }
+
+    /*
+     * @return the most recent timestamp in the report, as nanoseconds.
+     */
+    public long endTimeMs() {
+        return mTimePeriodEndMs;
+    }
+
+    public void dump(int report, PrintWriter pw, NotificationManagerService.DumpFilter filter) {
+        switch(report) {
+            case REPORT_REMOTE_VIEWS:
+                pw.print("  Packages with undecordated notifications (");
+                pw.print(mTimePeriodStartMs);
+                pw.print(" - ");
+                pw.print(mTimePeriodEndMs);
+                pw.println("):");
+                if (mUndecoratedPackageNames.size() == 0) {
+                    pw.println("    none");
+                } else {
+                    for (String pkg : mUndecoratedPackageNames) {
+                        if (!filter.filtered || pkg.equals(filter.pkgFilter)) {
+                            pw.println("    " + pkg);
+                        }
+                    }
+                }
+                break;
+
+            default:
+                pw.println("Unknown pulled stats request: " + report);
+                break;
+        }
+    }
+
+    @VisibleForTesting
+    void writeToProto(int report, ProtoOutputStream proto) {
+        switch(report) {
+            case REPORT_REMOTE_VIEWS:
+                for (String pkg: mUndecoratedPackageNames) {
+                    long token = proto.start(NotificationRemoteViewsProto.PACKAGE_REMOTE_VIEW_INFO);
+                    proto.write(PackageRemoteViewInfoProto.PACKAGE_NAME, pkg);
+                    proto.end(token);
+                }
+                break;
+
+            default:
+                Slog.w(TAG, "Unknown pulled stats request: " + report);
+                break;
+        }
+    }
+
+    public void addUndecoratedPackage(String packageName, long timestampMs) {
+        mUndecoratedPackageNames.add(packageName);
+        mTimePeriodEndMs = Math.max(mTimePeriodEndMs, timestampMs);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0329e2c..fd8db4b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -259,6 +259,10 @@
         // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
         // atomic install which needs to query sessions, which requires lock on mSessions.
         for (PackageInstallerSession session : stagedSessionsToRestore) {
+            if (mPm.isDeviceUpgrading() && !session.isStagedAndInTerminalState()) {
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Build fingerprint has changed");
+            }
             mStagingManager.restoreSession(session);
         }
         // Broadcasts are not sent while we restore sessions on boot, since no processes would be
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 4c33bec..b844362 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -48,6 +48,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PermissionWhitelistFlags;
 import android.content.pm.PackageManagerInternal;
@@ -2601,7 +2602,7 @@
 
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
-        flags = updatePermissions(changingPkgName, changingPkg, flags);
+        flags = updatePermissions(changingPkgName, changingPkg, flags, callback);
 
         synchronized (mLock) {
             if (mBackgroundPermissions == null) {
@@ -2651,7 +2652,8 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    private int updatePermissions(String packageName, PackageParser.Package pkg, int flags) {
+    private int updatePermissions(String packageName, PackageParser.Package pkg, int flags,
+            @Nullable PermissionCallback callback) {
         Set<BasePermission> needsUpdate = null;
         synchronized (mLock) {
             final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
@@ -2665,6 +2667,44 @@
                         && (pkg == null || !hasPermission(pkg, bp.getName()))) {
                         Slog.i(TAG, "Removing old permission tree: " + bp.getName()
                                 + " from package " + bp.getSourcePackageName());
+                        if (bp.isRuntime()) {
+                            final int[] userIds = mUserManagerInt.getUserIds();
+                            final int numUserIds = userIds.length;
+                            for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+                                final int userId = userIds[userIdNum];
+
+                                mPackageManagerInt.forEachPackage((Package p) -> {
+                                    final String pName = p.packageName;
+                                    final ApplicationInfo appInfo =
+                                            mPackageManagerInt.getApplicationInfo(pName, 0,
+                                                    Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
+                                    if (appInfo != null
+                                            && appInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                                        return;
+                                    }
+
+                                    final String permissionName = bp.getName();
+                                    if (checkPermission(permissionName, pName, Process.SYSTEM_UID,
+                                            userId) == PackageManager.PERMISSION_GRANTED) {
+                                        try {
+                                            revokeRuntimePermission(
+                                                    permissionName,
+                                                    pName,
+                                                    false,
+                                                    userId,
+                                                    callback);
+                                        } catch (IllegalArgumentException e) {
+                                            Slog.e(TAG,
+                                                    "Failed to revoke "
+                                                            + permissionName
+                                                            + " from "
+                                                            + pName,
+                                                    e);
+                                        }
+                                    }
+                                });
+                            }
+                        }
                         flags |= UPDATE_PERMISSIONS_ALL;
                         it.remove();
                     }
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 ff91299..a62bb74 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -773,9 +773,9 @@
 
         // Handle triggering the notification to show/hide when appropriate
         if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
-            runOnBgThread(this::triggerDynamicModeNotification);
+            triggerDynamicModeNotification();
         } else if (!enable) {
-            runOnBgThread(this::hideDynamicModeNotification);
+            hideDynamicModeNotification();
         }
 
         if (DEBUG) {
@@ -787,33 +787,42 @@
 
     @VisibleForTesting
     void triggerDynamicModeNotification() {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
-                R.string.dynamic_mode_notification_channel_name);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                    R.string.dynamic_mode_notification_channel_name);
 
-        manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
-                buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
-                        mContext.getResources().getString(R.string.dynamic_mode_notification_title),
-                        R.string.dynamic_mode_notification_summary,
-                        Intent.ACTION_POWER_USAGE_SUMMARY),
-                UserHandle.ALL);
+            manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
+                    buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                            mContext.getResources().getString(
+                                    R.string.dynamic_mode_notification_title),
+                            R.string.dynamic_mode_notification_summary,
+                            Intent.ACTION_POWER_USAGE_SUMMARY),
+                    UserHandle.ALL);
+        });
     }
 
     @VisibleForTesting
     void triggerStickyDisabledNotification() {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
-                R.string.battery_saver_notification_channel_name);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
+                    R.string.battery_saver_notification_channel_name);
 
-        final String percentage = NumberFormat.getPercentInstance()
-                .format((double) mBatteryLevel / 100.0);
-        manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
-                buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
-                        mContext.getResources().getString(
-                                R.string.battery_saver_charged_notification_title, percentage),
-                        R.string.battery_saver_off_notification_summary,
-                        Settings.ACTION_BATTERY_SAVER_SETTINGS),
-                UserHandle.ALL);
+            final String percentage = NumberFormat.getPercentInstance()
+                    .format((double) mBatteryLevel / 100.0);
+            manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
+                    buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
+                            mContext.getResources().getString(
+                                    R.string.battery_saver_charged_notification_title, percentage),
+                            R.string.battery_saver_off_notification_summary,
+                            Settings.ACTION_BATTERY_SAVER_SETTINGS),
+                    UserHandle.ALL);
+        });
     }
 
     private void ensureNotificationChannelExists(NotificationManager manager,
@@ -854,8 +863,12 @@
     }
 
     private void hideNotification(int notificationId) {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        manager.cancel(notificationId);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            manager.cancelAsUser(TAG, notificationId, UserHandle.ALL);
+        });
     }
 
     private void setStickyActive(boolean active) {
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c76bbb0..d70b628 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -41,6 +41,7 @@
 import android.app.AppOpsManager.HistoricalOpsRequest;
 import android.app.AppOpsManager.HistoricalPackageOps;
 import android.app.AppOpsManager.HistoricalUidOps;
+import android.app.INotificationManager;
 import android.app.ProcessMemoryState;
 import android.app.StatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -140,6 +141,7 @@
 import com.android.server.SystemServiceManager;
 import com.android.server.am.MemoryStatUtil.IonAllocations;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.notification.NotificationManagerService;
 import com.android.server.role.RoleManagerInternal;
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
@@ -1625,14 +1627,7 @@
                 if (statsFiles.size() != 1) {
                     return;
                 }
-                InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
-                        statsFiles.get(0));
-                int[] len = new int[1];
-                byte[] stats = readFully(stream, len);
-                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
-                        wallClockNanos);
-                e.writeStorage(Arrays.copyOf(stats, len[0]));
-                pulledData.add(e);
+                unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles);
                 new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
                         + lastHighWaterMark).delete();
                 new File(
@@ -1648,6 +1643,52 @@
         }
     }
 
+    private INotificationManager mNotificationManager =
+            INotificationManager.Stub.asInterface(
+                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+
+    private void pullNotificationStats(int reportId, int tagId, long elapsedNanos,
+            long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        final long callingToken = Binder.clearCallingIdentity();
+        try {
+            // determine last pull tine. Copy file trick from pullProcessStats?
+            long lastNotificationStatsNs = wallClockNanos -
+                    TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS);
+
+            List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+            long notificationStatsNs = mNotificationManager.pullStats(
+                    lastNotificationStatsNs, reportId, true, statsFiles);
+            if (statsFiles.size() != 1) {
+                return;
+            }
+            unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles);
+        } catch (IOException e) {
+            Log.e(TAG, "Getting notistats failed: ", e);
+
+        } catch (RemoteException e) {
+            Log.e(TAG, "Getting notistats failed: ", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Getting notistats failed: ", e);
+        } finally {
+            Binder.restoreCallingIdentity(callingToken);
+        }
+
+    }
+
+    static void unpackStreamedData(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData, List<ParcelFileDescriptor> statsFiles)
+            throws IOException {
+        InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
+                statsFiles.get(0));
+        int[] len = new int[1];
+        byte[] stats = readFully(stream, len);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                wallClockNanos);
+        e.writeStorage(Arrays.copyOf(stats, len[0]));
+        pulledData.add(e);
+    }
+
     static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
         int pos = 0;
         final int initialAvail = stream.available();
@@ -2477,6 +2518,11 @@
                 pullAppOps(elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.NOTIFICATION_REMOTE_VIEWS: {
+                pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS,
+                        tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 5f00148..89a5305 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -30,6 +30,7 @@
 import android.service.textclassifier.ITextClassifierCallback;
 import android.service.textclassifier.ITextClassifierService;
 import android.service.textclassifier.TextClassifierService;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.textclassifier.ConversationActions;
@@ -54,6 +55,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
+import java.util.Map;
 import java.util.Queue;
 
 /**
@@ -119,6 +121,8 @@
     private final Object mLock;
     @GuardedBy("mLock")
     final SparseArray<UserState> mUserStates = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>();
 
     private TextClassificationManagerService(Context context) {
         mContext = Preconditions.checkNotNull(context);
@@ -127,15 +131,16 @@
 
     @Override
     public void onSuggestSelection(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextSelection.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -150,15 +155,16 @@
 
     @Override
     public void onClassifyText(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextClassification.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -173,15 +179,16 @@
 
     @Override
     public void onGenerateLinks(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextLinks.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -196,12 +203,14 @@
 
     @Override
     public void onSelectionEvent(
-            TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
+            @Nullable TextClassificationSessionId sessionId, SelectionEvent event)
+            throws RemoteException {
         Preconditions.checkNotNull(event);
-        validateInput(mContext, event.getPackageName());
+        final int userId = event.getUserId();
+        validateInput(mContext, event.getPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onSelectionEvent(sessionId, event);
             } else {
@@ -213,16 +222,19 @@
     }
     @Override
     public void onTextClassifierEvent(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextClassifierEvent event) throws RemoteException {
         Preconditions.checkNotNull(event);
         final String packageName = event.getEventContext() == null
                 ? null
                 : event.getEventContext().getPackageName();
-        validateInput(mContext, packageName);
+        final int userId = event.getEventContext() == null
+                ? UserHandle.getCallingUserId()
+                : event.getEventContext().getUserId();
+        validateInput(mContext, packageName, userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onTextClassifierEvent(sessionId, event);
             } else {
@@ -235,15 +247,16 @@
 
     @Override
     public void onDetectLanguage(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextLanguage.Request request,
             ITextClassifierCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -258,15 +271,16 @@
 
     @Override
     public void onSuggestConversationActions(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             ConversationActions.Request request,
             ITextClassifierCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -285,13 +299,15 @@
             throws RemoteException {
         Preconditions.checkNotNull(sessionId);
         Preconditions.checkNotNull(classificationContext);
-        validateInput(mContext, classificationContext.getPackageName());
+        final int userId = classificationContext.getUserId();
+        validateInput(mContext, classificationContext.getPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onCreateTextClassificationSession(
                         classificationContext, sessionId);
+                mSessionUserIds.put(sessionId, userId);
             } else {
                 userState.mPendingRequests.add(new PendingRequest(
                         () -> onCreateTextClassificationSession(classificationContext, sessionId),
@@ -306,9 +322,15 @@
         Preconditions.checkNotNull(sessionId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            final int userId = mSessionUserIds.containsKey(sessionId)
+                    ? mSessionUserIds.get(sessionId)
+                    : UserHandle.getCallingUserId();
+            validateInput(mContext, null /* packageName */, userId);
+
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onDestroyTextClassificationSession(sessionId);
+                mSessionUserIds.remove(sessionId);
             } else {
                 userState.mPendingRequests.add(new PendingRequest(
                         () -> onDestroyTextClassificationSession(sessionId),
@@ -318,11 +340,6 @@
     }
 
     @GuardedBy("mLock")
-    private UserState getCallingUserStateLocked() {
-        return getUserStateLocked(UserHandle.getCallingUserId());
-    }
-
-    @GuardedBy("mLock")
     private UserState getUserStateLocked(int userId) {
         UserState result = mUserStates.get(userId);
         if (result == null) {
@@ -356,6 +373,7 @@
                     pw.decreaseIndent();
                 }
             }
+            pw.println("Number of active sessions: " + mSessionUserIds.size());
         }
     }
 
@@ -420,20 +438,32 @@
                 e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
     }
 
-    private static void validateInput(Context context, @Nullable String packageName)
+    private static void validateInput(
+            Context context, @Nullable String packageName, @UserIdInt int userId)
             throws RemoteException {
-        if (packageName == null) return;
 
         try {
-            final int packageUid = context.getPackageManager()
-                    .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
-            final int callingUid = Binder.getCallingUid();
-            Preconditions.checkArgument(callingUid == packageUid
-                    // Trust the system process:
-                    || callingUid == android.os.Process.SYSTEM_UID);
+            if (packageName != null) {
+                final int packageUid = context.getPackageManager()
+                        .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
+                final int callingUid = Binder.getCallingUid();
+                Preconditions.checkArgument(callingUid == packageUid
+                        // Trust the system process:
+                        || callingUid == android.os.Process.SYSTEM_UID,
+                        "Invalid package name. Package=" + packageName
+                                + ", CallingUid=" + callingUid);
+            }
+
+            Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
+            final int callingUserId = UserHandle.getCallingUserId();
+            if (callingUserId != userId) {
+                context.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                        "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
+            }
         } catch (Exception e) {
-            throw new RemoteException(
-                    String.format("Invalid package: name=%s, error=%s", packageName, e));
+            throw new RemoteException("Invalid request: " + e.getMessage(), e,
+                    /* enableSuppression */ true, /* writableStackTrace */ true);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b37c779..2d835d3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -865,6 +865,8 @@
                 if (canToastShowWhenLocked(callingPid)) {
                     attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
                 }
+                // Toasts can't be clickable
+                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                 break;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index f336497..ab23b29 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 
+import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
@@ -67,6 +68,7 @@
     @Mock AccessibilityServiceInfo mMockServiceInfo;
     @Mock ResolveInfo mMockResolveInfo;
     @Mock AccessibilityManagerService.SecurityPolicy mMockSecurityPolicy;
+    @Mock ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
     @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
     @Mock GlobalActionPerformer mMockGlobalActionPerformer;
@@ -89,7 +91,8 @@
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
                 mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
-                mMockGlobalActionPerformer);
+                mMockGlobalActionPerformer, mMockActivityTaskManagerInternal);
+        when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
     }
 
     @After
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
new file mode 100644
index 0000000..f6c4d3a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AutomaticBrightnessControllerTest {
+
+    private static final int BRIGHTNESS_MIN = 1;
+    private static final int BRIGHTNESS_MAX = 255;
+    private static final int LIGHT_SENSOR_RATE = 20;
+    private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
+    private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int SHORT_TERM_MODEL_TIMEOUT = 0;
+    private static final float DOZE_SCALE_FACTOR = 0.0f;
+    private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
+
+    private Context mContext;
+    @Mock SensorManager mSensorManager;
+    @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
+    @Mock HysteresisLevels mAmbientBrightnessThresholds;
+    @Mock HysteresisLevels mScreenBrightnessThresholds;
+    @Mock PackageManager mPackageManager;
+    @Mock Handler mNoopHandler;
+
+    private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    private AutomaticBrightnessController setupController(Sensor lightSensor) {
+        AutomaticBrightnessController controller = new AutomaticBrightnessController(
+                new AutomaticBrightnessController.Injector() {
+                    @Override
+                    public Handler getBackgroundThreadHandler() {
+                        return mNoopHandler;
+                    }
+                },
+                () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
+                mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN,
+                BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE,
+                BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG,
+                RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds,
+                mScreenBrightnessThresholds, SHORT_TERM_MODEL_TIMEOUT, mPackageManager);
+        controller.setLoggingEnabled(true);
+
+        // Configure the brightness controller and grab an instance of the sensor listener,
+        // through which we can deliver fake (for test) sensor values.
+        controller.configure(true /* enable */, null /* configuration */,
+                0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */,
+                false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+        return controller;
+    }
+
+    @Test
+    public void testNoHysteresisAtMinBrightness() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        AutomaticBrightnessController controller = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 5 as a brightness value
+        float lux1 = 100.0f;
+        float normalizedBrightness1 = 0.02f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness1);
+
+        // This is the important bit: When the new brightness is set, make sure the new
+        // brightening threshold is beyond the maximum brightness value...so that we can test that
+        // our threshold clamping works.
+        when(mScreenBrightnessThresholds.getBrighteningThreshold(5)).thenReturn(1.0f);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
+        assertEquals(5, controller.getAutomaticScreenBrightness());
+
+
+        // Set up system to return 255 as a brightness value
+        float lux2 = 10.0f;
+        float normalizedBrightness2 = 0.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness2);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
+        assertEquals(1, controller.getAutomaticScreenBrightness());
+    }
+
+    @Test
+    public void testNoHysteresisAtMaxBrightness() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        AutomaticBrightnessController controller = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 250 as a brightness value
+        float lux1 = 100.0f;
+        float normalizedBrightness1 = 0.98f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness1);
+
+        // This is the important bit: When the new brightness is set, make sure the new
+        // brightening threshold is beyond the maximum brightness value...so that we can test that
+        // our threshold clamping works.
+        when(mScreenBrightnessThresholds.getBrighteningThreshold(250)).thenReturn(260.0f);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
+        assertEquals(250, controller.getAutomaticScreenBrightness());
+
+
+        // Set up system to return 255 as a brightness value
+        float lux2 = 110.0f;
+        float normalizedBrightness2 = 1.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness2);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
+        assertEquals(255, controller.getAutomaticScreenBrightness());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 4742a73..8d5939a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -11,7 +11,7 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
 package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
new file mode 100644
index 0000000..859dfe3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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.display;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.os.SystemClock;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class TestUtils {
+
+    public static SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
+        final Constructor<SensorEvent> constructor =
+                SensorEvent.class.getDeclaredConstructor(int.class);
+        constructor.setAccessible(true);
+        final SensorEvent event = constructor.newInstance(1);
+        event.sensor = sensor;
+        event.values[0] = lux;
+        event.timestamp = SystemClock.elapsedRealtimeNanos();
+        return event;
+    }
+
+
+    public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+        setter.setAccessible(true);
+        setter.invoke(sensor, type);
+        if (strType != null) {
+            Field f = sensor.getClass().getDeclaredField("mStringType");
+            f.setAccessible(true);
+            f.set(sensor, strType);
+        }
+    }
+
+    public static Sensor createSensor(int type, String strType) throws Exception {
+        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+        constr.setAccessible(true);
+        Sensor sensor = constr.newInstance();
+        setSensorType(sensor, type, strType);
+        return sensor;
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 6b0798b..e25c1c6 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -16,29 +16,19 @@
 
 package com.android.server.display.whitebalance;
 
-import com.android.internal.R;
-import com.google.common.collect.ImmutableList;
-
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.eq;
-import org.mockito.stubbing.Answer;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -46,15 +36,21 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.R;
+import com.android.server.display.TestUtils;
+import com.android.server.display.whitebalance.AmbientFilter;
+
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
-import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.List;
 
 @RunWith(JUnit4.class)
@@ -80,8 +76,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mLightSensor = createSensor(Sensor.TYPE_LIGHT, null);
-        mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
+        mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
@@ -460,25 +456,6 @@
         }
     }
 
-    private void setSensorType(Sensor sensor, int type, String strType) throws Exception {
-        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
-        setter.setAccessible(true);
-        setter.invoke(sensor, type);
-        if (strType != null) {
-            Field f = sensor.getClass().getDeclaredField("mStringType");
-            f.setAccessible(true);
-            f.set(sensor, strType);
-        }
-    }
-
-    private Sensor createSensor(int type, String strType) throws Exception {
-        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
-        constr.setAccessible(true);
-        Sensor sensor = constr.newInstance();
-        setSensorType(sensor, type, strType);
-        return sensor;
-    }
-
     private TypedArray createTypedArray() throws Exception {
         TypedArray mockArray = mock(TypedArray.class);
         return mockArray;
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
index 6ff4f3b..3e3e535 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
@@ -28,15 +28,14 @@
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.SystemClock;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.server.display.TestUtils;
 import com.android.server.display.whitebalance.AmbientSensor.AmbientBrightnessSensor;
 import com.android.server.display.whitebalance.AmbientSensor.AmbientColorTemperatureSensor;
 
@@ -50,9 +49,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -73,8 +69,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mLightSensor = createSensor(Sensor.TYPE_LIGHT, null);
-        mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
+        mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
@@ -96,7 +92,7 @@
         // There should be no issues when we callback the listener, even if there is no callback
         // set.
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mLightSensor, 100));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 100));
     }
 
     @Test
@@ -122,7 +118,7 @@
         verify(mSensorManagerMock).registerListener(captor.capture(), eq(mLightSensor),
                 anyInt(), eq(mHandler));
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mLightSensor, luxValue));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, luxValue));
         assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
         assertEquals(luxValue, luxReturned[0]);
     }
@@ -155,39 +151,8 @@
         verify(mSensorManagerMock).registerListener(captor.capture(), eq(mAmbientColorSensor),
                 anyInt(), eq(mHandler));
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mAmbientColorSensor, colorTempValue));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mAmbientColorSensor, colorTempValue));
         assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
         assertEquals(colorTempValue, colorTempReturned[0]);
     }
-
-    private SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
-        final Constructor<SensorEvent> constructor =
-                SensorEvent.class.getDeclaredConstructor(int.class);
-        constructor.setAccessible(true);
-        final SensorEvent event = constructor.newInstance(1);
-        event.sensor = sensor;
-        event.values[0] = lux;
-        event.timestamp = SystemClock.elapsedRealtimeNanos();
-        return event;
-    }
-
-
-    private void setSensorType(Sensor sensor, int type, String strType) throws Exception {
-        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
-        setter.setAccessible(true);
-        setter.invoke(sensor, type);
-        if (strType != null) {
-            Field f = sensor.getClass().getDeclaredField("mStringType");
-            f.setAccessible(true);
-            f.set(sensor, strType);
-        }
-    }
-
-    private Sensor createSensor(int type, String strType) throws Exception {
-        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
-        constr.setAccessible(true);
-        Sensor sensor = constr.newInstance();
-        setSensorType(sensor, type, strType);
-        return sensor;
-    }
 }
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 4a9cef1..367962b 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -20,6 +20,7 @@
         "androidx.test.rules", "hamcrest-library",
         "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
+        "platformprotosnano",
         "hamcrest-library",
         "testables",
         "truth-prebuilt",
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 dc5edcd..6c91e2f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1017,6 +1017,20 @@
     }
 
     @Test
+    public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
+            throws Exception {
+        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        sbn.getNotification().flags =
+                Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
+        waitForIdle();
+        verify(mListeners, times(1)).notifyPostedLocked(any(), any());
+        verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), any());
+    }
+
+    @Test
     public void testUserInitiatedClearAll_noLeak() throws Exception {
         final NotificationRecord n = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 7c22350..fab6b7f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -52,11 +52,13 @@
 import android.media.AudioAttributes;
 import android.metrics.LogMaker;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
+import android.widget.RemoteViews;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -114,7 +116,9 @@
 
         when(mMockContext.getResources()).thenReturn(getContext().getResources());
         when(mMockContext.getPackageManager()).thenReturn(mPm);
-        when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.targetSdkVersion = Build.VERSION_CODES.O;
+        when(mMockContext.getApplicationInfo()).thenReturn(appInfo);
     }
 
     private StatusBarNotification getNotification(String pkg, boolean noisy, boolean defaultSound,
@@ -168,6 +172,28 @@
         return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
+    private StatusBarNotification getStyledNotification(boolean customContent, boolean customBig,
+            boolean customHeadsUp, Notification.Style style) {
+        final Builder builder = new Builder(mMockContext)
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        if (style != null) {
+            builder.setStyle(style);
+        }
+        if (customContent) {
+            builder.setCustomContentView(mock(RemoteViews.class));
+        }
+        if (customBig) {
+            builder.setCustomBigContentView(mock(RemoteViews.class));
+        }
+        if (customHeadsUp) {
+            builder.setCustomHeadsUpContentView(mock(RemoteViews.class));
+        }
+
+        Notification n = builder.build();
+        return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
+    }
+
     //
     // Tests
     //
@@ -999,4 +1025,74 @@
 
         assertEquals(IMPORTANCE_LOW, record.getImportance());
     }
+
+    @Test
+    public void testHasUndecoratedRemoteViews_NoRemoteViews() {
+        StatusBarNotification sbn = getStyledNotification(false, false, false, null);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse("false positive detection", record.hasUndecoratedRemoteView());
+    }
+
+    @Test
+    public void testHasUndecoratedRemoteViews_NoRemoteViewsWithStyle() {
+        StatusBarNotification sbn = getStyledNotification(false, false, false,
+                new Notification.BigPictureStyle());
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse("false positive detection", record.hasUndecoratedRemoteView());
+    }
+
+    @Test
+    public void testHasUndecoratedRemoteViews_UndecoratedContent() {
+        StatusBarNotification sbn = getStyledNotification(true, false, false, null);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue("false negative detection", record.hasUndecoratedRemoteView());
+    }
+
+
+    @Test
+    public void testHasUndecoratedRemoteViews_UndecoratedBig() {
+        StatusBarNotification sbn = getStyledNotification(false, true, false, null);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue("false negative detection", record.hasUndecoratedRemoteView());
+    }
+
+
+    @Test
+    public void testHasUndecoratedRemoteViews_UndecoratedHeadsup() {
+        StatusBarNotification sbn = getStyledNotification(false, false, true, null);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue("false negative detection", record.hasUndecoratedRemoteView());
+    }
+
+    @Test
+    public void testHasUndecoratedRemoteViews_DecoratedRemoteViews() {
+        StatusBarNotification sbn = getStyledNotification(true, true, true,
+                new Notification.DecoratedCustomViewStyle());
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse("false positive detection", record.hasUndecoratedRemoteView());
+    }
+
+    @Test
+    public void testHasUndecoratedRemoteViews_DecoratedMediaRemoteViews() {
+        StatusBarNotification sbn = getStyledNotification(true, true, true,
+                new Notification.DecoratedMediaCustomViewStyle());
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertFalse("false positive detection", record.hasUndecoratedRemoteView());
+    }
+
+    @Test
+    public void testHasUndecoratedRemoteViews_UndecoratedWrongStyle() {
+        StatusBarNotification sbn = getStyledNotification(true, true, true,
+                new Notification.BigPictureStyle());
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertTrue("false negative detection", record.hasUndecoratedRemoteView());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java
new file mode 100644
index 0000000..f685c68
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.notification;
+
+import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.service.notification.nano.NotificationRemoteViewsProto;
+import android.test.MoreAsserts;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+public class PulledStatsTest extends UiServiceTestCase {
+
+    @Test
+    public void testPulledStats_Empty() {
+        PulledStats stats = new PulledStats(0L);
+        assertEquals(0L, stats.endTimeMs());
+    }
+
+    @Test
+    public void testPulledStats_UnknownReport() {
+        PulledStats stats = new PulledStats(0L);
+        stats.addUndecoratedPackage("foo", 456);
+        stats.addUndecoratedPackage("bar", 123);
+
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        final ProtoOutputStream proto = new ProtoOutputStream(bytes);
+        stats.writeToProto(1023123, proto); // a very large number
+        proto.flush();
+
+        // expect empty output in response to an unrecognized request
+        assertEquals(0L, bytes.size());
+    }
+
+    @Test
+    public void testPulledStats_RemoteViewReportPackages() {
+        List<String> expectedPkgs = new ArrayList<>(2);
+        expectedPkgs.add("foo");
+        expectedPkgs.add("bar");
+
+        PulledStats stats = new PulledStats(0L);
+        for(String pkg: expectedPkgs) {
+            stats.addUndecoratedPackage(pkg, 111);
+        }
+
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        final ProtoOutputStream protoStream = new ProtoOutputStream(bytes);
+        stats.writeToProto(REPORT_REMOTE_VIEWS, protoStream);
+        protoStream.flush();
+
+        try {
+            NotificationRemoteViewsProto proto =
+                    NotificationRemoteViewsProto.parseFrom(bytes.toByteArray());
+            List<String> actualPkgs = new ArrayList<>(2);
+            for(int i = 0 ; i < proto.packageRemoteViewInfo.length; i++) {
+                actualPkgs.add(proto.packageRemoteViewInfo[i].packageName);
+            }
+            assertEquals(2, actualPkgs.size());
+            assertTrue("missing packages", actualPkgs.containsAll(expectedPkgs));
+            assertTrue("unexpected packages", expectedPkgs.containsAll(actualPkgs));
+        } catch (InvalidProtocolBufferNanoException e) {
+            e.printStackTrace();
+            fail("writeToProto generated unparsable output");
+        }
+
+    }
+    @Test
+    public void testPulledStats_RemoteViewReportEndTime() {
+        List<String> expectedPkgs = new ArrayList<>(2);
+        expectedPkgs.add("foo");
+        expectedPkgs.add("bar");
+
+        PulledStats stats = new PulledStats(0L);
+        long t = 111;
+        for(String pkg: expectedPkgs) {
+            t += 1000;
+            stats.addUndecoratedPackage(pkg, t);
+        }
+        assertEquals(t, stats.endTimeMs());
+    }
+
+}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 11c0e4a..9fc1949 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -136,17 +136,16 @@
         }
 
         // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
-        // is last time UsageStatsDatabase is persisted to disk.
+        // is last time UsageStatsDatabase is persisted to disk or the last event's time whichever
+        // is higher (because the file system timestamp is round down to integral seconds).
         // Also add a DEVICE_STARTUP event with current system timestamp.
         final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
         if (currentDailyStats != null) {
-            // File system timestamp only has precision of 1 second, add 1000ms to make up
-            // for the loss of round up.
-            final Event shutdownEvent =
-                    new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved + 1000);
+            final Event shutdownEvent = new Event(DEVICE_SHUTDOWN,
+                    Math.max(currentDailyStats.lastTimeSaved, currentDailyStats.endTime));
             shutdownEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
             currentDailyStats.addEvent(shutdownEvent);
-            final Event startupEvent = new Event(DEVICE_STARTUP, currentTimeMillis);
+            final Event startupEvent = new Event(DEVICE_STARTUP, System.currentTimeMillis());
             startupEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
             currentDailyStats.addEvent(startupEvent);
         }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 00c7548..8122374 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -486,7 +486,7 @@
 
     /* Opens the specified USB device */
     public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings,
-            String packageName, int uid) {
+            String packageName, int pid, int uid) {
         synchronized (mLock) {
             if (isBlackListed(deviceAddress)) {
                 throw new SecurityException("USB device is on a restricted bus");
@@ -498,7 +498,7 @@
                         "device " + deviceAddress + " does not exist or is restricted");
             }
 
-            settings.checkPermission(device, packageName, uid);
+            settings.checkPermission(device, packageName, pid, uid);
             return nativeOpenDevice(deviceAddress);
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 8ca77f0..077d6b9 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -93,7 +93,7 @@
                                 UserHandle.getUserId(uid));
 
                         if (mDevice instanceof UsbDevice) {
-                            settings.checkPermission((UsbDevice) mDevice, packageName, uid);
+                            settings.checkPermission((UsbDevice) mDevice, packageName, pid, uid);
                         } else {
                             settings.checkPermission((UsbAccessory) mDevice, uid);
                         }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 4be68b8..13275f3 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -249,6 +249,7 @@
         if (mHostManager != null) {
             if (deviceName != null) {
                 int uid = Binder.getCallingUid();
+                int pid = Binder.getCallingPid();
                 int user = UserHandle.getUserId(uid);
 
                 long ident = clearCallingIdentity();
@@ -256,7 +257,7 @@
                     synchronized (mLock) {
                         if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
                             fd = mHostManager.openDevice(deviceName, getSettingsForUser(user),
-                                    packageName, uid);
+                                    packageName, pid, uid);
                         } else {
                             Slog.w(TAG, "Cannot open " + deviceName + " for user " + user
                                     + " as user is not active.");
@@ -350,11 +351,12 @@
     @Override
     public boolean hasDevicePermission(UsbDevice device, String packageName) {
         final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final int userId = UserHandle.getUserId(uid);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            return getSettingsForUser(userId).hasPermission(device, packageName, uid);
+            return getSettingsForUser(userId).hasPermission(device, packageName, pid, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -376,11 +378,12 @@
     @Override
     public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
         final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final int userId = UserHandle.getUserId(uid);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            getSettingsForUser(userId).requestPermission(device, packageName, pi, uid);
+            getSettingsForUser(userId).requestPermission(device, packageName, pi, pid, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 84add88..e1bfb8a 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -127,11 +127,12 @@
      * Check for camera permission of the calling process.
      *
      * @param packageName Package name of the caller.
+     * @param pid Linux pid of the calling process.
      * @param uid Linux uid of the calling process.
      *
      * @return True in case camera permission is available, False otherwise.
      */
-    private boolean isCameraPermissionGranted(String packageName, int uid) {
+    private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
         int targetSdkVersion = android.os.Build.VERSION_CODES.P;
         try {
             ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
@@ -147,7 +148,8 @@
         }
 
         if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
-            int allowed = mUserContext.checkCallingPermission(android.Manifest.permission.CAMERA);
+            int allowed = mUserContext.checkPermission(android.Manifest.permission.CAMERA, pid,
+                    uid);
             if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
                 Slog.i(TAG, "Camera permission required for USB video class devices");
                 return false;
@@ -157,9 +159,9 @@
         return true;
     }
 
-    public boolean hasPermission(UsbDevice device, String packageName, int uid) {
+    public boolean hasPermission(UsbDevice device, String packageName, int pid, int uid) {
         if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, uid)) {
+            if (!isCameraPermissionGranted(packageName, pid, uid)) {
                 return false;
             }
         }
@@ -171,8 +173,8 @@
         return mUsbPermissionManager.hasPermission(accessory, uid);
     }
 
-    public void checkPermission(UsbDevice device, String packageName, int uid) {
-        if (!hasPermission(device, packageName, uid)) {
+    public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
+        if (!hasPermission(device, packageName, pid, uid)) {
             throw new SecurityException("User has not given " + uid + "/" + packageName
                     + " permission to access device " + device.getDeviceName());
         }
@@ -206,11 +208,12 @@
                 accessory, canBeDefault, packageName, uid, mUserContext, pi);
     }
 
-    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
+    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
+            int uid) {
         Intent intent = new Intent();
 
         // respond immediately if permission has already been granted
-        if (hasPermission(device, packageName, uid)) {
+        if (hasPermission(device, packageName, pid, uid)) {
             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
             try {
@@ -221,7 +224,7 @@
             return;
         }
         if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, uid)) {
+            if (!isCameraPermissionGranted(packageName, pid, uid)) {
                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
                 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
                 try {
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 3d28cf6..7f62c5f 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -1262,8 +1262,7 @@
              * Broadcast action: When SMS-MMS db is being created. If file-based encryption is
              * supported, this broadcast indicates creation of the db in credential-encrypted
              * storage. A boolean is specified in {@link #EXTRA_IS_INITIAL_CREATE} to indicate if
-             * this is the initial create of the db. Requires
-             * {@link android.Manifest.permission#READ_SMS} to receive.
+             * this is the initial create of the db.
              *
              * @see #EXTRA_IS_INITIAL_CREATE
              *
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 907808a..b646a93 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2866,7 +2866,6 @@
         /**
          * Location information during (and after) an emergency call is only provided over control
          * plane signaling from the network.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
 
@@ -2874,7 +2873,6 @@
          * Location information during (and after) an emergency call is provided over the data
          * plane and serviced by the framework GNSS service, but if it fails, the carrier also
          * supports control plane backup signaling.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
 
@@ -2882,7 +2880,6 @@
          * Location information during (and after) an emergency call is provided over the data plane
          * and serviced by the framework GNSS service only. There is no backup signalling over the
          * control plane if it fails.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
 
@@ -2990,11 +2987,21 @@
          * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
          * <p>
          * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
-         * @hide
          */
         public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
                 + "es_supl_control_plane_support_int";
 
+        /**
+         * A list of roaming PLMNs where SUPL ES mode does not support a control-plane mechanism to
+         * get a user's location in the event that data plane SUPL fails or is otherwise
+         * unavailable.
+         * <p>
+         * A string array of PLMNs that do not support a control-plane mechanism for getting a
+         * user's location for SUPL ES.
+         */
+        public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
+                KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -3011,6 +3018,7 @@
             defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
             defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                     SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
+            defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
             return defaults;
         }
     }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index c3cfb02..80776fe 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -28,6 +28,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
 import java.util.regex.PatternSyntaxException;
 
 /**
@@ -228,6 +229,10 @@
 
         private static final MacAddress MAC_ANY_ADDRESS =
                 MacAddress.fromString("02:00:00:00:00:00");
+        /**
+         * Maximum number of bytes allowed for a SSID.
+         */
+        private static final int MAX_SSID_BYTES = 32;
 
         private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
         private String mNetworkName = "";
@@ -279,6 +284,10 @@
                 throw new IllegalArgumentException(
                         "network name must be non-empty.");
             }
+            if (networkName.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) {
+                throw new IllegalArgumentException(
+                        "network name exceeds " + MAX_SSID_BYTES + " bytes.");
+            }
             try {
                 if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
                     throw new IllegalArgumentException(
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
index 41f109a..6199325 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
@@ -53,6 +53,13 @@
             fail("Unexpected IllegalArgumentException");
         }
 
+        // sunny case with maximum bytes for the network name
+        try {
+            b.setNetworkName("DIRECT-abcdefghijklmnopqrstuvwxy");
+        } catch (IllegalArgumentException e) {
+            fail("Unexpected IllegalArgumentException");
+        }
+
         // less than 9 characters.
         try {
             b.setNetworkName("DIRECT-z");
@@ -77,6 +84,12 @@
             b.setNetworkName("direct-a?");
             fail("expected IllegalArgumentException");
         } catch (IllegalArgumentException e) { }
+
+        // over maximum bytes
+        try {
+            b.setNetworkName("DIRECT-abcdefghijklmnopqrstuvwxyz");
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) { }
     }
 
     /**