Merge "Internal version of isChangeEnabled." into rvc-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 1142fb6..795d873 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3323,6 +3323,7 @@
     field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
     field public static final String NOTIFICATION_BADGING = "notification_badging";
     field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+    field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
     field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
     field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
     field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2bbce11..e998711 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -470,6 +470,14 @@
         NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"];
         NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"];
         NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"];
+        MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported =
+            294;
+        MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported =
+            295;
+        MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported =
+            296;
+        MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported =
+            297;
 
         // StatsdStats tracks platform atoms with ids upto 500.
         // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -10549,3 +10557,237 @@
     // Whether the Assistant handles were showing at the time of invocation.
     optional bool assistant_handles_showing = 6;
 }
+
+/**
+ * Logs when an AudioRecord finishes running on an audio device
+ *
+ * Logged from:
+ *   frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioRecordDeviceUsageReported {
+    // The devices connected to this AudioRecord.
+    // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+    // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    // See audio_device_t in system/media/audio/include/system/audio-base.h
+    optional string devices = 1;
+
+    // The name of the remote device attached to the device, typically available for USB or BT.
+    // This may be empty for a fixed device, or separated by "|" if more than one.
+    optional string device_names = 2;
+
+    // The amount of time spent in the device as measured by the active track in AudioFlinger.
+    optional int64 device_time_nanos = 3;
+
+    // The audio data format used for encoding.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+    optional string encoding = 4;
+
+    // The client-server buffer framecount.
+    // The framecount is generally between 960 - 48000 for PCM encoding.
+    // The framecount represents raw buffer size in bytes for non-PCM encoding.
+    optional int32 frame_count = 5;
+
+    // The number of audio intervals (contiguous, continuous playbacks).
+    optional int32 interval_count = 6;
+
+    // The sample rate of the AudioRecord.
+    // A number generally between 8000-96000 (frames per second).
+    optional int32 sample_rate = 7;
+
+    // The audio input flags used to construct the AudioRecord.
+    // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+    optional string flags = 8;
+
+    // The santized package name of the audio client associated with the AudioRecord.
+    // See getSanitizedPackageNameAndVersionCode() in
+    // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+    optional string package_name = 9;
+
+    // The selected device id (nonzero if a non-default device is selected)
+    optional int32 selected_device_id = 10;
+
+    // The caller of the AudioRecord.
+    // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    optional string caller = 11;
+
+    // The audio source for AudioRecord.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t
+    optional string source = 12;
+}
+
+/**
+ * Logs when an AudioThread finishes running on an audio device
+ *
+ * Logged from:
+ *   frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioThreadDeviceUsageReported {
+    // The devices connected to this audio thread.
+    // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+    // (for record threads):
+    // See lookup<INPUT_DEVICE> in frameworks/av/services/mediametrics/AudioTypes.cpp
+    // (for playback threads):
+    // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    // See audio_device_t in system/media/audio/include/system/audio-base.h
+    optional string devices = 1;
+
+    // The name of the remote device attached to the device, typically available for USB or BT.
+    // This may be empty for a fixed device, or separated by "|" if more than one.
+    optional string device_names = 2;
+
+    // The amount of time spent in the device as measured by the active track in AudioFlinger.
+    optional int64 device_time_nanos = 3;
+
+    // The audio data format used for encoding.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+    optional string encoding = 4;
+
+    // The framecount of the buffer delivered to (or from) the HAL.
+    // The framecount is generally ~960 for PCM encoding.
+    // The framecount represents raw buffer size in bytes for non-PCM encoding.
+    optional int32 frame_count = 5;
+
+    // The number of audio intervals (contiguous, continuous playbacks).
+    optional int32 interval_count = 6;
+
+    // The sample rate of the audio thread.
+    // A number generally between 8000-96000 (frames per second).
+    optional int32 sample_rate = 7;
+
+    // The audio flags used to construct the thread
+    // (for record threads):
+    // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+    // (for playback threads):
+    // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+    optional string flags = 8;
+
+    // The number of underruns encountered for a playback thread or the
+    // number of overruns encountered for a capture thread.
+    optional int32 xruns = 9;
+
+    // The type of thread
+    // A thread type enumeration from
+    // frameworks/av/mediametrics/services/Translate.h
+    optional string type = 10;
+}
+
+/**
+ * Logs when an AudioTrack finishes running on an audio device
+ *
+ * Logged from:
+ *   frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioTrackDeviceUsageReported {
+    // The output devices connected to this AudioTrack.
+    // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2".
+    // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    // See audio_device_t in system/media/audio/include/system/audio-base.h
+    optional string devices = 1;
+
+    // The name of the remote device attached to the device, typically available for USB or BT.
+    // This may be empty for a fixed device, or separated by "|" if more than one.
+    optional string device_names = 2;
+
+    // The amount of time spent in the device as measured by the active track in AudioFlinger.
+    optional int64 device_time_nanos = 3;
+
+    // The audio data format used for encoding.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+    optional string encoding = 4;
+
+    // The client-server buffer framecount.
+    // The framecount is generally between 960 - 48000 for PCM encoding.
+    // The framecount represents raw buffer size in bytes for non-PCM encoding.
+    // A static track (see traits) may have a very large framecount.
+    optional int32 frame_count = 5;
+
+    // The number of audio intervals (contiguous, continuous playbacks).
+    optional int32 interval_count = 6;
+
+    // The sample rate of the AudioTrack.
+    // A number generally between 8000-96000 (frames per second).
+    optional int32 sample_rate = 7;
+
+    // The audio flags used to construct the AudioTrack.
+    // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+    optional string flags = 8;
+
+    // The number of underruns encountered.
+    optional int32 xruns = 9;
+
+    // The santized package name of the audio client associated with the AudioTrack.
+    // See getSanitizedPackageNameAndVersionCode() in
+    // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+    optional string package_name = 10;
+
+    // The latency of the last sample in the buffer in milliseconds.
+    optional float device_latency_millis = 11;
+
+    // The startup time in milliseconds from start() to sample played.
+    optional float device_startup_millis = 12;
+
+    // The average volume of the track on the device [ 0.f - 1.f ]
+    optional float device_volume = 13;
+
+    // The selected device id (nonzero if a non-default device is selected)
+    optional int32 selected_device_id = 14;
+
+    // The stream_type category for the AudioTrack.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t
+    optional string stream_type = 15;
+
+    // The usage for the AudioTrack.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t
+    optional string usage = 16;
+
+    // The content type of the AudioTrack.
+    // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t
+    optional string content_type = 17;
+
+    // The caller of the AudioTrack.
+    // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    optional string caller = 18;
+
+    // The traits of the AudioTrack.
+    // A string OR of different traits, may be empty string.
+    // Only "static" is supported for R.
+    // See lookup<TRACK_TRAITS>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    optional string traits = 19;
+}
+
+/**
+ * Logs the status of an audio device connection attempt.
+ *
+ * Logged from:
+ *   frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioDeviceConnectionReported {
+    // The input devices represented by this report.
+    // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+    // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    // See audio_device_t in system/media/audio/include/system/audio-base.h
+    optional string input_devices = 1;
+
+    // The output devices represented by this report.
+    // A string OR of various output device categories.
+    // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+    // See audio_device_t in system/media/audio/include/system/audio-base.h
+    optional string output_devices = 2;
+
+    // The name of the remote device attached to the device, typically available for USB or BT.
+    // This may be empty for a fixed device, or separated by "|" if more than one.
+    optional string device_names = 3;
+
+    // The result of the audio device connection.
+    // 0 indicates success: connection verified.
+    // 1 indicates unknown: connection not verified or not known if diverted properly.
+    // Other values indicate specific status.
+    // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h
+    optional int32 result = 4;
+
+    // Average milliseconds of time to connect
+    optional float time_to_connect_millis = 5;
+
+    // Number of connections if aggregated statistics, otherwise 1.
+    optional int32 connection_count = 6;
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1f90e40..322cac8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4247,6 +4247,12 @@
      * device. After this method is called, the device must be unlocked using strong authentication
      * (PIN, pattern, or password). This API is intended for use only by device admins.
      * <p>
+     * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
+     * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
+     * true, then the method will return without completing any action. Before version
+     * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
+     * regardless of the caller's permissions.
+     * <p>
      * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
      * to be able to call this method; if it has not, a security exception will be thrown.
      * <p>
@@ -4274,6 +4280,12 @@
      * device. After this method is called, the device must be unlocked using strong authentication
      * (PIN, pattern, or password). This API is intended for use only by device admins.
      * <p>
+     * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
+     * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
+     * true, then the method will return without completing any action. Before version
+     * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
+     * regardless of the caller's permissions.
+     * <p>
      * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
      * to be able to call this method; if it has not, a security exception will be thrown.
      * <p>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e10fcea..4f0a972 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -24,6 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
@@ -6238,6 +6239,8 @@
          * determines if the IME should be shown when a hard keyboard is attached.
          * @hide
          */
+        @TestApi
+        @SuppressLint("NoSettingsProvider")
         public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
 
         /**
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index 1e16273..e592fad 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -214,10 +214,13 @@
     }
 
     /**
-     * Get a singleton {@link ControlTemplate} that has no features.
+     * Get a singleton {@link ControlTemplate}, which supports no direct user input.
      *
-     * This template has no distinctive field, not even an identifier. Used for a {@link Control}
-     * that accepts no type of input, or when there is no known state.
+     * Used by {@link Control.StatelessBuilder} when there is no known state. Can also be used
+     * in {@link Control.StatefulBuilder} for conveying information to a user about the
+     * {@link Control} but direct user interaction is not desired. Since this template has no
+     * corresponding {@link ControlAction}, any user interaction will launch the
+     * {@link Control#getAppIntent()}.
      *
      * @return a singleton {@link ControlTemplate} to indicate no specific template is used by
      *         this {@link Control}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 775ef81..8aa7b63 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -23,9 +23,12 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.Log;
+import android.view.IWindowManager;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -44,13 +47,14 @@
  */
 @Singleton
 public class UserSwitchTransitionViewController extends OverlayViewController {
-    private static final String TAG = "UserSwitchTransitionViewController";
+    private static final String TAG = "UserSwitchTransition";
     private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true";
 
     private final Context mContext;
     private final Handler mHandler;
     private final Resources mResources;
     private final UserManager mUserManager;
+    private final IWindowManager mWindowManagerService;
 
     @GuardedBy("this")
     private boolean mShowing;
@@ -62,6 +66,7 @@
             @Main Handler handler,
             @Main Resources resources,
             UserManager userManager,
+            IWindowManager windowManagerService,
             OverlayViewGlobalStateController overlayViewGlobalStateController) {
 
         super(R.id.user_switching_dialog_stub, overlayViewGlobalStateController);
@@ -70,6 +75,7 @@
         mHandler = handler;
         mResources = resources;
         mUserManager = userManager;
+        mWindowManagerService = windowManagerService;
     }
 
     /**
@@ -81,6 +87,13 @@
         if (mPreviousUserId == newUserId || mShowing) return;
         mShowing = true;
         mHandler.post(() -> {
+            try {
+                mWindowManagerService.setSwitchingUser(true);
+                mWindowManagerService.lockNow(null);
+            } catch (RemoteException e) {
+                Log.e(TAG, "unable to notify window manager service regarding user switch");
+            }
+
             start();
             populateDialog(mPreviousUserId, newUserId);
             // next time a new user is selected, this current new user will be the previous user.
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
index eab381c..65c5562 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
@@ -28,6 +28,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
+import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
@@ -52,6 +53,8 @@
     private TestableResources mTestableResources;
     @Mock
     private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+    @Mock
+    private IWindowManager mWindowManagerService;
 
     @Before
     public void setUp() {
@@ -62,6 +65,7 @@
                 Handler.getMain(),
                 mTestableResources.getResources(),
                 (UserManager) mContext.getSystemService(Context.USER_SERVICE),
+                mWindowManagerService,
                 mOverlayViewGlobalStateController
         );
 
@@ -125,8 +129,10 @@
 
         TestableUserSwitchTransitionViewController(Context context, Handler handler,
                 Resources resources, UserManager userManager,
+                IWindowManager windowManagerService,
                 OverlayViewGlobalStateController overlayViewGlobalStateController) {
-            super(context, handler, resources, userManager, overlayViewGlobalStateController);
+            super(context, handler, resources, userManager, windowManagerService,
+                    overlayViewGlobalStateController);
             mHandler = handler;
         }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
new file mode 100644
index 0000000..9f26d85
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager;
+import android.os.SystemProperties;
+
+public abstract class BlurUtils {
+
+    private static boolean mBlurSupportedSysProp = SystemProperties
+            .getBoolean("ro.surface_flinger.supports_background_blur", false);
+    private static boolean mBlurDisabledSysProp = SystemProperties
+            .getBoolean("persist.sys.sf.disable_blurs", false);
+
+    /**
+     * If this device can render blurs.
+     *
+     * @return {@code true} when supported.
+     */
+    public static boolean supportsBlursOnWindows() {
+        return mBlurSupportedSysProp && !mBlurDisabledSysProp && ActivityManager.isHighEndGfx();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 47e6645..6dcc9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -163,6 +163,11 @@
                             Log.d(TAG, "onActivityViewReady: calling startActivity, "
                                     + "bubble=" + getBubbleKey());
                         }
+                        if (mActivityView == null) {
+                            mBubbleController.removeBubble(getBubbleKey(),
+                                    BubbleController.DISMISS_INVALID_INTENT);
+                            return;
+                        }
                         try {
                             if (!mIsOverflow && mBubble.usingShortcutInfo()) {
                                 options.setApplyActivityFlagsForBubbles(true);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 015079f..d5fe9b2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1822,6 +1822,10 @@
             mExpandedBubble.getExpandedView().hideImeIfVisible();
         }
 
+        // Let the expanded animation controller know that it shouldn't animate child adds/reorders
+        // since we're about to animate collapsed.
+        mExpandedAnimationController.notifyPreparingToCollapse();
+
         final long startDelay =
                 (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
         postDelayed(() -> mExpandedAnimationController.collapseBackToStack(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 86fe10d..cb8995a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -92,6 +92,14 @@
     private int mScreenOrientation;
 
     private boolean mAnimatingExpand = false;
+
+    /**
+     * Whether we are animating other Bubbles UI elements out in preparation for a call to
+     * {@link #collapseBackToStack}. If true, we won't animate bubbles in response to adds or
+     * reorders.
+     */
+    private boolean mPreparingToCollapse = false;
+
     private boolean mAnimatingCollapse = false;
     private @Nullable Runnable mAfterExpand;
     private Runnable mAfterCollapse;
@@ -150,6 +158,7 @@
      */
     public void expandFromStack(
             @Nullable Runnable after, @Nullable Runnable leadBubbleEndAction) {
+        mPreparingToCollapse = false;
         mAnimatingCollapse = false;
         mAnimatingExpand = true;
         mAfterExpand = after;
@@ -165,9 +174,20 @@
         expandFromStack(after, null /* leadBubbleEndAction */);
     }
 
+    /**
+     * Sets that we're animating the stack collapsed, but haven't yet called
+     * {@link #collapseBackToStack}. This will temporarily suspend animations for bubbles that are
+     * added or re-ordered, since the upcoming collapse animation will handle positioning those
+     * bubbles in the collapsed stack.
+     */
+    public void notifyPreparingToCollapse() {
+        mPreparingToCollapse = true;
+    }
+
     /** Animate collapsing the bubbles back to their stacked position. */
     public void collapseBackToStack(PointF collapsePoint, Runnable after) {
         mAnimatingExpand = false;
+        mPreparingToCollapse = false;
         mAnimatingCollapse = true;
         mAfterCollapse = after;
         mCollapsePoint = collapsePoint;
@@ -501,12 +521,18 @@
             startOrUpdatePathAnimation(false /* expanding */);
         } else {
             child.setTranslationX(getBubbleLeft(index));
-            animationForChild(child)
-                    .translationY(
-                            getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */
-                            getExpandedY() /* to */)
-                    .start();
-            updateBubblePositions();
+
+            // If we're preparing to collapse, don't start animations since the collapse animation
+            // will take over and animate the new bubble into the correct (stacked) position.
+            if (!mPreparingToCollapse) {
+                animationForChild(child)
+                        .translationY(
+                                getExpandedY()
+                                        - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */
+                                getExpandedY() /* to */)
+                        .start();
+                updateBubblePositions();
+            }
         }
     }
 
@@ -532,12 +558,20 @@
 
     @Override
     void onChildReordered(View child, int oldIndex, int newIndex) {
-        updateBubblePositions();
+        if (mPreparingToCollapse) {
+            // If a re-order is received while we're preparing to collapse, ignore it. Once started,
+            // the collapse animation will animate all of the bubbles to their correct (stacked)
+            // position.
+            return;
+        }
 
-        // We expect reordering during collapse, since we'll put the last selected bubble on top.
-        // Update the collapse animation so they end up in the right stacked positions.
         if (mAnimatingCollapse) {
+            // If a re-order is received during collapse, update the animation so that the bubbles
+            // end up in the correct (stacked) position.
             startOrUpdatePathAnimation(false /* expanding */);
+        } else {
+            // Otherwise, animate the bubbles around to reflect their new order.
+            updateBubblePositions();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index d4d4d2a..eed5531 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -20,7 +20,7 @@
 import android.service.controls.Control
 import android.service.controls.ControlsProviderService
 import android.service.controls.actions.ControlAction
-import com.android.systemui.controls.UserAwareController
+import com.android.systemui.util.UserAwareController
 import java.util.function.Consumer
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 45ba1e6..496741b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -21,7 +21,7 @@
 import android.service.controls.ControlsProviderService
 import android.service.controls.actions.ControlAction
 import com.android.systemui.controls.ControlStatus
-import com.android.systemui.controls.UserAwareController
+import com.android.systemui.util.UserAwareController
 import com.android.systemui.controls.management.ControlsFavoritingActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import java.util.function.Consumer
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index 647dacc..b9f1666 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName
 import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.controls.UserAwareController
+import com.android.systemui.util.UserAwareController
 import com.android.systemui.statusbar.policy.CallbackController
 
 /**
@@ -26,7 +26,7 @@
  */
 interface ControlsListingController :
         CallbackController<ControlsListingController.ControlsListingCallback>,
-        UserAwareController {
+    UserAwareController {
 
     /**
      * @return the current list of services that satisfies the [ServiceListing].
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 353367e..ee02b85 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -89,6 +89,7 @@
             return when {
                 status != Control.STATUS_OK -> StatusBehavior::class
                 deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
+                template == ControlTemplate.NO_TEMPLATE -> TouchBehavior::class
                 template is ToggleTemplate -> ToggleBehavior::class
                 template is StatelessTemplate -> TouchBehavior::class
                 template is ToggleRangeTemplate -> ToggleRangeBehavior::class
@@ -235,7 +236,10 @@
         controlsController.action(cws.componentName, cws.ci, action)
     }
 
-    fun usePanel(): Boolean = deviceType in ControlViewHolder.FORCE_PANEL_DEVICES
+    fun usePanel(): Boolean {
+        return deviceType in ControlViewHolder.FORCE_PANEL_DEVICES ||
+            controlTemplate == ControlTemplate.NO_TEMPLATE
+    }
 
     fun bindBehavior(
         existingBehavior: Behavior?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index 2365e67..0a84f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -30,6 +31,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
+import com.android.systemui.util.UserAwareController;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -37,7 +39,7 @@
 
 import javax.inject.Inject;
 
-public class AutoAddTracker {
+public class AutoAddTracker implements UserAwareController {
 
     private static final String[][] CONVERT_PREFS = {
             {Key.QS_HOTSPOT_ADDED, HOTSPOT},
@@ -49,20 +51,39 @@
 
     private final ArraySet<String> mAutoAdded;
     private final Context mContext;
+    private int mUserId;
 
-    @Inject
-    public AutoAddTracker(Context context) {
+    public AutoAddTracker(Context context, int userId) {
         mContext = context;
+        mUserId = userId;
         mAutoAdded = new ArraySet<>(getAdded());
         // TODO: remove migration code and shared preferences keys after P release
-        for (String[] convertPref : CONVERT_PREFS) {
-            if (Prefs.getBoolean(context, convertPref[0], false)) {
-                setTileAdded(convertPref[1]);
-                Prefs.remove(context, convertPref[0]);
+        if (mUserId == UserHandle.USER_SYSTEM) {
+            for (String[] convertPref : CONVERT_PREFS) {
+                if (Prefs.getBoolean(context, convertPref[0], false)) {
+                    setTileAdded(convertPref[1]);
+                    Prefs.remove(context, convertPref[0]);
+                }
             }
         }
         mContext.getContentResolver().registerContentObserver(
-                Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver);
+                Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
+                UserHandle.USER_ALL);
+    }
+
+    @Override
+    public void changeUser(UserHandle newUser) {
+        if (newUser.getIdentifier() == mUserId) {
+            return;
+        }
+        mUserId = newUser.getIdentifier();
+        mAutoAdded.clear();
+        mAutoAdded.addAll(getAdded());
+    }
+
+    @Override
+    public int getCurrentUserId() {
+        return mUserId;
     }
 
     public boolean isAdded(String tile) {
@@ -86,12 +107,13 @@
     }
 
     private void saveTiles() {
-        Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
-                TextUtils.join(",", mAutoAdded));
+        Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
+                TextUtils.join(",", mAutoAdded), mUserId);
     }
 
     private Collection<String> getAdded() {
-        String current = Secure.getString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES);
+        String current = Secure.getStringForUser(mContext.getContentResolver(),
+                Secure.QS_AUTO_ADDED_TILES, mUserId);
         if (current == null) {
             return Collections.emptyList();
         }
@@ -102,7 +124,27 @@
     protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange) {
+            mAutoAdded.clear();
             mAutoAdded.addAll(getAdded());
         }
     };
+
+    public static class Builder {
+        private final Context mContext;
+        private int mUserId;
+
+        @Inject
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        public Builder setUserId(int userId) {
+            mUserId = userId;
+            return this;
+        }
+
+        public AutoAddTracker build() {
+            return new AutoAddTracker(mContext, mUserId);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 65d3572..858a7e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -255,6 +255,9 @@
         int currentUser = ActivityManager.getCurrentUser();
         if (currentUser != mCurrentUser) {
             mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
+            if (mAutoTiles != null) {
+                mAutoTiles.changeUser(UserHandle.of(currentUser));
+            }
         }
         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
         mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index fc8c8db..825919f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -19,6 +19,7 @@
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -34,6 +35,7 @@
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
+import com.android.systemui.util.UserAwareController;
 
 import java.util.ArrayList;
 import java.util.Objects;
@@ -43,7 +45,7 @@
 /**
  * Manages which tiles should be automatically added to QS.
  */
-public class AutoTileManager {
+public class AutoTileManager implements UserAwareController {
     private static final String TAG = "AutoTileManager";
 
     public static final String HOTSPOT = "hotspot";
@@ -52,7 +54,9 @@
     public static final String WORK = "work";
     public static final String NIGHT = "night";
     public static final String CAST = "cast";
-    public static final String SETTING_SEPARATOR = ":";
+    static final String SETTING_SEPARATOR = ":";
+
+    private UserHandle mCurrentUser;
 
     private final Context mContext;
     private final QSTileHost mHost;
@@ -66,43 +70,56 @@
     private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
 
     @Inject
-    public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
+    public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
+            QSTileHost host,
             @Background Handler handler,
             HotspotController hotspotController,
             DataSaverController dataSaverController,
             ManagedProfileController managedProfileController,
             NightDisplayListener nightDisplayListener,
             CastController castController) {
-        mAutoTracker = autoAddTracker;
         mContext = context;
         mHost = host;
+        mCurrentUser = mHost.getUserContext().getUser();
+        mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build();
         mHandler = handler;
         mHotspotController = hotspotController;
         mDataSaverController = dataSaverController;
         mManagedProfileController = managedProfileController;
         mNightDisplayListener = nightDisplayListener;
         mCastController = castController;
+
+        populateSettingsList();
+        startControllersAndSettingsListeners();
+    }
+
+    protected void startControllersAndSettingsListeners() {
         if (!mAutoTracker.isAdded(HOTSPOT)) {
-            hotspotController.addCallback(mHotspotCallback);
+            mHotspotController.addCallback(mHotspotCallback);
         }
         if (!mAutoTracker.isAdded(SAVER)) {
-            dataSaverController.addCallback(mDataSaverListener);
+            mDataSaverController.addCallback(mDataSaverListener);
         }
         if (!mAutoTracker.isAdded(WORK)) {
-            managedProfileController.addCallback(mProfileCallback);
+            mManagedProfileController.addCallback(mProfileCallback);
         }
         if (!mAutoTracker.isAdded(NIGHT)
                 && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            nightDisplayListener.setCallback(mNightDisplayCallback);
+            mNightDisplayListener.setCallback(mNightDisplayCallback);
         }
         if (!mAutoTracker.isAdded(CAST)) {
-            castController.addCallback(mCastCallback);
+            mCastController.addCallback(mCastCallback);
         }
-        populateSettingsList();
+
+        int settingsN = mAutoAddSettingList.size();
+        for (int i = 0; i < settingsN; i++) {
+            if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
+                mAutoAddSettingList.get(i).setListening(true);
+            }
+        }
     }
 
-    public void destroy() {
-        mAutoTracker.destroy();
+    protected void stopListening() {
         mHotspotController.removeCallback(mHotspotCallback);
         mDataSaverController.removeCallback(mDataSaverListener);
         mManagedProfileController.removeCallback(mProfileCallback);
@@ -116,6 +133,11 @@
         }
     }
 
+    public void destroy() {
+        stopListening();
+        mAutoTracker.destroy();
+    }
+
     /**
      * Populates a list with the pairs setting:spec in the config resource.
      * <p>
@@ -137,17 +159,39 @@
             if (split.length == 2) {
                 String setting = split[0];
                 String spec = split[1];
-                if (!mAutoTracker.isAdded(spec)) {
-                    AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
-                    mAutoAddSettingList.add(s);
-                    s.setListening(true);
-                }
+                // Populate all the settings. As they may not have been added in other users
+                AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
+                mAutoAddSettingList.add(s);
             } else {
                 Log.w(TAG, "Malformed item in array: " + tile);
             }
         }
     }
 
+    @Override
+    public void changeUser(UserHandle newUser) {
+        if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
+            mHandler.post(() -> changeUser(newUser));
+            return;
+        }
+        if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) {
+            return;
+        }
+        stopListening();
+        mCurrentUser = newUser;
+        int settingsN = mAutoAddSettingList.size();
+        for (int i = 0; i < settingsN; i++) {
+            mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier());
+        }
+        mAutoTracker.changeUser(newUser);
+        startControllersAndSettingsListeners();
+    }
+
+    @Override
+    public int getCurrentUserId() {
+        return mCurrentUser.getIdentifier();
+    }
+
     public void unmarkTileAsAutoAdded(String tabSpec) {
         mAutoTracker.setTileRemoved(tabSpec);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index daefef5..c211de0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -16,14 +16,19 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.content.Intent.ACTION_OVERLAY_CHANGED;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
 import android.content.om.OverlayInfo;
 import android.content.pm.PackageManager;
 import android.content.res.ApkAssets;
+import android.os.PatternMatcher;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -79,6 +84,19 @@
                 }
             };
 
+    // The primary user SysUI process doesn't get AppInfo changes from overlay package changes for
+    // the secondary user (b/158613864), so we need to update the interaction mode here as well
+    // as a fallback if we don't receive the configuration change
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) {
+                Log.d(TAG, "ACTION_OVERLAY_CHANGED");
+            }
+            updateCurrentInteractionMode(true /* notify */);
+        }
+    };
+
 
     @Inject
     public NavigationModeController(Context context,
@@ -92,6 +110,11 @@
         mUiBgExecutor = uiBgExecutor;
         deviceProvisionedController.addCallback(mDeviceProvisionedCallback);
 
+        IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+        overlayFilter.addDataScheme("package");
+        overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
+        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
+
         configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
             @Override
             public void onOverlayChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
rename to packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index d2776d2..693c270 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.controls
+package com.android.systemui.util
 
 import android.os.UserHandle
 
@@ -23,6 +23,8 @@
  * changes.
  */
 interface UserAwareController {
+    @JvmDefault
     fun changeUser(newUser: UserHandle) {}
+
     val currentUserId: Int
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 0ae9461..61f5a7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -40,6 +41,8 @@
 @SmallTest
 public class AutoAddTrackerTest extends SysuiTestCase {
 
+    private static final int USER = 0;
+
     private AutoAddTracker mAutoTracker;
 
     @Before
@@ -51,7 +54,7 @@
     public void testMigration() {
         Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
         Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
-        mAutoTracker = new AutoAddTracker(mContext);
+        mAutoTracker = new AutoAddTracker(mContext, USER);
 
         assertTrue(mAutoTracker.isAdded(SAVER));
         assertTrue(mAutoTracker.isAdded(WORK));
@@ -68,7 +71,7 @@
 
     @Test
     public void testChangeFromBackup() {
-        mAutoTracker = new AutoAddTracker(mContext);
+        mAutoTracker = new AutoAddTracker(mContext, USER);
 
         assertFalse(mAutoTracker.isAdded(SAVER));
 
@@ -82,7 +85,7 @@
 
     @Test
     public void testSetAdded() {
-        mAutoTracker = new AutoAddTracker(mContext);
+        mAutoTracker = new AutoAddTracker(mContext, USER);
 
         assertFalse(mAutoTracker.isAdded(SAVER));
         mAutoTracker.setTileAdded(SAVER);
@@ -94,16 +97,35 @@
 
     @Test
     public void testPersist() {
-        mAutoTracker = new AutoAddTracker(mContext);
+        mAutoTracker = new AutoAddTracker(mContext, USER);
 
         assertFalse(mAutoTracker.isAdded(SAVER));
         mAutoTracker.setTileAdded(SAVER);
 
         mAutoTracker.destroy();
-        mAutoTracker = new AutoAddTracker(mContext);
+        mAutoTracker = new AutoAddTracker(mContext, USER);
 
         assertTrue(mAutoTracker.isAdded(SAVER));
 
         mAutoTracker.destroy();
     }
+
+    @Test
+    public void testIndependentUsers() {
+        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker.setTileAdded(SAVER);
+
+        mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+        assertFalse(mAutoTracker.isAdded(SAVER));
+    }
+
+    @Test
+    public void testChangeUser() {
+        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker.setTileAdded(SAVER);
+
+        mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+        mAutoTracker.changeUser(UserHandle.of(USER));
+        assertTrue(mAutoTracker.isAdded(SAVER));
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 05cdd80..31779cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,18 +16,34 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContentResolver;
+import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -38,14 +54,18 @@
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.phone.AutoTileManagerTest.MyContextWrapper;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.HotspotController;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -64,9 +84,18 @@
     private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
     private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
 
+    private static final int USER = 0;
+
     @Mock private QSTileHost mQsTileHost;
     @Mock private AutoAddTracker mAutoAddTracker;
     @Mock private CastController mCastController;
+    @Mock private HotspotController mHotspotController;
+    @Mock private DataSaverController mDataSaverController;
+    @Mock private ManagedProfileController mManagedProfileController;
+    @Mock private NightDisplayListener mNightDisplayListener;
+    @Mock(answer = Answers.RETURNS_SELF)
+    private AutoAddTracker.Builder mAutoAddTrackerBuilder;
+    @Mock private Context mUserContext;
 
     private AutoTileManager mAutoTileManager;
 
@@ -82,20 +111,110 @@
                 }
         );
 
-        mAutoTileManager = createAutoTileManager();
+        when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
+        when(mQsTileHost.getUserContext()).thenReturn(mUserContext);
+        when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
+
+        mAutoTileManager = createAutoTileManager(new
+                MyContextWrapper(mContext));
     }
 
-    private AutoTileManager createAutoTileManager() {
-        return new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
+    @After
+    public void tearDown() {
+        mAutoTileManager.destroy();
+    }
+
+    private AutoTileManager createAutoTileManager(Context context) {
+        return new AutoTileManager(context, mAutoAddTrackerBuilder, mQsTileHost,
                 Handler.createAsync(TestableLooper.get(this).getLooper()),
-                mock(HotspotController.class),
-                mock(DataSaverController.class),
-                mock(ManagedProfileController.class),
-                mock(NightDisplayListener.class),
+                mHotspotController,
+                mDataSaverController,
+                mManagedProfileController,
+                mNightDisplayListener,
                 mCastController);
     }
 
     @Test
+    public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
+        TestableLooper.get(this).runWithLooper(() ->
+                mAutoTileManager.changeUser(UserHandle.of(USER + 1))
+        );
+
+        InOrder inOrderHotspot = inOrder(mHotspotController);
+        inOrderHotspot.verify(mHotspotController).removeCallback(any());
+        inOrderHotspot.verify(mHotspotController).addCallback(any());
+
+        InOrder inOrderDataSaver = inOrder(mDataSaverController);
+        inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
+        inOrderDataSaver.verify(mDataSaverController).addCallback(any());
+
+        InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
+        inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
+        inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
+
+        InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
+        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
+        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
+
+        InOrder inOrderCast = inOrder(mCastController);
+        inOrderCast.verify(mCastController).removeCallback(any());
+        inOrderCast.verify(mCastController).addCallback(any());
+
+        SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+        assertEquals(USER + 1, setting.getCurrentUser());
+        assertTrue(setting.isListening());
+    }
+
+    @Test
+    public void testChangeUserSomeCallbacksNotAdded() throws Exception {
+        when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
+        when(mAutoAddTracker.isAdded("work")).thenReturn(true);
+        when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
+        when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
+
+        TestableLooper.get(this).runWithLooper(() ->
+                mAutoTileManager.changeUser(UserHandle.of(USER + 1))
+        );
+
+        verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
+
+        InOrder inOrderHotspot = inOrder(mHotspotController);
+        inOrderHotspot.verify(mHotspotController).removeCallback(any());
+        inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
+
+        InOrder inOrderDataSaver = inOrder(mDataSaverController);
+        inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
+        inOrderDataSaver.verify(mDataSaverController).addCallback(any());
+
+        InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
+        inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
+        inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
+
+        InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
+        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
+        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
+
+        InOrder inOrderCast = inOrder(mCastController);
+        inOrderCast.verify(mCastController).removeCallback(any());
+        inOrderCast.verify(mCastController, never()).addCallback(any());
+
+        SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+        assertEquals(USER + 1, setting.getCurrentUser());
+        assertFalse(setting.isListening());
+    }
+
+    @Test
+    public void testGetCurrentUserId() throws Exception {
+        assertEquals(USER, mAutoTileManager.getCurrentUserId());
+
+        TestableLooper.get(this).runWithLooper(() ->
+                mAutoTileManager.changeUser(UserHandle.of(USER + 100))
+        );
+
+        assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
+    }
+
+    @Test
     public void nightTileAdded_whenActivated() {
         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
             return;
@@ -213,14 +332,14 @@
     public void testEmptyArray_doesNotCrash() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsAutoAdd, new String[0]);
-        createAutoTileManager();
+        createAutoTileManager(mContext).destroy();
     }
 
     @Test
     public void testMissingConfig_doesNotCrash() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsAutoAdd, null);
-        createAutoTileManager();
+        createAutoTileManager(mContext).destroy();
     }
 
     // Will only notify if it's listening
@@ -231,4 +350,22 @@
             s.onChange(false);
         }
     }
+
+    class MyContextWrapper extends ContextWrapper {
+
+        private TestableContentResolver mSpiedTCR;
+
+        MyContextWrapper(TestableContext context) {
+            super(context);
+            mSpiedTCR = spy(context.getContentResolver());
+            doNothing().when(mSpiedTCR).registerContentObserver(any(), anyBoolean(), any(),
+                    anyInt());
+            doNothing().when(mSpiedTCR).unregisterContentObserver(any());
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mSpiedTCR;
+        }
+    }
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index f14def6a..fd6f171 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -37,8 +37,8 @@
     private TetheringConstants() { }
 
     /**
-     * Extra used for communicating with the TetherService and TetherProvisioningActivity.
-     * Includes the type of tethering to enable if any.
+     * Extra used for communicating with the TetherService. Includes the type of tethering to
+     * enable if any.
      */
     public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     /**
@@ -56,38 +56,8 @@
      */
     public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
     /**
-     * Extra used for communicating with the TetherService and TetherProvisioningActivity.
-     * Contains the {@link ResultReceiver} which will receive provisioning results.
-     * Can not be empty.
+     * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+     * which will receive provisioning results. Can be left empty.
      */
     public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
-
-    /**
-     * Extra used for communicating with the TetherService and TetherProvisioningActivity.
-     * Contains the subId of current active cellular upstream.
-     * @hide
-     */
-    public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
-
-    /**
-     * Extra used for telling TetherProvisioningActivity the entitlement package name and class
-     * name to start UI entitlement check.
-     * @hide
-     */
-    public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME =
-            "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME";
-
-    /**
-     * Extra used for telling TetherService the intent action to start silent entitlement check.
-     * @hide
-     */
-    public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION =
-            "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION";
-
-    /**
-     * Extra used for TetherService to receive the response of provisioning check.
-     * @hide
-     */
-    public static final String EXTRA_TETHER_PROVISIONING_RESPONSE =
-            "android.net.extra.TETHER_PROVISIONING_RESPONSE";
 }
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 9dace70..3c6e8d8 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -19,10 +19,6 @@
 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
-import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
-import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
-import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
-import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_INVALID;
@@ -73,6 +69,7 @@
     protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
     private static final String ACTION_PROVISIONING_ALARM =
             "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
+    private static final String EXTRA_SUBID = "subId";
 
     private final ComponentName mSilentProvisioningService;
     private static final int MS_PER_HOUR = 60 * 60 * 1000;
@@ -200,9 +197,9 @@
         // till upstream change to cellular.
         if (mUsingCellularAsUpstream) {
             if (showProvisioningUi) {
-                runUiTetherProvisioning(downstreamType, config);
+                runUiTetherProvisioning(downstreamType, config.activeDataSubId);
             } else {
-                runSilentTetherProvisioning(downstreamType, config);
+                runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
             }
             mNeedReRunProvisioningUi = false;
         } else {
@@ -265,9 +262,9 @@
             if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
                 if (mNeedReRunProvisioningUi) {
                     mNeedReRunProvisioningUi = false;
-                    runUiTetherProvisioning(downstream, config);
+                    runUiTetherProvisioning(downstream, config.activeDataSubId);
                 } else {
-                    runSilentTetherProvisioning(downstream, config);
+                    runSilentTetherProvisioning(downstream, config.activeDataSubId);
                 }
             }
         }
@@ -364,7 +361,7 @@
      * @param subId default data subscription ID.
      */
     @VisibleForTesting
-    protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) {
+    protected void runSilentTetherProvisioning(int type, int subId) {
         if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
         // For silent provisioning, settings would stop tethering when entitlement fail.
         ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null);
@@ -372,20 +369,17 @@
         Intent intent = new Intent();
         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
         intent.putExtra(EXTRA_RUN_PROVISION, true);
-        intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi);
-        intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
-        intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
+        intent.putExtra(EXTRA_SUBID, subId);
         intent.setComponent(mSilentProvisioningService);
         // Only admin user can change tethering and SilentTetherProvisioning don't need to
         // show UI, it is fine to always start setting's background service as system user.
         mContext.startService(intent);
-        return intent;
     }
 
-    private void runUiTetherProvisioning(int type, final TetheringConfiguration config) {
+    private void runUiTetherProvisioning(int type, int subId) {
         ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null);
-        runUiTetherProvisioning(type, config, receiver);
+        runUiTetherProvisioning(type, subId, receiver);
     }
 
     /**
@@ -395,20 +389,17 @@
      * @param receiver to receive entitlement check result.
      */
     @VisibleForTesting
-    protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config,
-            ResultReceiver receiver) {
+    protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
         if (DBG) mLog.i("runUiTetherProvisioning: " + type);
 
         Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
-        intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
+        intent.putExtra(EXTRA_SUBID, subId);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         // Only launch entitlement UI for system user. Entitlement UI should not appear for other
         // user because only admin user is allowed to change tethering.
         mContext.startActivity(intent);
-        return intent;
     }
 
     // Not needed to check if this don't run on the handler thread because it's private.
@@ -640,7 +631,7 @@
             receiver.send(cacheValue, null);
         } else {
             ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
-            runUiTetherProvisioning(downstream, config, proxy);
+            runUiTetherProvisioning(downstream, config.activeDataSubId, proxy);
         }
     }
 }
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 1d45f12..48a600d 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -107,7 +107,6 @@
     public final String[] provisioningApp;
     public final String provisioningAppNoUi;
     public final int provisioningCheckPeriod;
-    public final String provisioningResponse;
 
     public final int activeDataSubId;
 
@@ -142,13 +141,10 @@
         enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
 
         provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
-        provisioningAppNoUi = getResourceString(res,
-                R.string.config_mobile_hotspot_provision_app_no_ui);
+        provisioningAppNoUi = getProvisioningAppNoUi(res);
         provisioningCheckPeriod = getResourceInteger(res,
                 R.integer.config_mobile_hotspot_provision_check_period,
                 0 /* No periodic re-check */);
-        provisioningResponse = getResourceString(res,
-                R.string.config_mobile_hotspot_provision_response);
 
         mOffloadPollInterval = getResourceInteger(res,
                 R.integer.config_tether_offload_poll_interval,
@@ -341,9 +337,9 @@
         return copy(LEGACY_DHCP_DEFAULT_RANGE);
     }
 
-    private static String getResourceString(Resources res, final int resId) {
+    private static String getProvisioningAppNoUi(Resources res) {
         try {
-            return res.getString(resId);
+            return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui);
         } catch (Resources.NotFoundException e) {
             return "";
         }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 354e753..72fa916 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -16,16 +16,8 @@
 
 package com.android.networkstack.tethering;
 
-import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
-import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
-import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
-import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
-import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
-import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
 import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringManager.TETHERING_INVALID;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -52,7 +44,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.net.util.SharedLog;
 import android.os.Bundle;
@@ -62,7 +53,6 @@
 import android.os.SystemProperties;
 import android.os.test.TestLooper;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 
 import androidx.test.filters.SmallTest;
@@ -86,7 +76,6 @@
 
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
-    private static final String PROVISIONING_APP_RESPONSE = "app_response";
 
     @Mock private CarrierConfigManager mCarrierConfigManager;
     @Mock private Context mContext;
@@ -133,51 +122,15 @@
         }
 
         @Override
-        protected Intent runUiTetherProvisioning(int type,
-                final TetheringConfiguration config, final ResultReceiver receiver) {
-            Intent intent = super.runUiTetherProvisioning(type, config, receiver);
-            assertUiTetherProvisioningIntent(type, config, receiver, intent);
+        protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
             uiProvisionCount++;
             receiver.send(fakeEntitlementResult, null);
-            return intent;
-        }
-
-        private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config,
-                final ResultReceiver receiver, final Intent intent) {
-            assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction());
-            assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
-            final String[] appName = intent.getStringArrayExtra(
-                    EXTRA_TETHER_UI_PROVISIONING_APP_NAME);
-            assertEquals(PROVISIONING_APP_NAME.length, appName.length);
-            for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) {
-                assertEquals(PROVISIONING_APP_NAME[i], appName[i]);
-            }
-            assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK));
-            assertEquals(config.activeDataSubId,
-                    intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
         }
 
         @Override
-        protected Intent runSilentTetherProvisioning(int type,
-                final TetheringConfiguration config) {
-            Intent intent = super.runSilentTetherProvisioning(type, config);
-            assertSilentTetherProvisioning(type, config, intent);
+        protected void runSilentTetherProvisioning(int type, int subId) {
             silentProvisionCount++;
             addDownstreamMapping(type, fakeEntitlementResult);
-            return intent;
-        }
-
-        private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config,
-                final Intent intent) {
-            assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
-            assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false));
-            assertEquals(PROVISIONING_NO_UI_APP_NAME,
-                    intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION));
-            assertEquals(PROVISIONING_APP_RESPONSE,
-                    intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE));
-            assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK));
-            assertEquals(config.activeDataSubId,
-                    intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
         }
     }
 
@@ -234,8 +187,6 @@
                 .thenReturn(PROVISIONING_APP_NAME);
         when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
                 .thenReturn(PROVISIONING_NO_UI_APP_NAME);
-        when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn(
-                PROVISIONING_APP_RESPONSE);
         // Act like the CarrierConfigManager is present and ready unless told otherwise.
         when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
                 .thenReturn(mCarrierConfigManager);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 3121863..1999ad7 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -61,8 +61,6 @@
     private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
 
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
-    private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
-    private static final String PROVISIONING_APP_RESPONSE = "app_response";
     @Mock private Context mContext;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private Resources mResources;
@@ -390,8 +388,6 @@
                 new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
         assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
         assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
-        assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME);
-        assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE);
     }
 
     private void setUpResourceForSubId() {
@@ -407,10 +403,6 @@
                 new int[0]);
         when(mResourcesForSubId.getStringArray(
                 R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
-        when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
-                .thenReturn(PROVISIONING_NO_UI_APP_NAME);
-        when(mResourcesForSubId.getString(
-                R.string.config_mobile_hotspot_provision_response)).thenReturn(
-                PROVISIONING_APP_RESPONSE);
     }
+
 }
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 8d4efed..5787f7c4 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -26,6 +26,7 @@
 import android.hardware.rebootescrow.IRebootEscrow;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -244,6 +245,9 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Could not retrieve escrow data");
             return null;
+        } catch (ServiceSpecificException e) {
+            Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
+            return null;
         }
     }
 
@@ -335,7 +339,7 @@
 
         try {
             rebootEscrow.storeKey(new byte[32]);
-        } catch (RemoteException e) {
+        } catch (RemoteException | ServiceSpecificException e) {
             Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
         }
 
@@ -373,7 +377,7 @@
             rebootEscrow.storeKey(escrowKey.getKeyBytes());
             armedRebootEscrow = true;
             Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
-        } catch (RemoteException e) {
+        } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
         }
 
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 098b2ef..4e1a234 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -301,6 +301,7 @@
     JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper);
 
     Mutex mLock;
+    Mutex mStreamLock;
     jweak mThiz;
     sp<Looper> mLooper;
 
@@ -338,6 +339,7 @@
 }
 
 int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
+    Mutex::Autolock autoLock(&mStreamLock);
     KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
     if (connections.indexOfKey(streamId) < 0) {
         connections.add(streamId, Connection());
@@ -412,6 +414,7 @@
 }
 
 int JTvInputHal::removeStream(int deviceId, int streamId) {
+    Mutex::Autolock autoLock(&mStreamLock);
     KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
     if (connections.indexOfKey(streamId) < 0) {
         return BAD_VALUE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 10ad07c..7b624ca 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -631,7 +631,7 @@
 
     /**
      * Whether or not device admin feature is supported. If it isn't return defaults for all
-     * public methods.
+     * public methods, unless the caller has the appropriate permission for a particular method.
      */
     final boolean mHasFeature;
 
@@ -6032,7 +6032,8 @@
 
     @Override
     public void lockNow(int flags, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE)
+                != PackageManager.PERMISSION_GRANTED) {
             return;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 4127fec..c4d1211 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -43,6 +43,7 @@
 import android.content.pm.UserInfo;
 import android.hardware.rebootescrow.IRebootEscrow;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 
@@ -178,6 +179,13 @@
     }
 
     @Test
+    public void clearCredentials_HalFailure_NonFatal() throws Exception {
+        doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any());
+        mService.clearRebootEscrow();
+        verify(mRebootEscrow).storeKey(eq(new byte[32]));
+    }
+
+    @Test
     public void armService_Success() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
         mService.setRebootEscrowListener(mockListener);
@@ -200,6 +208,24 @@
     }
 
     @Test
+    public void armService_HalFailure_NonFatal() throws Exception {
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mRebootEscrow);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mRebootEscrow, never()).storeKey(any());
+
+        assertNull(
+                mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
+        doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any());
+        assertFalse(mService.armRebootEscrowIfNeeded());
+        verify(mRebootEscrow).storeKey(any());
+    }
+
+    @Test
     public void armService_MultipleUsers_Success() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
         mService.setRebootEscrowListener(mockListener);