Merge "Add RIL_REQUEST_SET_N1_MODE_ENABLED and_REQUEST_IS_N1_MODE_ENABLED"
diff --git a/core/api/current.txt b/core/api/current.txt
index dabb8b6..f80c095 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -54043,23 +54043,22 @@
   }
 
   public final class TextAppearanceInfo implements android.os.Parcelable {
-    ctor public TextAppearanceInfo(@NonNull android.widget.TextView);
     method public int describeContents();
-    method @Nullable public String getFontFamilyName();
     method @Nullable public String getFontFeatureSettings();
     method @Nullable public String getFontVariationSettings();
+    method @ColorInt public int getHighlightTextColor();
+    method @ColorInt public int getHintTextColor();
     method public float getLetterSpacing();
     method public int getLineBreakStyle();
     method public int getLineBreakWordStyle();
-    method public int getMaxLength();
+    method @ColorInt public int getLinkTextColor();
+    method @ColorInt public int getShadowColor();
     method @Px public float getShadowDx();
     method @Px public float getShadowDy();
     method @Px public float getShadowRadius();
+    method @Nullable public String getSystemFontFamilyName();
     method @ColorInt public int getTextColor();
-    method @ColorInt public int getTextColorHighlight();
-    method @ColorInt public int getTextColorHint();
-    method @Nullable public android.content.res.ColorStateList getTextColorLink();
-    method @IntRange(from=0xffffffff, to=android.graphics.fonts.FontStyle.FONT_WEIGHT_MAX) public int getTextFontWeight();
+    method @IntRange(from=android.graphics.fonts.FontStyle.FONT_WEIGHT_UNSPECIFIED, to=android.graphics.fonts.FontStyle.FONT_WEIGHT_MAX) public int getTextFontWeight();
     method @NonNull public android.os.LocaleList getTextLocales();
     method public float getTextScaleX();
     method @Px public float getTextSize();
@@ -54071,6 +54070,33 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextAppearanceInfo> CREATOR;
   }
 
+  public static final class TextAppearanceInfo.Builder {
+    ctor public TextAppearanceInfo.Builder();
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo build();
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setAllCaps(boolean);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setElegantTextHeight(boolean);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setFallbackLineSpacing(boolean);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setFontFeatureSettings(@Nullable String);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setFontVariationSettings(@Nullable String);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setHighlightTextColor(@ColorInt int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setHintTextColor(@ColorInt int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setLetterSpacing(float);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setLineBreakStyle(int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setLineBreakWordStyle(int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setLinkTextColor(@ColorInt int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setShadowColor(@ColorInt int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setShadowDx(@Px float);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setShadowDy(@Px float);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setShadowRadius(@Px float);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setSystemFontFamilyName(@Nullable String);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setTextColor(@ColorInt int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setTextFontWeight(@IntRange(from=android.graphics.fonts.FontStyle.FONT_WEIGHT_UNSPECIFIED, to=android.graphics.fonts.FontStyle.FONT_WEIGHT_MAX) int);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setTextLocales(@NonNull android.os.LocaleList);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setTextScaleX(float);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setTextSize(@Px float);
+    method @NonNull public android.view.inputmethod.TextAppearanceInfo.Builder setTextStyle(int);
+  }
+
   public final class TextAttribute implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.os.PersistableBundle getExtras();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1b3282e..60e090e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1407,9 +1407,41 @@
     public static final int OP_FOREGROUND_SERVICE_SPECIAL_USE =
             AppProtoEnums.APP_OP_FOREGROUND_SERVICE_SPECIAL_USE;
 
+    /**
+     * Exempt from start foreground service from background restriction.
+     *
+     * Only to be used by the system.
+     *
+     * @hide
+     */
+    public static final int OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION =
+            AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION;
+
+    /**
+     * Exempt from start foreground service from background with while in user permission
+     * restriction.
+     *
+     * Only to be used by the system.
+     *
+     * @hide
+     */
+    public static final int OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION =
+            AppProtoEnums
+                    .APP_OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION;
+
+    /**
+     * Hide foreground service stop button in quick settings.
+     *
+     * Only to be used by the system.
+     *
+     * @hide
+     */
+    public static final int OP_SYSTEM_EXEMPT_FROM_FGS_STOP_BUTTON =
+            AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_FGS_STOP_BUTTON;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 128;
+    public static final int _NUM_OP = 131;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1947,6 +1979,38 @@
     public static final String OPSTR_FOREGROUND_SERVICE_SPECIAL_USE =
             "android:foreground_service_special_use";
 
+    /**
+     * Exempt from start foreground service from background restriction.
+     *
+     * Only to be used by the system.
+     *
+     * @hide
+     */
+    public static final String OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION =
+            "android:system_exempt_from_fgs_bg_start_restriction";
+
+    /**
+     * Exempt from start foreground service from background with while in user permission
+     * restriction.
+     *
+     * Only to be used by the system.
+     *
+     * @hide
+     */
+    public static final String
+            OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION =
+            "android:system_exempt_from_fgs_bg_start_while_in_use_permission_restriction";
+
+    /**
+     * Hide foreground service stop button in quick settings.
+     *
+     * Only to be used by the system.
+     *
+     * @hide
+     */
+    public static final String OPSTR_SYSTEM_EXEMPT_FROM_FGS_STOP_BUTTON =
+            "android:system_exempt_from_fgs_stop_button";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2441,6 +2505,17 @@
         new AppOpInfo.Builder(OP_FOREGROUND_SERVICE_SPECIAL_USE,
                 OPSTR_FOREGROUND_SERVICE_SPECIAL_USE, "FOREGROUND_SERVICE_SPECIAL_USE")
                 .setPermission(Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE).build(),
+        new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION,
+                OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION,
+                "SYSTEM_EXEMPT_FROM_FGS_BG_START_RESTRICTION").build(),
+        new AppOpInfo.Builder(
+                OP_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION,
+                OPSTR_SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION,
+                "SYSTEM_EXEMPT_FROM_FGS_BG_START_WHILE_IN_USE_PERMISSION_RESTRICTION")
+                .build(),
+        new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_FGS_STOP_BUTTON,
+                OPSTR_SYSTEM_EXEMPT_FROM_FGS_STOP_BUTTON,
+                "SYSTEM_EXEMPT_FROM_FGS_STOP_BUTTON").build()
     };
 
     // The number of longs needed to form a full bitmask of app ops
@@ -2498,12 +2573,6 @@
                 sPermToOp.put(sAppOpInfos[op].permission, op);
             }
         }
-
-        if ((_NUM_OP + Long.SIZE - 1) / Long.SIZE != 2) {
-            // The code currently assumes that the length of sAppOpsNotedInThisBinderTransaction is
-            // two longs
-            throw new IllegalStateException("notedAppOps collection code assumes < 128 appops");
-        }
     }
 
     /** Config used to control app ops access messages sampling */
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 37c5cab..8111184 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -16,9 +16,12 @@
 
 package android.app;
 
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.backup.IBackupCallback;
 import android.app.backup.IBackupManager;
 import android.os.ParcelFileDescriptor;
+
+import com.android.internal.infra.AndroidFuture;
  
 /**
  * Interface presented by applications being asked to participate in the
@@ -193,4 +196,14 @@
      * @param message The message to be passed to the agent's application in an exception.
      */
     void fail(String message);
+
+    /**
+     * Provides the logging results that were accumulated in the BackupAgent during a backup or
+     * restore operation. This method should be called after the agent completes its backup or
+     * restore.
+     *
+     * @param resultsFuture a future that is completed with the logging results.
+     */
+    void getLoggerResults(
+            in AndroidFuture<List<BackupRestoreEventLogger.DataTypeResult>> resultsFuture);
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index b1b59b0..a4f612d 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -41,6 +41,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
 
 import libcore.io.IoUtils;
 
@@ -202,6 +203,7 @@
 
     Handler mHandler = null;
 
+    @Nullable private volatile BackupRestoreEventLogger mLogger = null;
     @Nullable private UserHandle mUser;
      // This field is written from the main thread (in onCreate), and read in a Binder thread (in
      // onFullBackup that is called from system_server via Binder).
@@ -234,6 +236,20 @@
         } catch (InterruptedException e) { /* ignored */ }
     }
 
+    /**
+     * Get a logger to record app-specific backup and restore events that are happening during a
+     * backup or restore operation.
+     *
+     * <p>The logger instance had been created by the system with the correct {@link
+     * BackupRestoreEventLogger.OperationType} that corresponds to the operation the {@code
+     * BackupAgent} is currently handling.
+     *
+     * @hide
+     */
+    @Nullable
+    public BackupRestoreEventLogger getBackupRestoreEventLogger() {
+        return mLogger;
+    }
 
     public BackupAgent() {
         super(null);
@@ -264,6 +280,9 @@
      * @hide
      */
     public void onCreate(UserHandle user, @OperationType int operationType) {
+        // TODO: Instantiate with the correct type using a parameter.
+        mLogger = new BackupRestoreEventLogger(BackupRestoreEventLogger.OperationType.BACKUP);
+
         onCreate();
 
         mUser = user;
@@ -1305,6 +1324,16 @@
                 }
             }
         }
+
+        @Override
+        public void getLoggerResults(
+                AndroidFuture<List<BackupRestoreEventLogger.DataTypeResult>> in) {
+            if (mLogger != null) {
+                in.complete(mLogger.getLoggingResults());
+            } else {
+                in.complete(Collections.emptyList());
+            }
+        }
     }
 
     static class FailRunnable implements Runnable {
diff --git a/core/java/android/app/backup/BackupRestoreEventLogger.aidl b/core/java/android/app/backup/BackupRestoreEventLogger.aidl
new file mode 100644
index 0000000..d6ef4e6
--- /dev/null
+++ b/core/java/android/app/backup/BackupRestoreEventLogger.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+parcelable BackupRestoreEventLogger.DataTypeResult;
\ No newline at end of file
diff --git a/core/java/android/app/backup/BackupRestoreEventLogger.java b/core/java/android/app/backup/BackupRestoreEventLogger.java
index 6f62c8a..68740cb 100644
--- a/core/java/android/app/backup/BackupRestoreEventLogger.java
+++ b/core/java/android/app/backup/BackupRestoreEventLogger.java
@@ -19,6 +19,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import java.lang.annotation.Retention;
@@ -312,7 +316,7 @@
     /**
      * Encapsulate logging results for a single data type.
      */
-    public static class DataTypeResult {
+    public static class DataTypeResult implements Parcelable {
         @BackupRestoreDataType
         private final String mDataType;
         private int mSuccessCount;
@@ -362,5 +366,57 @@
         public byte[] getMetadataHash() {
             return mMetadataHash;
         }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mDataType);
+
+            dest.writeInt(mSuccessCount);
+
+            dest.writeInt(mFailCount);
+
+            Bundle errorsBundle = new Bundle();
+            for (Map.Entry<String, Integer> e : mErrors.entrySet()) {
+                errorsBundle.putInt(e.getKey(), e.getValue());
+            }
+            dest.writeBundle(errorsBundle);
+
+            dest.writeByteArray(mMetadataHash);
+        }
+
+        public static final Parcelable.Creator<DataTypeResult> CREATOR =
+                new Parcelable.Creator<>() {
+                    public DataTypeResult createFromParcel(Parcel in) {
+                        String dataType = in.readString();
+
+                        int successCount = in.readInt();
+
+                        int failCount = in.readInt();
+
+                        Map<String, Integer> errors = new ArrayMap<>();
+                        Bundle errorsBundle = in.readBundle(getClass().getClassLoader());
+                        for (String key : errorsBundle.keySet()) {
+                            errors.put(key, errorsBundle.getInt(key));
+                        }
+
+                        byte[] metadataHash = in.createByteArray();
+
+                        DataTypeResult result = new DataTypeResult(dataType);
+                        result.mSuccessCount = successCount;
+                        result.mFailCount = failCount;
+                        result.mErrors.putAll(errors);
+                        result.mMetadataHash = metadataHash;
+                        return result;
+                    }
+
+                    public DataTypeResult[] newArray(int size) {
+                        return new DataTypeResult[size];
+                    }
+                };
     }
 }
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index 2581daa..d628b7f 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -30,6 +30,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import dalvik.system.CloseGuard;
 
 import java.util.List;
@@ -79,6 +81,7 @@
     private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
 
     private final AppPredictionSessionId mSessionId;
+    @GuardedBy("itself")
     private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
 
     /**
@@ -94,7 +97,7 @@
         IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
         mPredictionManager = IPredictionManager.Stub.asInterface(b);
         mSessionId = new AppPredictionSessionId(
-                context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
+                context.getPackageName() + ":" + UUID.randomUUID(), context.getUserId());
         try {
             mPredictionManager.createPredictionSession(predictionContext, mSessionId, getToken());
         } catch (RemoteException e) {
@@ -155,6 +158,15 @@
      */
     public void registerPredictionUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
             @NonNull AppPredictor.Callback callback) {
+        synchronized (mRegisteredCallbacks) {
+            registerPredictionUpdatesLocked(callbackExecutor, callback);
+        }
+    }
+
+    @GuardedBy("mRegisteredCallbacks")
+    private void registerPredictionUpdatesLocked(
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull AppPredictor.Callback callback) {
         if (mIsClosed.get()) {
             throw new IllegalStateException("This client has already been destroyed.");
         }
@@ -183,6 +195,13 @@
      * @param callback The callback to be unregistered.
      */
     public void unregisterPredictionUpdates(@NonNull AppPredictor.Callback callback) {
+        synchronized (mRegisteredCallbacks) {
+            unregisterPredictionUpdatesLocked(callback);
+        }
+    }
+
+    @GuardedBy("mRegisteredCallbacks")
+    private void unregisterPredictionUpdatesLocked(@NonNull AppPredictor.Callback callback) {
         if (mIsClosed.get()) {
             throw new IllegalStateException("This client has already been destroyed.");
         }
@@ -235,7 +254,7 @@
         }
 
         try {
-            mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice(targets),
+            mPredictionManager.sortAppTargets(mSessionId, new ParceledListSlice<>(targets),
                     new CallbackWrapper(callbackExecutor, callback));
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to sort targets", e);
@@ -251,19 +270,25 @@
         if (!mIsClosed.getAndSet(true)) {
             mCloseGuard.close();
 
-            // Do destroy;
-            try {
-                mPredictionManager.onDestroyPredictionSession(mSessionId);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to notify app target event", e);
-                e.rethrowAsRuntimeException();
+            synchronized (mRegisteredCallbacks) {
+                destroySessionLocked();
             }
-            mRegisteredCallbacks.clear();
         } else {
             throw new IllegalStateException("This client has already been destroyed.");
         }
     }
 
+    @GuardedBy("mRegisteredCallbacks")
+    private void destroySessionLocked() {
+        try {
+            mPredictionManager.onDestroyPredictionSession(mSessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to notify app target event", e);
+            e.rethrowAsRuntimeException();
+        }
+        mRegisteredCallbacks.clear();
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6eeb5e7..8a09cd7 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -25,9 +25,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.ActivityThread;
-import android.content.ContentResolver;
-import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -882,9 +879,8 @@
     @NonNull
     @RequiresPermission(READ_DEVICE_CONFIG)
     public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
         return new Properties(namespace,
-                Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
+                Settings.Config.getStrings(namespace, Arrays.asList(names)));
     }
 
     /**
@@ -1023,8 +1019,7 @@
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean setProperty(@NonNull String namespace, @NonNull String name,
             @Nullable String value, boolean makeDefault) {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        return Settings.Config.putString(contentResolver, namespace, name, value, makeDefault);
+        return Settings.Config.putString(namespace, name, value, makeDefault);
     }
 
     /**
@@ -1045,8 +1040,7 @@
     @SystemApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean setProperties(@NonNull Properties properties) throws BadConfigException {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        return Settings.Config.setStrings(contentResolver, properties.getNamespace(),
+        return Settings.Config.setStrings(properties.getNamespace(),
                 properties.mMap);
     }
 
@@ -1062,8 +1056,7 @@
     @SystemApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        return Settings.Config.deleteString(contentResolver, namespace, name);
+        return Settings.Config.deleteString(namespace, name);
     }
 
     /**
@@ -1094,8 +1087,7 @@
     @SystemApi
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        Settings.Config.resetToDefaults(contentResolver, resetMode, namespace);
+        Settings.Config.resetToDefaults(resetMode, namespace);
     }
 
     /**
@@ -1112,8 +1104,7 @@
      */
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static void setSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        Settings.Config.setSyncDisabledMode(contentResolver, syncDisabledMode);
+        Settings.Config.setSyncDisabledMode(syncDisabledMode);
     }
 
     /**
@@ -1124,8 +1115,7 @@
      */
     @RequiresPermission(WRITE_DEVICE_CONFIG)
     public static @SyncDisabledMode int getSyncDisabledMode() {
-        ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
-        return Settings.Config.getSyncDisabledMode(contentResolver);
+        return Settings.Config.getSyncDisabledMode();
     }
 
     /**
@@ -1148,8 +1138,7 @@
             @NonNull String namespace,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnPropertiesChangedListener onPropertiesChangedListener) {
-        enforceReadPermission(ActivityThread.currentApplication().getApplicationContext(),
-                namespace);
+        enforceReadPermission(namespace);
         synchronized (sLock) {
             Pair<String, Executor> oldNamespace = sListeners.get(onPropertiesChangedListener);
             if (oldNamespace == null) {
@@ -1216,7 +1205,7 @@
                     }
                 }
             };
-            ActivityThread.currentApplication().getContentResolver()
+            Settings.Config
                     .registerContentObserver(createNamespaceUri(namespace), true, contentObserver);
             sNamespaces.put(namespace, new Pair<>(contentObserver, 1));
         }
@@ -1240,8 +1229,7 @@
             sNamespaces.put(namespace, new Pair<>(namespaceCount.first, namespaceCount.second - 1));
         } else {
             // Decrementing a namespace to zero means we no longer need its ContentObserver.
-            ActivityThread.currentApplication().getContentResolver()
-                    .unregisterContentObserver(namespaceCount.first);
+            Settings.Config.unregisterContentObserver(namespaceCount.first);
             sNamespaces.remove(namespace);
         }
     }
@@ -1281,8 +1269,8 @@
      * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
      * @hide
      */
-    public static void enforceReadPermission(Context context, String namespace) {
-        if (context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
+    public static void enforceReadPermission(String namespace) {
+        if (Settings.Config.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
                 != PackageManager.PERMISSION_GRANTED) {
             if (!PUBLIC_NAMESPACES.contains(namespace)) {
                 throw new SecurityException("Permission denial: reading from settings requires:"
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 52b1adb..ef448f5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -47,9 +47,11 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PermissionName;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.location.ILocationManager;
@@ -3344,7 +3346,7 @@
         public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
                 List<String> names) {
             String namespace = prefix.substring(0, prefix.length() - 1);
-            DeviceConfig.enforceReadPermission(ActivityThread.currentApplication(), namespace);
+            DeviceConfig.enforceReadPermission(namespace);
             ArrayMap<String, String> keyValues = new ArrayMap<>();
             int currentGeneration = -1;
 
@@ -18002,20 +18004,36 @@
 
         /**
          * Look up a name in the database.
-         * @param resolver to access the database with
          * @param name to look up in the table
          * @return the corresponding value, or null if not present
          *
          * @hide
          */
         @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
-        static String getString(ContentResolver resolver, String name) {
+        static String getString(String name) {
+            ContentResolver resolver = getContentResolver();
             return sNameValueCache.getStringForUser(resolver, name, resolver.getUserId());
         }
 
         /**
          * Look up a list of names in the database, within the specified namespace.
          *
+         * @param namespace to which the names belong
+         * @param names to look up in the table
+         * @return a non null, but possibly empty, map from name to value for any of the names that
+         *         were found during lookup.
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+        public static Map<String, String> getStrings(@NonNull String namespace,
+                @NonNull List<String> names) {
+            return getStrings(getContentResolver(), namespace, names);
+        }
+
+        /**
+         * Look up a list of names in the database, within the specified namespace.
+         *
          * @param resolver to access the database with
          * @param namespace to which the names belong
          * @param names to look up in the table
@@ -18053,7 +18071,6 @@
          * <strong>not</strong> be set as the default.
          * </p>
          *
-         * @param resolver to access the database with.
          * @param namespace to store the name/value pair in.
          * @param name to store.
          * @param value to associate with the name.
@@ -18065,8 +18082,9 @@
          * @hide
          */
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static boolean putString(@NonNull ContentResolver resolver, @NonNull String namespace,
+        public static boolean putString(@NonNull String namespace,
                 @NonNull String name, @Nullable String value, boolean makeDefault) {
+            ContentResolver resolver = getContentResolver();
             return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name),
                     value, null, makeDefault, resolver.getUserId(),
                     DEFAULT_OVERRIDEABLE_BY_RESTORE);
@@ -18076,6 +18094,23 @@
          * Clear all name/value pairs for the provided namespace and save new name/value pairs in
          * their place.
          *
+         * @param namespace to which the names should be set.
+         * @param keyValues map of key names (without the prefix) to values.
+         * @return true if the name/value pairs were set, false if setting was blocked
+         *
+         * @hide
+         */
+        @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+        public static boolean setStrings(@NonNull String namespace,
+                @NonNull Map<String, String> keyValues)
+                throws DeviceConfig.BadConfigException {
+            return setStrings(getContentResolver(), namespace, keyValues);
+        }
+
+        /**
+         * Clear all name/value pairs for the provided namespace and save new name/value pairs in
+         * their place.
+         *
          * @param resolver to access the database with.
          * @param namespace to which the names should be set.
          * @param keyValues map of key names (without the prefix) to values.
@@ -18106,7 +18141,6 @@
         /**
          * Delete a name/value pair from the database for the specified namespace.
          *
-         * @param resolver to access the database with.
          * @param namespace to delete the name/value pair from.
          * @param name to delete.
          * @return true if the value was deleted, false on database errors. If the name/value pair
@@ -18117,8 +18151,9 @@
          * @hide
          */
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+        static boolean deleteString(@NonNull String namespace,
                 @NonNull String name) {
+            ContentResolver resolver = getContentResolver();
             return sNameValueCache.deleteStringForUser(resolver,
                     createCompositeName(namespace, name), resolver.getUserId());
         }
@@ -18129,7 +18164,6 @@
          * The method accepts an optional prefix parameter. If provided, only pairs with a name that
          * starts with the exact prefix will be reset. Otherwise all will be reset.
          *
-         * @param resolver Handle to the content resolver.
          * @param resetMode The reset mode to use.
          * @param namespace Optionally, to limit which which namespace is reset.
          *
@@ -18138,9 +18172,10 @@
          * @hide
          */
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
+        static void resetToDefaults(@ResetMode int resetMode,
                 @Nullable String namespace) {
             try {
+                ContentResolver resolver = getContentResolver();
                 Bundle arg = new Bundle();
                 arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
                 arg.putInt(CALL_METHOD_RESET_MODE_KEY, resetMode);
@@ -18163,9 +18198,9 @@
          */
         @SuppressLint("AndroidFrameworkRequiresPermission")
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static void setSyncDisabledMode(
-                @NonNull ContentResolver resolver, @SyncDisabledMode int disableSyncMode) {
+        static void setSyncDisabledMode(@SyncDisabledMode int disableSyncMode) {
             try {
+                ContentResolver resolver = getContentResolver();
                 Bundle args = new Bundle();
                 args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode);
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
@@ -18184,8 +18219,9 @@
          */
         @SuppressLint("AndroidFrameworkRequiresPermission")
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
-        static int getSyncDisabledMode(@NonNull ContentResolver resolver) {
+        static int getSyncDisabledMode() {
             try {
+                ContentResolver resolver = getContentResolver();
                 Bundle args = Bundle.EMPTY;
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
                 Bundle bundle = cp.call(resolver.getAttributionSource(),
@@ -18202,7 +18238,6 @@
         /**
          * Register callback for monitoring Config table.
          *
-         * @param resolver Handle to the content resolver.
          * @param callback callback to register
          *
          * @hide
@@ -18213,6 +18248,50 @@
             registerMonitorCallbackAsUser(resolver, resolver.getUserId(), callback);
         }
 
+
+        /**
+         * Register a content observer
+         *
+         * @hide
+         */
+        public static void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,
+                @NonNull ContentObserver observer) {
+            ActivityThread.currentApplication().getContentResolver()
+               .registerContentObserver(uri, notifyForDescendants, observer);
+        }
+
+        /**
+         * Unregister a content observer
+         *
+         * @hide
+         */
+        public static void unregisterContentObserver(@NonNull ContentObserver observer) {
+            ActivityThread.currentApplication().getContentResolver()
+              .unregisterContentObserver(observer);
+        }
+
+        /**
+         * Determine whether the calling process of an IPC <em>or you</em> have been
+         * granted a particular permission.  This is the same as
+         * {@link #checkCallingPermission}, except it grants your own permissions
+         * if you are not currently processing an IPC.  Use with care!
+         *
+         * @param permission The name of the permission being checked.
+         *
+         * @return {@link PackageManager#PERMISSION_GRANTED} if the calling
+         * pid/uid is allowed that permission, or
+         * {@link PackageManager#PERMISSION_DENIED} if it is not.
+         *
+         * @see PackageManager#checkPermission(String, String)
+         * @see #checkPermission
+         * @see #checkCallingPermission
+         * @hide
+         */
+        public static int checkCallingOrSelfPermission(@NonNull @PermissionName String permission) {
+            return ActivityThread.currentApplication()
+               .getApplicationContext().checkCallingOrSelfPermission(permission);
+        }
+
         private static void registerMonitorCallbackAsUser(
                 @NonNull ContentResolver resolver, @UserIdInt int userHandle,
                 @NonNull RemoteCallback callback) {
@@ -18245,6 +18324,10 @@
             Preconditions.checkNotNull(namespace);
             return namespace + "/";
         }
+
+        private static ContentResolver getContentResolver() {
+            return ActivityThread.currentApplication().getContentResolver();
+        }
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/TextAppearanceInfo.java b/core/java/android/view/inputmethod/TextAppearanceInfo.java
index 1df4fc5..500c41c 100644
--- a/core/java/android/view/inputmethod/TextAppearanceInfo.java
+++ b/core/java/android/view/inputmethod/TextAppearanceInfo.java
@@ -16,12 +16,13 @@
 
 package android.view.inputmethod;
 
+import static android.graphics.Typeface.NORMAL;
+
 import android.annotation.ColorInt;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Px;
-import android.content.res.ColorStateList;
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.graphics.fonts.FontStyle;
@@ -30,7 +31,7 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.InputFilter;
+import android.text.method.TransformationMethod;
 import android.widget.TextView;
 
 import java.util.Objects;
@@ -38,7 +39,6 @@
 /**
  * Information about text appearance in an editor, passed through
  * {@link CursorAnchorInfo} for use by {@link InputMethodService}.
- *
  * @see TextView
  * @see Paint
  * @see CursorAnchorInfo.Builder#setTextAppearanceInfo(TextAppearanceInfo)
@@ -46,12 +46,12 @@
  */
 public final class TextAppearanceInfo implements Parcelable {
     /**
-     * The text size (in pixels) for current {@link TextView}.
+     * The text size (in pixels) for current editor.
      */
     private final @Px float mTextSize;
 
     /**
-     * The LocaleList of the text.
+     * The {@link LocaleList} of the text.
      */
     @NonNull private final LocaleList mTextLocales;
 
@@ -64,7 +64,8 @@
     /**
      * The weight of the text.
      */
-    private final @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int mTextFontWeight;
+    @IntRange(from = FontStyle.FONT_WEIGHT_UNSPECIFIED, to = FontStyle.FONT_WEIGHT_MAX)
+    private final int mTextFontWeight;
 
     /**
      * The style (normal, bold, italic, bold|italic) of the text, see {@link Typeface}.
@@ -72,8 +73,7 @@
     private final @Typeface.Style int mTextStyle;
 
     /**
-     * Whether the transformation method applied to the current {@link TextView} is set to
-     * ALL CAPS.
+     * Whether the transformation method applied to the current editor is set to all caps.
      */
     private final boolean mAllCaps;
 
@@ -93,6 +93,11 @@
     private final @Px float mShadowRadius;
 
     /**
+     * The shadow color of the text shadow.
+     */
+    private final @ColorInt int mShadowColor;
+
+    /**
      * The elegant text height, especially for less compacted complex script text.
      */
     private final boolean mElegantTextHeight;
@@ -135,67 +140,46 @@
     /**
      * The color of the text selection highlight.
      */
-    private final @ColorInt int mTextColorHighlight;
+    private final @ColorInt int mHighlightTextColor;
 
     /**
-     * The current text color.
+     * The current text color of the editor.
      */
     private final @ColorInt int mTextColor;
 
     /**
-     * The current color of the hint text.
+     *  The current color of the hint text.
      */
-    private final @ColorInt int mTextColorHint;
+    private final @ColorInt int mHintTextColor;
 
     /**
-     * The text color for links.
+     * The text color used to paint the links in the editor.
      */
-    @Nullable private final ColorStateList mTextColorLink;
+    private final @ColorInt int mLinkTextColor;
 
-    /**
-     * The max length of text.
-     */
-    private final int mMaxLength;
-
-
-    public TextAppearanceInfo(@NonNull TextView textView) {
-        mTextSize = textView.getTextSize();
-        mTextLocales = textView.getTextLocales();
-        Typeface typeface = textView.getPaint().getTypeface();
-        String systemFontFamilyName = null;
-        int textFontWeight = -1;
-        if (typeface != null) {
-            systemFontFamilyName = typeface.getSystemFontFamilyName();
-            textFontWeight = typeface.getWeight();
-        }
-        mSystemFontFamilyName = systemFontFamilyName;
-        mTextFontWeight = textFontWeight;
-        mTextStyle = textView.getTypefaceStyle();
-        mAllCaps = textView.isAllCaps();
-        mShadowRadius = textView.getShadowRadius();
-        mShadowDx = textView.getShadowDx();
-        mShadowDy = textView.getShadowDy();
-        mElegantTextHeight = textView.isElegantTextHeight();
-        mFallbackLineSpacing = textView.isFallbackLineSpacing();
-        mLetterSpacing = textView.getLetterSpacing();
-        mFontFeatureSettings = textView.getFontFeatureSettings();
-        mFontVariationSettings = textView.getFontVariationSettings();
-        mLineBreakStyle = textView.getLineBreakStyle();
-        mLineBreakWordStyle = textView.getLineBreakWordStyle();
-        mTextScaleX = textView.getTextScaleX();
-        mTextColorHighlight = textView.getHighlightColor();
-        mTextColor = textView.getCurrentTextColor();
-        mTextColorHint = textView.getCurrentHintTextColor();
-        mTextColorLink = textView.getLinkTextColors();
-        int maxLength = -1;
-        for (InputFilter filter: textView.getFilters()) {
-            if (filter instanceof InputFilter.LengthFilter) {
-                maxLength = ((InputFilter.LengthFilter) filter).getMax();
-                // There is at most one LengthFilter.
-                break;
-            }
-        }
-        mMaxLength = maxLength;
+    private TextAppearanceInfo(@NonNull final TextAppearanceInfo.Builder builder) {
+        mTextSize = builder.mTextSize;
+        mTextLocales = builder.mTextLocales;
+        mSystemFontFamilyName = builder.mSystemFontFamilyName;
+        mTextFontWeight = builder.mTextFontWeight;
+        mTextStyle = builder.mTextStyle;
+        mAllCaps = builder.mAllCaps;
+        mShadowDx = builder.mShadowDx;
+        mShadowDy = builder.mShadowDy;
+        mShadowRadius = builder.mShadowRadius;
+        mShadowColor = builder.mShadowColor;
+        mElegantTextHeight = builder.mElegantTextHeight;
+        mFallbackLineSpacing = builder.mFallbackLineSpacing;
+        mLetterSpacing = builder.mLetterSpacing;
+        mFontFeatureSettings = builder.mFontFeatureSettings;
+        mFontVariationSettings = builder.mFontVariationSettings;
+        mLineBreakStyle = builder.mLineBreakStyle;
+        mLineBreakWordStyle = builder.mLineBreakWordStyle;
+        mTextScaleX = builder.mTextScaleX;
+        mHighlightTextColor = builder.mHighlightTextColor;
+        mTextColor = builder.mTextColor;
+        mHintTextColor = builder.mHintTextColor;
+        mLinkTextColor = builder.mLinkTextColor;
     }
 
     @Override
@@ -214,6 +198,7 @@
         dest.writeFloat(mShadowDx);
         dest.writeFloat(mShadowDy);
         dest.writeFloat(mShadowRadius);
+        dest.writeInt(mShadowColor);
         dest.writeBoolean(mElegantTextHeight);
         dest.writeBoolean(mFallbackLineSpacing);
         dest.writeFloat(mLetterSpacing);
@@ -222,14 +207,13 @@
         dest.writeInt(mLineBreakStyle);
         dest.writeInt(mLineBreakWordStyle);
         dest.writeFloat(mTextScaleX);
-        dest.writeInt(mTextColorHighlight);
+        dest.writeInt(mHighlightTextColor);
         dest.writeInt(mTextColor);
-        dest.writeInt(mTextColorHint);
-        dest.writeTypedObject(mTextColorLink, flags);
-        dest.writeInt(mMaxLength);
+        dest.writeInt(mHintTextColor);
+        dest.writeInt(mLinkTextColor);
     }
 
-    private TextAppearanceInfo(@NonNull Parcel in) {
+    TextAppearanceInfo(@NonNull Parcel in) {
         mTextSize = in.readFloat();
         mTextLocales = LocaleList.CREATOR.createFromParcel(in);
         mAllCaps = in.readBoolean();
@@ -239,6 +223,7 @@
         mShadowDx = in.readFloat();
         mShadowDy = in.readFloat();
         mShadowRadius = in.readFloat();
+        mShadowColor = in.readInt();
         mElegantTextHeight = in.readBoolean();
         mFallbackLineSpacing = in.readBoolean();
         mLetterSpacing = in.readFloat();
@@ -247,11 +232,10 @@
         mLineBreakStyle = in.readInt();
         mLineBreakWordStyle = in.readInt();
         mTextScaleX = in.readFloat();
-        mTextColorHighlight = in.readInt();
+        mHighlightTextColor = in.readInt();
         mTextColor = in.readInt();
-        mTextColorHint = in.readInt();
-        mTextColorLink = in.readTypedObject(ColorStateList.CREATOR);
-        mMaxLength = in.readInt();
+        mHintTextColor = in.readInt();
+        mLinkTextColor = in.readInt();
     }
 
     @NonNull
@@ -268,14 +252,14 @@
     };
 
     /**
-     * Returns the text size (in pixels) for current {@link TextView}.
+     * Returns the text size (in pixels) for current editor.
      */
     public @Px float getTextSize() {
         return mTextSize;
     }
 
     /**
-     * Returns the LocaleList of the text.
+     * Returns the {@link LocaleList} of the text.
      */
     @NonNull
     public LocaleList getTextLocales() {
@@ -286,31 +270,38 @@
      * Returns the font family name if the {@link Typeface} of the text is created from a
      * system font family. Returns null if no {@link Typeface} is specified, or it is not created
      * from a system font family.
+     *
+     * @see Typeface#getSystemFontFamilyName()
      */
     @Nullable
-    public String getFontFamilyName() {
+    public String getSystemFontFamilyName() {
         return mSystemFontFamilyName;
     }
 
     /**
-     * Returns the weight of the text. Returns -1 when no {@link Typeface} is specified.
+     * Returns the weight of the text, or {@code FontStyle#FONT_WEIGHT_UNSPECIFIED}
+     * when no {@link Typeface} is specified.
      */
-    public @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int getTextFontWeight() {
+    @IntRange(from = FontStyle.FONT_WEIGHT_UNSPECIFIED, to = FontStyle.FONT_WEIGHT_MAX)
+    public int getTextFontWeight() {
         return mTextFontWeight;
     }
 
     /**
      * Returns the style (normal, bold, italic, bold|italic) of the text. Returns
-     * {@link Typeface#NORMAL} when no {@link Typeface} is specified. See {@link Typeface} for
-     * more information.
+     * {@link Typeface#NORMAL} when no {@link Typeface} is specified.
+     *
+     * @see Typeface
      */
     public @Typeface.Style int getTextStyle() {
         return mTextStyle;
     }
 
     /**
-     * Returns whether the transformation method applied to the current {@link TextView} is set to
-     * ALL CAPS.
+     * Returns whether the transformation method applied to the current editor is set to all caps.
+     *
+     * @see TextView#setAllCaps(boolean)
+     * @see TextView#setTransformationMethod(TransformationMethod)
      */
     public boolean isAllCaps() {
         return mAllCaps;
@@ -318,6 +309,8 @@
 
     /**
      * Returns the horizontal offset (in pixels) of the text shadow.
+     *
+     * @see Paint#setShadowLayer(float, float, float, int)
      */
     public @Px float getShadowDx() {
         return mShadowDx;
@@ -325,6 +318,8 @@
 
     /**
      * Returns the vertical offset (in pixels) of the text shadow.
+     *
+     * @see Paint#setShadowLayer(float, float, float, int)
      */
     public @Px float getShadowDy() {
         return mShadowDy;
@@ -332,15 +327,28 @@
 
     /**
      * Returns the blur radius (in pixels) of the text shadow.
+     *
+     * @see Paint#setShadowLayer(float, float, float, int)
      */
     public @Px float getShadowRadius() {
         return mShadowRadius;
     }
 
     /**
+     * Returns the color of the text shadow.
+     *
+     * @see Paint#setShadowLayer(float, float, float, int)
+     */
+    public @ColorInt int getShadowColor() {
+        return mShadowColor;
+    }
+
+    /**
      * Returns {@code true} if the elegant height metrics flag is set. This setting selects font
      * variants that have not been compacted to fit Latin-based vertical metrics, and also increases
      * top and bottom bounds to provide more space.
+     *
+     * @see Paint#isElegantTextHeight()
      */
     public boolean isElegantTextHeight() {
         return mElegantTextHeight;
@@ -411,13 +419,17 @@
 
     /**
      * Returns the color of the text selection highlight.
+     *
+     * @see TextView#getHighlightColor()
      */
-    public @ColorInt int getTextColorHighlight() {
-        return mTextColorHighlight;
+    public @ColorInt int getHighlightTextColor() {
+        return mHighlightTextColor;
     }
 
     /**
-     * Returns the current text color.
+     * Returns the current text color of the editor.
+     *
+     * @see TextView#getCurrentTextColor()
      */
     public @ColorInt int getTextColor() {
         return mTextColor;
@@ -425,27 +437,22 @@
 
     /**
      * Returns the current color of the hint text.
+     *
+     * @see TextView#getCurrentHintTextColor()
      */
-    public @ColorInt int getTextColorHint() {
-        return mTextColorHint;
+    public @ColorInt int getHintTextColor() {
+        return mHintTextColor;
     }
 
     /**
-     * Returns the text color for links.
+     * Returns the text color used to paint the links in the editor.
+     *
+     * @see TextView#getLinkTextColors()
      */
-    @Nullable
-    public ColorStateList getTextColorLink() {
-        return mTextColorLink;
+    public @ColorInt int getLinkTextColor() {
+        return mLinkTextColor;
     }
 
-    /**
-     * Returns the max length of text, which is used to set an input filter to constrain the text
-     * length to the specified number. Returns -1 when there is no {@link InputFilter.LengthFilter}
-     * in the Editor.
-     */
-    public int getMaxLength() {
-        return mMaxLength;
-    }
 
     @Override
     public boolean equals(Object o) {
@@ -456,27 +463,29 @@
                 && mTextFontWeight == that.mTextFontWeight && mTextStyle == that.mTextStyle
                 && mAllCaps == that.mAllCaps && Float.compare(that.mShadowDx, mShadowDx) == 0
                 && Float.compare(that.mShadowDy, mShadowDy) == 0 && Float.compare(
-                that.mShadowRadius, mShadowRadius) == 0 && mMaxLength == that.mMaxLength
+                that.mShadowRadius, mShadowRadius) == 0 && that.mShadowColor == mShadowColor
                 && mElegantTextHeight == that.mElegantTextHeight
                 && mFallbackLineSpacing == that.mFallbackLineSpacing && Float.compare(
                 that.mLetterSpacing, mLetterSpacing) == 0 && mLineBreakStyle == that.mLineBreakStyle
                 && mLineBreakWordStyle == that.mLineBreakWordStyle
-                && mTextColorHighlight == that.mTextColorHighlight && mTextColor == that.mTextColor
-                && mTextColorLink.getDefaultColor() == that.mTextColorLink.getDefaultColor()
-                && mTextColorHint == that.mTextColorHint && Objects.equals(
-                mTextLocales, that.mTextLocales) && Objects.equals(mSystemFontFamilyName,
-                that.mSystemFontFamilyName) && Objects.equals(mFontFeatureSettings,
-                that.mFontFeatureSettings) && Objects.equals(mFontVariationSettings,
-                that.mFontVariationSettings) && Float.compare(that.mTextScaleX, mTextScaleX) == 0;
+                && mHighlightTextColor == that.mHighlightTextColor
+                && mTextColor == that.mTextColor
+                && mLinkTextColor == that.mLinkTextColor
+                && mHintTextColor == that.mHintTextColor
+                && Objects.equals(mTextLocales, that.mTextLocales)
+                && Objects.equals(mSystemFontFamilyName, that.mSystemFontFamilyName)
+                && Objects.equals(mFontFeatureSettings, that.mFontFeatureSettings)
+                && Objects.equals(mFontVariationSettings, that.mFontVariationSettings)
+                && Float.compare(that.mTextScaleX, mTextScaleX) == 0;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mTextSize, mTextLocales, mSystemFontFamilyName, mTextFontWeight,
-                mTextStyle, mAllCaps, mShadowDx, mShadowDy, mShadowRadius, mElegantTextHeight,
-                mFallbackLineSpacing, mLetterSpacing, mFontFeatureSettings, mFontVariationSettings,
-                mLineBreakStyle, mLineBreakWordStyle, mTextScaleX, mTextColorHighlight, mTextColor,
-                mTextColorHint, mTextColorLink, mMaxLength);
+                mTextStyle, mAllCaps, mShadowDx, mShadowDy, mShadowRadius, mShadowColor,
+                mElegantTextHeight, mFallbackLineSpacing, mLetterSpacing, mFontFeatureSettings,
+                mFontVariationSettings, mLineBreakStyle, mLineBreakWordStyle, mTextScaleX,
+                mHighlightTextColor, mTextColor, mHintTextColor, mLinkTextColor);
     }
 
     @Override
@@ -491,6 +500,7 @@
                 + ", mShadowDx=" + mShadowDx
                 + ", mShadowDy=" + mShadowDy
                 + ", mShadowRadius=" + mShadowRadius
+                + ", mShadowColor=" + mShadowColor
                 + ", mElegantTextHeight=" + mElegantTextHeight
                 + ", mFallbackLineSpacing=" + mFallbackLineSpacing
                 + ", mLetterSpacing=" + mLetterSpacing
@@ -499,11 +509,290 @@
                 + ", mLineBreakStyle=" + mLineBreakStyle
                 + ", mLineBreakWordStyle=" + mLineBreakWordStyle
                 + ", mTextScaleX=" + mTextScaleX
-                + ", mTextColorHighlight=" + mTextColorHighlight
+                + ", mHighlightTextColor=" + mHighlightTextColor
                 + ", mTextColor=" + mTextColor
-                + ", mTextColorHint=" + mTextColorHint
-                + ", mTextColorLink=" + mTextColorLink
-                + ", mMaxLength=" + mMaxLength
+                + ", mHintTextColor=" + mHintTextColor
+                + ", mLinkTextColor=" + mLinkTextColor
                 + '}';
     }
+
+    /**
+     * Builder for {@link TextAppearanceInfo}.
+     */
+    public static final class Builder {
+        private @Px float mTextSize = -1;
+        private @NonNull LocaleList mTextLocales = LocaleList.getAdjustedDefault();
+        @Nullable private String mSystemFontFamilyName = null;
+        @IntRange(from = FontStyle.FONT_WEIGHT_UNSPECIFIED, to = FontStyle.FONT_WEIGHT_MAX)
+        private int mTextFontWeight = FontStyle.FONT_WEIGHT_UNSPECIFIED;
+        private @Typeface.Style int mTextStyle = NORMAL;
+        private boolean mAllCaps = false;
+        private @Px float mShadowDx = 0;
+        private @Px float mShadowDy = 0;
+        private @Px float mShadowRadius = 0;
+        private @ColorInt int mShadowColor = 0;
+        private boolean mElegantTextHeight = false;
+        private boolean mFallbackLineSpacing = false;
+        private float mLetterSpacing = 0;
+        @Nullable private String mFontFeatureSettings = null;
+        @Nullable private String mFontVariationSettings = null;
+        @LineBreakConfig.LineBreakStyle
+        private int mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_NONE;
+        @LineBreakConfig.LineBreakWordStyle
+        private int mLineBreakWordStyle = LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+        private float mTextScaleX = 1;
+        private @ColorInt int mHighlightTextColor = 0;
+        private @ColorInt int mTextColor = 0;
+        private @ColorInt int mHintTextColor = 0;
+        private @ColorInt int mLinkTextColor = 0;
+
+        /**
+         * Set the text size (in pixels) obtained from the current editor.
+         */
+        @NonNull
+        public Builder setTextSize(@Px float textSize) {
+            mTextSize = textSize;
+            return this;
+        }
+
+        /**
+         * Set the {@link LocaleList} of the text.
+         */
+        @NonNull
+        public Builder setTextLocales(@NonNull LocaleList textLocales) {
+            mTextLocales = textLocales;
+            return this;
+        }
+
+        /**
+         * Set the system font family name if the {@link Typeface} of the text is created from a
+         * system font family.
+         *
+         * @see Typeface#getSystemFontFamilyName()
+         */
+        @NonNull
+        public Builder setSystemFontFamilyName(@Nullable String systemFontFamilyName) {
+            mSystemFontFamilyName = systemFontFamilyName;
+            return this;
+        }
+
+        /**
+         * Set the weight of the text.
+         */
+        @NonNull
+        public Builder setTextFontWeight(
+                @IntRange(from = FontStyle.FONT_WEIGHT_UNSPECIFIED,
+                        to = FontStyle.FONT_WEIGHT_MAX) int textFontWeight) {
+            mTextFontWeight = textFontWeight;
+            return this;
+        }
+
+        /**
+         * Set the style (normal, bold, italic, bold|italic) of the text.
+         *
+         * @see Typeface
+         */
+        @NonNull
+        public Builder setTextStyle(@Typeface.Style int textStyle) {
+            mTextStyle = textStyle;
+            return this;
+        }
+
+        /**
+         * Set whether the transformation method applied to the current editor  is set to all caps.
+         *
+         * @see TextView#setAllCaps(boolean)
+         * @see TextView#setTransformationMethod(TransformationMethod)
+         */
+        @NonNull
+        public Builder setAllCaps(boolean allCaps) {
+            mAllCaps = allCaps;
+            return this;
+        }
+
+        /**
+         * Set the horizontal offset (in pixels) of the text shadow.
+         *
+         * @see Paint#setShadowLayer(float, float, float, int)
+         */
+        @NonNull
+        public Builder setShadowDx(@Px float shadowDx) {
+            mShadowDx = shadowDx;
+            return this;
+        }
+
+        /**
+         * Set the vertical offset (in pixels) of the text shadow.
+         *
+         * @see Paint#setShadowLayer(float, float, float, int)
+         */
+        @NonNull
+        public Builder setShadowDy(@Px float shadowDy) {
+            mShadowDy = shadowDy;
+            return this;
+        }
+
+        /**
+         * Set the blur radius (in pixels) of the text shadow.
+         *
+         * @see Paint#setShadowLayer(float, float, float, int)
+         */
+        @NonNull
+        public Builder setShadowRadius(@Px float shadowRadius) {
+            mShadowRadius = shadowRadius;
+            return this;
+        }
+
+        /**
+         * Set the color of the text shadow.
+         *
+         * @see Paint#setShadowLayer(float, float, float, int)
+         */
+        @NonNull
+        public Builder setShadowColor(@ColorInt int shadowColor) {
+            mShadowColor = shadowColor;
+            return this;
+        }
+
+        /**
+         * Set the elegant height metrics flag. This setting selects font variants that
+         * have not been compacted to fit Latin-based vertical metrics, and also increases
+         * top and bottom bounds to provide more space.
+         *
+         * @see Paint#isElegantTextHeight()
+         */
+        @NonNull
+        public Builder setElegantTextHeight(boolean elegantTextHeight) {
+            mElegantTextHeight = elegantTextHeight;
+            return this;
+        }
+
+        /**
+         * Set whether to expand linespacing based on fallback fonts.
+         *
+         * @see TextView#setFallbackLineSpacing(boolean)
+         */
+        @NonNull
+        public Builder setFallbackLineSpacing(boolean fallbackLineSpacing) {
+            mFallbackLineSpacing = fallbackLineSpacing;
+            return this;
+        }
+
+        /**
+         * Set the text letter-spacing, which determines the spacing between characters.
+         * The value is in 'EM' units. Normally, this value is 0.0.
+         */
+        @NonNull
+        public Builder setLetterSpacing(float letterSpacing) {
+            mLetterSpacing = letterSpacing;
+            return this;
+        }
+
+        /**
+         * Set the font feature settings.
+         *
+         * @see Paint#getFontFeatureSettings()
+         */
+        @NonNull
+        public Builder setFontFeatureSettings(@Nullable String fontFeatureSettings) {
+            mFontFeatureSettings = fontFeatureSettings;
+            return this;
+        }
+
+        /**
+         * Set the font variation settings. Returns null if no variation is specified.
+         *
+         * @see Paint#getFontVariationSettings()
+         */
+        @NonNull
+        public Builder setFontVariationSettings(@Nullable String fontVariationSettings) {
+            mFontVariationSettings = fontVariationSettings;
+            return this;
+        }
+
+        /**
+         * Set the line-break strategies for text wrapping.
+         *
+         * @see TextView#setLineBreakStyle(int)
+         */
+        @NonNull
+        public Builder setLineBreakStyle(@LineBreakConfig.LineBreakStyle int lineBreakStyle) {
+            mLineBreakStyle = lineBreakStyle;
+            return this;
+        }
+
+        /**
+         * Set the line-break word strategies for text wrapping.
+         *
+         * @see TextView#setLineBreakWordStyle(int)
+         */
+        @NonNull
+        public Builder setLineBreakWordStyle(
+                @LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
+            mLineBreakWordStyle = lineBreakWordStyle;
+            return this;
+        }
+
+        /**
+         * Set the extent by which text should be stretched horizontally.
+         */
+        @NonNull
+        public Builder setTextScaleX(float textScaleX) {
+            mTextScaleX = textScaleX;
+            return this;
+        }
+
+        /**
+         * Set the color of the text selection highlight.
+         *
+         * @see TextView#getHighlightColor()
+         */
+        @NonNull
+        public Builder setHighlightTextColor(@ColorInt int highlightTextColor) {
+            mHighlightTextColor = highlightTextColor;
+            return this;
+        }
+
+        /**
+         * Set the current text color of the editor.
+         *
+         * @see TextView#getCurrentTextColor()
+         */
+        @NonNull
+        public Builder setTextColor(@ColorInt int textColor) {
+            mTextColor = textColor;
+            return this;
+        }
+
+        /**
+         * Set the current color of the hint text.
+         *
+         * @see TextView#getCurrentHintTextColor()
+         */
+        @NonNull
+        public Builder setHintTextColor(@ColorInt int hintTextColor) {
+            mHintTextColor = hintTextColor;
+            return this;
+        }
+
+        /**
+         * Set the text color used to paint the links in the editor.
+         *
+         * @see TextView#getLinkTextColors()
+         */
+        @NonNull
+        public Builder setLinkTextColor(@ColorInt int linkTextColor) {
+            mLinkTextColor = linkTextColor;
+            return this;
+        }
+
+        /**
+         * Returns {@link TextAppearanceInfo} using parameters in this
+         * {@link TextAppearanceInfo.Builder}.
+         */
+        @NonNull
+        public TextAppearanceInfo build() {
+            return new TextAppearanceInfo(this);
+        }
+    }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 56524a2..5740f86 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -21,6 +21,7 @@
 
 import android.R;
 import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +38,7 @@
 import android.content.UndoOwner;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -49,8 +51,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.RenderNode;
+import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.fonts.FontStyle;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -4762,8 +4766,41 @@
             }
 
             if (includeTextAppearance) {
-                TextAppearanceInfo textAppearanceInfo = new TextAppearanceInfo(mTextView);
-                builder.setTextAppearanceInfo(textAppearanceInfo);
+                Typeface typeface = mTextView.getPaint().getTypeface();
+                String systemFontFamilyName = null;
+                int textFontWeight = FontStyle.FONT_WEIGHT_UNSPECIFIED;
+                if (typeface != null) {
+                    systemFontFamilyName = typeface.getSystemFontFamilyName();
+                    textFontWeight = typeface.getWeight();
+                }
+                ColorStateList linkTextColors = mTextView.getLinkTextColors();
+                @ColorInt int linkTextColor = linkTextColors != null
+                        ? linkTextColors.getDefaultColor() : 0;
+
+                TextAppearanceInfo.Builder appearanceBuilder = new TextAppearanceInfo.Builder();
+                appearanceBuilder.setTextSize(mTextView.getTextSize())
+                        .setTextLocales(mTextView.getTextLocales())
+                        .setSystemFontFamilyName(systemFontFamilyName)
+                        .setTextFontWeight(textFontWeight)
+                        .setTextStyle(mTextView.getTypefaceStyle())
+                        .setAllCaps(mTextView.isAllCaps())
+                        .setShadowDx(mTextView.getShadowDx())
+                        .setShadowDy(mTextView.getShadowDy())
+                        .setShadowRadius(mTextView.getShadowRadius())
+                        .setShadowColor(mTextView.getShadowColor())
+                        .setElegantTextHeight(mTextView.isElegantTextHeight())
+                        .setFallbackLineSpacing(mTextView.isFallbackLineSpacing())
+                        .setLetterSpacing(mTextView.getLetterSpacing())
+                        .setFontFeatureSettings(mTextView.getFontFeatureSettings())
+                        .setFontVariationSettings(mTextView.getFontVariationSettings())
+                        .setLineBreakStyle(mTextView.getLineBreakStyle())
+                        .setLineBreakWordStyle(mTextView.getLineBreakWordStyle())
+                        .setTextScaleX(mTextView.getTextScaleX())
+                        .setHighlightTextColor(mTextView.getHighlightColor())
+                        .setTextColor(mTextView.getCurrentTextColor())
+                        .setHintTextColor(mTextView.getCurrentHintTextColor())
+                        .setLinkTextColor(linkTextColor);
+                builder.setTextAppearanceInfo(appearanceBuilder.build());
             }
             imm.updateCursorAnchorInfo(mTextView, builder.build());
 
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 09f0991..614f962 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -91,7 +91,6 @@
 import android.annotation.NonNull;
 import android.annotation.UiThread;
 import android.annotation.WorkerThread;
-import android.app.ActivityThread;
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
@@ -417,7 +416,6 @@
     public InteractionJankMonitor(@NonNull HandlerThread worker) {
         // Check permission early.
         DeviceConfig.enforceReadPermission(
-            ActivityThread.currentApplication().getApplicationContext(),
             DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR);
 
         mRunningTrackers = new SparseArray<>();
diff --git a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
index 37cf470..4d5b0d2 100644
--- a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java
@@ -46,6 +46,7 @@
 public class BackupAgentTest {
     // An arbitrary user.
     private static final UserHandle USER_HANDLE = new UserHandle(15);
+    private static final String DATA_TYPE_BACKED_UP = "test data type";
 
     @Mock FullBackup.BackupScheme mBackupScheme;
 
@@ -73,6 +74,42 @@
         assertThat(rules).isEqualTo(expectedRules);
     }
 
+    @Test
+    public void getBackupRestoreEventLogger_beforeOnCreate_isNull() {
+        BackupAgent agent = new TestFullBackupAgent();
+
+        assertThat(agent.getBackupRestoreEventLogger()).isNull();
+    }
+
+    @Test
+    public void getBackupRestoreEventLogger_afterOnCreateForBackup_initializedForBackup() {
+        BackupAgent agent = new TestFullBackupAgent();
+        agent.onCreate(USER_HANDLE, OperationType.BACKUP); // TODO: pass in new operation type
+
+        assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo(1);
+    }
+
+    @Test
+    public void getBackupRestoreEventLogger_afterOnCreateForRestore_initializedForRestore() {
+        BackupAgent agent = new TestFullBackupAgent();
+        agent.onCreate(USER_HANDLE, OperationType.BACKUP); // TODO: pass in new operation type
+
+        assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo(1);
+    }
+
+    @Test
+    public void getBackupRestoreEventLogger_afterBackup_containsLogsLoggedByAgent()
+            throws Exception {
+        BackupAgent agent = new TestFullBackupAgent();
+        agent.onCreate(USER_HANDLE, OperationType.BACKUP); // TODO: pass in new operation type
+
+        // TestFullBackupAgent logs DATA_TYPE_BACKED_UP when onFullBackup is called.
+        agent.onFullBackup(new FullBackupDataOutput(/* quota = */ 0));
+
+        assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get(0).getDataType())
+                .isEqualTo(DATA_TYPE_BACKED_UP);
+    }
+
     private BackupAgent getAgentForOperationType(@OperationType int operationType) {
         BackupAgent agent = new TestFullBackupAgent();
         agent.onCreate(USER_HANDLE, operationType);
@@ -88,6 +125,11 @@
         }
 
         @Override
+        public void onFullBackup(FullBackupDataOutput data) {
+            getBackupRestoreEventLogger().logItemsBackedUp(DATA_TYPE_BACKED_UP, 1);
+        }
+
+        @Override
         public void onRestore(BackupDataInput data, int appVersionCode,
                 ParcelFileDescriptor newState) throws IOException {
             // Left empty as this is a full backup agent.
diff --git a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
index bbd2ef3..b9fdc6d 100644
--- a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java
@@ -25,6 +25,7 @@
 
 import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType;
 import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
+import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -35,6 +36,7 @@
 
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
@@ -236,10 +238,10 @@
         mLogger.logItemsBackupFailed(DATA_TYPE_1, firstCount, ERROR_1);
         mLogger.logItemsBackupFailed(DATA_TYPE_1, secondCount, ERROR_2);
 
-        int firstErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
-                .getErrors().get(ERROR_1);
-        int secondErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
-                .getErrors().get(ERROR_2);
+        int firstErrorTypeCount =
+                getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_1);
+        int secondErrorTypeCount =
+                getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_2);
         assertThat(firstErrorTypeCount).isEqualTo(firstCount);
         assertThat(secondErrorTypeCount).isEqualTo(secondCount);
     }
@@ -253,16 +255,54 @@
         mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstCount, ERROR_1);
         mLogger.logItemsRestoreFailed(DATA_TYPE_1, secondCount, ERROR_2);
 
-        int firstErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
-                .getErrors().get(ERROR_1);
-        int secondErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1)
-                .getErrors().get(ERROR_2);
+        int firstErrorTypeCount =
+                getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_1);
+        int secondErrorTypeCount =
+                getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_2);
         assertThat(firstErrorTypeCount).isEqualTo(firstCount);
         assertThat(secondErrorTypeCount).isEqualTo(secondCount);
     }
 
-    private static DataTypeResult getResultForDataType(BackupRestoreEventLogger logger,
-            @BackupRestoreDataType String dataType) {
+    @Test
+    public void testGetLoggingResults_resultsParceledAndUnparceled_recreatedCorrectly() {
+        mLogger = new BackupRestoreEventLogger(RESTORE);
+        int firstTypeSuccessCount = 1;
+        int firstTypeErrorOneCount = 2;
+        int firstTypeErrorTwoCount = 3;
+        mLogger.logItemsRestored(DATA_TYPE_1, firstTypeSuccessCount);
+        mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstTypeErrorOneCount, ERROR_1);
+        mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstTypeErrorTwoCount, ERROR_2);
+        mLogger.logRestoreMetadata(DATA_TYPE_1, METADATA_1);
+        int secondTypeSuccessCount = 4;
+        int secondTypeErrorOneCount = 5;
+        mLogger.logItemsRestored(DATA_TYPE_2, secondTypeSuccessCount);
+        mLogger.logItemsRestoreFailed(DATA_TYPE_2, secondTypeErrorOneCount, ERROR_1);
+
+        List<DataTypeResult> resultsList = mLogger.getLoggingResults();
+        Parcel parcel = Parcel.obtain();
+
+        parcel.writeParcelableList(resultsList, /* flags= */ 0);
+
+        parcel.setDataPosition(0);
+        List<DataTypeResult> recreatedList = new ArrayList<>();
+        parcel.readParcelableList(
+                recreatedList, DataTypeResult.class.getClassLoader(), DataTypeResult.class);
+
+        assertThat(recreatedList.get(0).getDataType()).isEqualTo(DATA_TYPE_1);
+        assertThat(recreatedList.get(0).getSuccessCount()).isEqualTo(firstTypeSuccessCount);
+        assertThat(recreatedList.get(0).getFailCount())
+                .isEqualTo(firstTypeErrorOneCount + firstTypeErrorTwoCount);
+        assertThat(recreatedList.get(0).getErrors().get(ERROR_1)).isEqualTo(firstTypeErrorOneCount);
+        assertThat(recreatedList.get(0).getErrors().get(ERROR_2)).isEqualTo(firstTypeErrorTwoCount);
+        assertThat(recreatedList.get(1).getDataType()).isEqualTo(DATA_TYPE_2);
+        assertThat(recreatedList.get(1).getSuccessCount()).isEqualTo(secondTypeSuccessCount);
+        assertThat(recreatedList.get(1).getFailCount()).isEqualTo(secondTypeErrorOneCount);
+        assertThat(recreatedList.get(1).getErrors().get(ERROR_1))
+                .isEqualTo(secondTypeErrorOneCount);
+    }
+
+    private static DataTypeResult getResultForDataType(
+            BackupRestoreEventLogger logger, @BackupRestoreDataType String dataType) {
         Optional<DataTypeResult> result = getResultForDataTypeIfPresent(logger, dataType);
         if (result.isEmpty()) {
             fail("Failed to find result for data type: " + dataType);
@@ -273,8 +313,9 @@
     private static Optional<DataTypeResult> getResultForDataTypeIfPresent(
             BackupRestoreEventLogger logger, @BackupRestoreDataType String dataType) {
         List<DataTypeResult> resultList = logger.getLoggingResults();
-        return resultList.stream().filter(
-                dataTypeResult -> dataTypeResult.getDataType().equals(dataType)).findAny();
+        return resultList.stream()
+                .filter(dataTypeResult -> dataTypeResult.getDataType().equals(dataType))
+                .findAny();
     }
 
     private byte[] getMetaDataHash(String metaData) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 950ee21..9583a59 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -668,6 +668,12 @@
      * @param bluetoothProfile the Bluetooth profile
      */
     public void onActiveDeviceChanged(boolean isActive, int bluetoothProfile) {
+        if (BluetoothUtils.D) {
+            Log.d(TAG, "onActiveDeviceChanged: "
+                    + "profile " + BluetoothProfile.getProfileName(bluetoothProfile)
+                    + ", device " + mDevice.getAnonymizedAddress()
+                    + ", isActive " + isActive);
+        }
         boolean changed = false;
         switch (bluetoothProfile) {
         case BluetoothProfile.A2DP:
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index fa96a2f..0b7b2f9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -112,10 +112,10 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.providers.settings.SettingsState.Setting;
 
-import libcore.util.HexEncoding;
-
 import com.google.android.collect.Sets;
 
+import libcore.util.HexEncoding;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -1144,7 +1144,7 @@
             Slog.v(LOG_TAG, "getConfigSetting(" + name + ")");
         }
 
-        DeviceConfig.enforceReadPermission(getContext(), /*namespace=*/name.split("/")[0]);
+        DeviceConfig.enforceReadPermission(/*namespace=*/name.split("/")[0]);
 
         // Get the value.
         synchronized (mLock) {
@@ -1317,7 +1317,7 @@
             Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
         }
 
-        DeviceConfig.enforceReadPermission(getContext(),
+        DeviceConfig.enforceReadPermission(
                 prefix != null ? prefix.split("/")[0] : null);
 
         synchronized (mLock) {
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceOnMainThreadDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceOnMainThreadDetector.kt
index 1d808ba..74e6d85 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceOnMainThreadDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceOnMainThreadDetector.kt
@@ -59,11 +59,11 @@
                     !hasWorkerThreadAnnotation(context, node.getParentOfType(UClass::class.java))
             ) {
                 context.report(
-                    ISSUE,
-                    method,
-                    context.getLocation(node),
-                    "This method should be annotated with `@WorkerThread` because " +
-                        "it calls ${method.name}",
+                    issue = ISSUE,
+                    location = context.getLocation(node),
+                    message =
+                        "This method should be annotated with `@WorkerThread` because " +
+                            "it calls ${method.name}",
                 )
             }
         }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
index 1129929..344d0a3 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
@@ -52,10 +52,9 @@
         val evaluator = context.evaluator
         if (evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
             context.report(
-                    ISSUE,
-                    method,
-                    context.getNameLocation(node),
-                    "`Context.${method.name}()` should be replaced with " +
+                    issue = ISSUE,
+                    location = context.getNameLocation(node),
+                    message = "`Context.${method.name}()` should be replaced with " +
                     "`BroadcastSender.${method.name}()`"
             )
         }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedMainThreadDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedMainThreadDetector.kt
index bab76ab..14099eb 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedMainThreadDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedMainThreadDetector.kt
@@ -38,10 +38,9 @@
     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
         if (context.evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
             context.report(
-                ISSUE,
-                method,
-                context.getNameLocation(node),
-                "Replace with injected `@Main Executor`."
+                issue = ISSUE,
+                location = context.getNameLocation(node),
+                message = "Replace with injected `@Main Executor`."
             )
         }
     }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedServiceDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedServiceDetector.kt
index b622900..aa4b2f7 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedServiceDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/NonInjectedServiceDetector.kt
@@ -44,11 +44,11 @@
                 method.containingClass?.qualifiedName == CLASS_CONTEXT
         ) {
             context.report(
-                ISSUE,
-                method,
-                context.getNameLocation(node),
-                "Use `@Inject` to get system-level service handles instead of " +
-                    "`Context.getSystemService()`"
+                issue = ISSUE,
+                location = context.getNameLocation(node),
+                message =
+                    "Use `@Inject` to get system-level service handles instead of " +
+                        "`Context.getSystemService()`"
             )
         } else if (
             evaluator.isStatic(method) &&
@@ -56,10 +56,10 @@
                 method.containingClass?.qualifiedName == "android.accounts.AccountManager"
         ) {
             context.report(
-                ISSUE,
-                method,
-                context.getNameLocation(node),
-                "Replace `AccountManager.get()` with an injected instance of `AccountManager`"
+                issue = ISSUE,
+                location = context.getNameLocation(node),
+                message =
+                    "Replace `AccountManager.get()` with an injected instance of `AccountManager`"
             )
         }
     }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt
index 4ba3afc..5840e8f 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt
@@ -38,10 +38,10 @@
     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
         if (context.evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
             context.report(
-                    ISSUE,
-                    method,
-                    context.getNameLocation(node),
-                    "Register `BroadcastReceiver` using `BroadcastDispatcher` instead of `Context`"
+                    issue = ISSUE,
+                    location = context.getNameLocation(node),
+                    message = "Register `BroadcastReceiver` using `BroadcastDispatcher` instead " +
+                    "of `Context`"
             )
         }
     }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
index 7be21a5..b15a41b 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
@@ -46,10 +46,10 @@
                 method.containingClass?.qualifiedName == "android.app.ActivityManager"
         ) {
             context.report(
-                ISSUE_SLOW_USER_ID_QUERY,
-                method,
-                context.getNameLocation(node),
-                "Use `UserTracker.getUserId()` instead of `ActivityManager.getCurrentUser()`"
+                issue = ISSUE_SLOW_USER_ID_QUERY,
+                location = context.getNameLocation(node),
+                message =
+                    "Use `UserTracker.getUserId()` instead of `ActivityManager.getCurrentUser()`"
             )
         }
         if (
@@ -58,10 +58,9 @@
                 method.containingClass?.qualifiedName == "android.os.UserManager"
         ) {
             context.report(
-                ISSUE_SLOW_USER_INFO_QUERY,
-                method,
-                context.getNameLocation(node),
-                "Use `UserTracker.getUserInfo()` instead of `UserManager.getUserInfo()`"
+                issue = ISSUE_SLOW_USER_INFO_QUERY,
+                location = context.getNameLocation(node),
+                message = "Use `UserTracker.getUserInfo()` instead of `UserManager.getUserInfo()`"
             )
         }
     }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt
index 4b9aa13..bf02589 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt
@@ -44,10 +44,9 @@
         val evaluator = context.evaluator
         if (evaluator.isMemberInClass(referenced as? PsiField, "android.graphics.Bitmap.Config")) {
             context.report(
-                    ISSUE,
-                    referenced,
-                    context.getNameLocation(reference),
-                    "Replace software bitmap with `Config.HARDWARE`"
+                    issue = ISSUE,
+                    location = context.getNameLocation(reference),
+                    message = "Replace software bitmap with `Config.HARDWARE`"
             )
         }
     }
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/StaticSettingsProviderDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/StaticSettingsProviderDetector.kt
index 1db0725..22f15bd 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/StaticSettingsProviderDetector.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/StaticSettingsProviderDetector.kt
@@ -66,10 +66,9 @@
         val subclassName = className.substring(CLASS_SETTINGS.length + 1)
 
         context.report(
-            ISSUE,
-            method,
-            context.getNameLocation(node),
-            "`@Inject` a ${subclassName}Settings instead"
+            issue = ISSUE,
+            location = context.getNameLocation(node),
+            message = "`@Inject` a ${subclassName}Settings instead"
         )
     }
 
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BindServiceOnMainThreadDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BindServiceOnMainThreadDetectorTest.kt
index c35ac61..426211e 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BindServiceOnMainThreadDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BindServiceOnMainThreadDetectorTest.kt
@@ -126,6 +126,32 @@
     }
 
     @Test
+    fun testSuppressUnbindService() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+                    import android.content.ServiceConnection;
+
+                    @SuppressLint("BindServiceOnMainThread")
+                    public class TestClass {
+                        public void unbind(Context context, ServiceConnection connection) {
+                          context.unbindService(connection);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(BindServiceOnMainThreadDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testWorkerMethod() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BroadcastSentViaContextDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BroadcastSentViaContextDetectorTest.kt
index 376acb5..30b68f7 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BroadcastSentViaContextDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/BroadcastSentViaContextDetectorTest.kt
@@ -129,6 +129,34 @@
     }
 
     @Test
+    fun testSuppressSendBroadcastInActivity() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.app.Activity;
+                    import android.os.UserHandle;
+
+                    public class TestClass {
+                        @SuppressWarnings("BroadcastSentViaContext")
+                        public void send(Activity activity) {
+                          Intent intent = new Intent(Intent.ACTION_VIEW);
+                          activity.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
+                        }
+
+                    }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(BroadcastSentViaContextDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testSendBroadcastInBroadcastSender() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedMainThreadDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedMainThreadDetectorTest.kt
index 301c338..ed3d14a 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedMainThreadDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedMainThreadDetectorTest.kt
@@ -61,6 +61,32 @@
     }
 
     @Test
+    fun testSuppressGetMainThreadHandler() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+                    import android.os.Handler;
+
+                    @SuppressWarnings("NonInjectedMainThread")
+                    public class TestClass {
+                        public void test(Context context) {
+                          Handler mainThreadHandler = context.getMainThreadHandler();
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(NonInjectedMainThreadDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testGetMainLooper() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedServiceDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedServiceDetectorTest.kt
index 0a74bfc..846129a 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedServiceDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/NonInjectedServiceDetectorTest.kt
@@ -91,6 +91,32 @@
     }
 
     @Test
+    fun testSuppressGetServiceWithClass() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.content.Context;
+                        import android.os.UserManager;
+
+                        public class TestClass {
+                            @SuppressLint("NonInjectedService")
+                            public void getSystemServiceWithoutDagger(Context context) {
+                                context.getSystemService(UserManager.class);
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(NonInjectedServiceDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testGetAccountManager() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterReceiverViaContextDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterReceiverViaContextDetectorTest.kt
index 9ed7aa0..0ac8f8e 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterReceiverViaContextDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterReceiverViaContextDetectorTest.kt
@@ -63,6 +63,34 @@
     }
 
     @Test
+    fun testSuppressRegisterReceiver() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.BroadcastReceiver;
+                    import android.content.Context;
+                    import android.content.IntentFilter;
+
+                    @SuppressWarnings("RegisterReceiverViaContext")
+                    public class TestClass {
+                        public void bind(Context context, BroadcastReceiver receiver,
+                            IntentFilter filter) {
+                          context.registerReceiver(receiver, filter, 0);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(RegisterReceiverViaContextDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testRegisterReceiverAsUser() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SlowUserQueryDetectorTest.kt
index 54cac7b..34a4249 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SlowUserQueryDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SlowUserQueryDetectorTest.kt
@@ -76,7 +76,7 @@
                         import android.os.UserManager;
 
                         public class TestClass {
-                            public void slewlyGetUserInfo(UserManager userManager) {
+                            public void slowlyGetUserInfo(UserManager userManager) {
                                 userManager.getUserInfo();
                             }
                         }
@@ -101,6 +101,34 @@
     }
 
     @Test
+    fun testSuppressGetUserInfo() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.os.UserManager;
+
+                        public class TestClass {
+                            @SuppressWarnings("SlowUserInfoQuery")
+                            public void slowlyGetUserInfo(UserManager userManager) {
+                                userManager.getUserInfo();
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(
+                SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testUserTrackerGetUserId() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SoftwareBitmapDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SoftwareBitmapDetectorTest.kt
index c632636..34becc6 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SoftwareBitmapDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/SoftwareBitmapDetectorTest.kt
@@ -63,6 +63,31 @@
     }
 
     @Test
+    fun testSuppressSoftwareBitmap() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    import android.graphics.Bitmap;
+
+                    @SuppressWarnings("SoftwareBitmap")
+                    public class TestClass {
+                        public void test() {
+                          Bitmap.createBitmap(300, 300, Bitmap.Config.RGB_565);
+                          Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(SoftwareBitmapDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
     fun testHardwareBitmap() {
         lint()
             .files(
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/StaticSettingsProviderDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/StaticSettingsProviderDetectorTest.kt
index b83ed70..efe4c90 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/StaticSettingsProviderDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/StaticSettingsProviderDetectorTest.kt
@@ -28,7 +28,7 @@
     override fun getIssues(): List<Issue> = listOf(StaticSettingsProviderDetector.ISSUE)
 
     @Test
-    fun testGetServiceWithString() {
+    fun testSuppressGetServiceWithString() {
         lint()
             .files(
                 TestFiles.java(
@@ -204,5 +204,34 @@
             )
     }
 
+    @Test
+    fun testGetServiceWithString() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+
+                        import android.provider.Settings;
+                        import android.provider.Settings.Global;
+                        import android.provider.Settings.Secure;
+
+                        public class TestClass {
+                            @SuppressWarnings("StaticSettingsProvider")
+                            public void getSystemServiceWithoutDagger(Context context) {
+                                final ContentResolver cr = mContext.getContentResolver();
+                                Global.getFloat(cr, Settings.Global.UNLOCK_SOUND);
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(StaticSettingsProviderDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
     private val stubs = androidStubs
 }
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index a3b4b38..6976786 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -25,9 +25,15 @@
 
 -keep class ** extends androidx.preference.PreferenceFragment
 -keep class com.android.systemui.tuner.*
+
+# The plugins and animation subpackages both act as shared libraries that might be referenced in
+# dynamically-loaded plugin APKs.
 -keep class com.android.systemui.plugins.** {
     *;
 }
+-keep class !com.android.systemui.animation.R$**,com.android.systemui.animation.** {
+    *;
+}
 -keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
     *;
 }
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index d27fa19..8b85940 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -34,30 +34,13 @@
         android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_alignParentEnd="true"
         android:gravity="center_vertical|end" >
-        <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+
+        <include
             android:id="@+id/user_switcher_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:gravity="center"
-            android:orientation="horizontal"
-            android:paddingTop="4dp"
-            android:paddingBottom="4dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:background="@drawable/status_bar_user_chip_bg"
-            android:visibility="visible" >
-            <ImageView android:id="@+id/current_user_avatar"
-                android:layout_width="@dimen/multi_user_avatar_keyguard_size"
-                android:layout_height="@dimen/multi_user_avatar_keyguard_size"
-                android:scaleType="centerInside"
-                android:paddingEnd="4dp" />
-
-            <TextView android:id="@+id/current_user_name"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-                />
-        </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
+            android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
+            layout="@layout/status_bar_user_chip_container" />
 
         <FrameLayout android:id="@+id/system_icons_container"
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 80e65a3..f7600e6 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -136,31 +136,12 @@
                 android:gravity="center_vertical|end"
                 android:clipChildren="false">
 
-                <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+                <include
                     android:id="@+id/user_switcher_container"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:gravity="center"
-                    android:orientation="horizontal"
-                    android:paddingTop="4dp"
-                    android:paddingBottom="4dp"
-                    android:paddingStart="8dp"
-                    android:paddingEnd="8dp"
-                    android:layout_marginEnd="16dp"
-                    android:background="@drawable/status_bar_user_chip_bg"
-                    android:visibility="visible" >
-                    <ImageView android:id="@+id/current_user_avatar"
-                        android:layout_width="@dimen/multi_user_avatar_keyguard_size"
-                        android:layout_height="@dimen/multi_user_avatar_keyguard_size"
-                        android:scaleType="centerInside"
-                        android:paddingEnd="4dp" />
-
-                    <TextView android:id="@+id/current_user_name"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-                        />
-                </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
+                    android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
+                    layout="@layout/status_bar_user_chip_container" />
 
                 <include layout="@layout/system_icons" />
             </com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_user_chip_container.xml b/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
new file mode 100644
index 0000000..b374074
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_user_chip_container.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/user_switcher_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="horizontal"
+    android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
+    android:background="@drawable/status_bar_user_chip_bg"
+    android:visibility="visible" >
+    <ImageView android:id="@+id/current_user_avatar"
+        android:layout_width="@dimen/status_bar_user_chip_avatar_size"
+        android:layout_height="@dimen/status_bar_user_chip_avatar_size"
+        android:layout_margin="4dp"
+        android:scaleType="centerInside" />
+
+    <TextView android:id="@+id/current_user_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingEnd="8dp"
+        android:textAppearance="@style/TextAppearance.StatusBar.UserChip"
+        />
+</com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fbdccff..437d89b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1422,6 +1422,11 @@
     <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
     <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
 
+    <!-- Status bar user chip -->
+    <dimen name="status_bar_user_chip_avatar_size">16dp</dimen>
+    <dimen name="status_bar_user_chip_end_margin">12dp</dimen>
+    <dimen name="status_bar_user_chip_text_size">12sp</dimen>
+
     <!-- Internet panel related dimensions -->
     <dimen name="internet_dialog_list_max_height">662dp</dimen>
     <!-- The height of the WiFi network in Internet panel. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 72c8163e..9eafdb9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1996,6 +1996,9 @@
     <!-- SysUI Tuner: Summary of no shortcut being selected [CHAR LIMIT=60] -->
     <string name="lockscreen_none">None</string>
 
+    <!-- ClockId to use when none is set by user -->
+    <string name="lockscreen_clock_id_fallback" translatable="false">DEFAULT</string>
+
     <!-- SysUI Tuner: Format string for describing launching an app [CHAR LIMIT=60] -->
     <string name="tuner_launch_app">Launch <xliff:g id="app" example="Settings">%1$s</xliff:g></string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ae80070..fe4f639 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -23,6 +23,12 @@
         <item name="android:textColor">@color/status_bar_clock_color</item>
     </style>
 
+    <style name="TextAppearance.StatusBar.UserChip" parent="@*android:style/TextAppearance.StatusBar.Icon">
+        <item name="android:textSize">@dimen/status_bar_user_chip_text_size</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:textColor">@color/status_bar_clock_color</item>
+    </style>
+
     <style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar">
         <item name="android:textColor">?android:attr/textColorTertiary</item>
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 601cb66..5c2c27a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -41,6 +41,7 @@
     val isEnabled: Boolean,
     userHandle: Int,
     defaultClockProvider: ClockProvider,
+    val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
 ) {
     // Usually this would be a typealias, but a SAM provides better java interop
     fun interface ClockChangeListener {
@@ -69,10 +70,13 @@
                     context.contentResolver,
                     Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
                 )
-                ClockSetting.deserialize(json)?.clockId ?: DEFAULT_CLOCK_ID
+                if (json == null || json.isEmpty()) {
+                    return fallbackClockId
+                }
+                ClockSetting.deserialize(json).clockId
             } catch (ex: Exception) {
                 Log.e(TAG, "Failed to parse clock setting", ex)
-                DEFAULT_CLOCK_ID
+                fallbackClockId
             }
         }
         set(value) {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 9767313..b514f60 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.UserHandle;
 
+import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -50,6 +51,7 @@
                 handler,
                 featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
                 UserHandle.USER_ALL,
-                defaultClockProvider);
+                defaultClockProvider,
+                context.getString(R.string.lockscreen_clock_id_fallback));
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
index 8fc8600..a7d4455 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
@@ -21,10 +21,7 @@
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
@@ -50,10 +47,4 @@
     static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) {
         return view.findViewById(R.id.user_switcher_container);
     }
-
-    /** */
-    @Binds
-    @KeyguardStatusBarViewScope
-    abstract StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
-            StatusBarUserSwitcherControllerImpl controller);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e27660f..51691c2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -61,6 +61,9 @@
     // TODO(b/254512517): Tracking Bug
     val FSI_REQUIRES_KEYGUARD = unreleasedFlag(110, "fsi_requires_keyguard", teamfood = true)
 
+    // TODO(b/259130119): Tracking Bug
+    val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true)
+
     // TODO(b/254512538): Tracking Bug
     val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true)
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index 9ba3501..03bb7a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -32,8 +32,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.FgsManagerController
@@ -42,10 +40,9 @@
 import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository
 import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository
 import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
-import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.security.data.repository.SecurityRepository
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.user.UserSwitcherActivity
+import com.android.systemui.user.domain.interactor.UserInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
@@ -100,13 +97,12 @@
 @Inject
 constructor(
     private val activityStarter: ActivityStarter,
-    private val featureFlags: FeatureFlags,
     private val metricsLogger: MetricsLogger,
     private val uiEventLogger: UiEventLogger,
     private val deviceProvisionedController: DeviceProvisionedController,
     private val qsSecurityFooterUtils: QSSecurityFooterUtils,
     private val fgsManagerController: FgsManagerController,
-    private val userSwitchDialogController: UserSwitchDialogController,
+    private val userInteractor: UserInteractor,
     securityRepository: SecurityRepository,
     foregroundServicesRepository: ForegroundServicesRepository,
     userSwitcherRepository: UserSwitcherRepository,
@@ -182,22 +178,6 @@
     }
 
     override fun showUserSwitcher(context: Context, expandable: Expandable) {
-        if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
-            userSwitchDialogController.showDialog(context, expandable)
-            return
-        }
-
-        val intent =
-            Intent(context, UserSwitcherActivity::class.java).apply {
-                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-            }
-
-        activityStarter.startActivity(
-            intent,
-            true /* dismissShade */,
-            expandable.activityLaunchController(),
-            true /* showOverlockscreenwhenlocked */,
-            UserHandle.SYSTEM,
-        )
+        userInteractor.showUserSwitcher(context, expandable)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 7a49a49..13566ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -48,6 +48,9 @@
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
+import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -70,7 +73,7 @@
     private ImageView mMultiUserAvatar;
     private BatteryMeterView mBatteryView;
     private StatusIconContainer mStatusIconContainer;
-    private ViewGroup mUserSwitcherContainer;
+    private StatusBarUserSwitcherContainer mUserSwitcherContainer;
 
     private boolean mKeyguardUserSwitcherEnabled;
     private boolean mKeyguardUserAvatarEnabled;
@@ -121,8 +124,12 @@
         loadDimens();
     }
 
-    public ViewGroup getUserSwitcherContainer() {
-        return mUserSwitcherContainer;
+    /**
+     * Should only be called from {@link KeyguardStatusBarViewController}
+     * @param viewModel view model for the status bar user chip
+     */
+    void init(StatusBarUserChipViewModel viewModel) {
+        StatusBarUserChipViewBinder.bind(mUserSwitcherContainer, viewModel);
     }
 
     @Override
@@ -304,10 +311,7 @@
         lp = (LayoutParams) mStatusIconArea.getLayoutParams();
         lp.removeRule(RelativeLayout.RIGHT_OF);
         lp.width = LayoutParams.WRAP_CONTENT;
-
-        LinearLayout.LayoutParams llp =
-                (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
-        llp.setMarginStart(getResources().getDimensionPixelSize(
+        lp.setMarginStart(getResources().getDimensionPixelSize(
                 R.dimen.system_icons_super_container_margin_start));
         return true;
     }
@@ -339,10 +343,7 @@
         lp = (LayoutParams) mStatusIconArea.getLayoutParams();
         lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view);
         lp.width = LayoutParams.MATCH_PARENT;
-
-        LinearLayout.LayoutParams llp =
-                (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
-        llp.setMarginStart(0);
+        lp.setMarginStart(0);
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 14cebf4..d4dc1dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -59,13 +59,11 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.fragment.StatusBarIconBlocklistKt;
 import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -110,9 +108,7 @@
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final StatusBarContentInsetsProvider mInsetsProvider;
     private final UserManager mUserManager;
-    private final StatusBarUserSwitcherFeatureController mFeatureController;
-    private final StatusBarUserSwitcherController mUserSwitcherController;
-    private final StatusBarUserInfoTracker mStatusBarUserInfoTracker;
+    private final StatusBarUserChipViewModel mStatusBarUserChipViewModel;
     private final SecureSettings mSecureSettings;
     private final CommandQueue mCommandQueue;
     private final Executor mMainExecutor;
@@ -276,9 +272,7 @@
             SysuiStatusBarStateController statusBarStateController,
             StatusBarContentInsetsProvider statusBarContentInsetsProvider,
             UserManager userManager,
-            StatusBarUserSwitcherFeatureController featureController,
-            StatusBarUserSwitcherController userSwitcherController,
-            StatusBarUserInfoTracker statusBarUserInfoTracker,
+            StatusBarUserChipViewModel userChipViewModel,
             SecureSettings secureSettings,
             CommandQueue commandQueue,
             @Main Executor mainExecutor,
@@ -301,9 +295,7 @@
         mStatusBarStateController = statusBarStateController;
         mInsetsProvider = statusBarContentInsetsProvider;
         mUserManager = userManager;
-        mFeatureController = featureController;
-        mUserSwitcherController = userSwitcherController;
-        mStatusBarUserInfoTracker = statusBarUserInfoTracker;
+        mStatusBarUserChipViewModel = userChipViewModel;
         mSecureSettings = secureSettings;
         mCommandQueue = commandQueue;
         mMainExecutor = mainExecutor;
@@ -328,8 +320,7 @@
                 R.dimen.header_notifications_collide_distance);
 
         mView.setKeyguardUserAvatarEnabled(
-                !mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
-        mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
+                !mStatusBarUserChipViewModel.getChipEnabled());
         mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
 
         mDisableStateTracker = new DisableStateTracker(
@@ -344,11 +335,11 @@
         super.onInit();
         mCarrierTextController.init();
         mBatteryMeterViewController.init();
-        mUserSwitcherController.init();
     }
 
     @Override
     protected void onViewAttached() {
+        mView.init(mStatusBarUserChipViewModel);
         mConfigurationController.addCallback(mConfigurationListener);
         mAnimationScheduler.addCallback(mAnimationCallback);
         mUserInfoController.addCallback(mOnUserInfoChangedListener);
@@ -394,9 +385,6 @@
     /** Sets whether user switcher is enabled. */
     public void setKeyguardUserSwitcherEnabled(boolean enabled) {
         mView.setKeyguardUserSwitcherEnabled(enabled);
-        // We don't have a listener for when the user switcher setting changes, so this is
-        // where we re-check the state
-        mStatusBarUserInfoTracker.checkEnabled();
     }
 
     /** Sets whether this controller should listen to battery updates. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 7aeb08d..28bc64d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -38,6 +38,9 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
+import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.Objects;
@@ -73,6 +76,11 @@
         mTouchEventHandler = handler;
     }
 
+    void init(StatusBarUserChipViewModel viewModel) {
+        StatusBarUserSwitcherContainer container = findViewById(R.id.user_switcher_container);
+        StatusBarUserChipViewBinder.bind(container, viewModel);
+    }
+
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index f9c4c8f..a6c2b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -23,11 +23,11 @@
 import android.view.ViewTreeObserver
 import com.android.systemui.R
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UNFOLD_STATUS_BAR
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
 import com.android.systemui.util.ViewController
 import com.android.systemui.util.kotlin.getOrNull
 import com.android.systemui.util.view.ViewUtil
@@ -40,7 +40,7 @@
     view: PhoneStatusBarView,
     @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
     private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
-    private val userSwitcherController: StatusBarUserSwitcherController,
+    private val userChipViewModel: StatusBarUserChipViewModel,
     private val viewUtil: ViewUtil,
     touchEventHandler: PhoneStatusBarView.TouchEventHandler,
     private val configurationController: ConfigurationController
@@ -91,10 +91,10 @@
 
     init {
         mView.setTouchEventHandler(touchEventHandler)
+        mView.init(userChipViewModel)
     }
 
     override fun onInit() {
-        userSwitcherController.init()
     }
 
     fun setImportantForAccessibility(mode: Int) {
@@ -156,9 +156,9 @@
         private val unfoldComponent: Optional<SysUIUnfoldComponent>,
         @Named(UNFOLD_STATUS_BAR)
         private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
-        private val userSwitcherController: StatusBarUserSwitcherController,
+        private val userChipViewModel: StatusBarUserChipViewModel,
         private val viewUtil: ViewUtil,
-        private val configurationController: ConfigurationController
+        private val configurationController: ConfigurationController,
     ) {
         fun create(
             view: PhoneStatusBarView,
@@ -168,7 +168,7 @@
                 view,
                 progressProvider.getOrNull(),
                 unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
-                userSwitcherController,
+                userChipViewModel,
                 viewUtil,
                 touchEventHandler,
                 configurationController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 41f1f95..efec270 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -29,8 +29,6 @@
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
 
@@ -39,7 +37,6 @@
 
 import javax.inject.Named;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.Multibinds;
@@ -126,12 +123,6 @@
     }
 
     /** */
-    @Binds
-    @StatusBarFragmentScope
-    StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
-            StatusBarUserSwitcherControllerImpl controller);
-
-    /** */
     @Provides
     @StatusBarFragmentScope
     static PhoneStatusBarViewController providePhoneStatusBarViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
deleted file mode 100644
index f6b8cb0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.userswitcher
-
-import android.graphics.drawable.Drawable
-import android.os.UserManager
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.policy.CallbackController
-import com.android.systemui.statusbar.policy.UserInfoController
-import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
-import java.io.PrintWriter
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-/**
- * Since every user switcher chip will user the exact same information and logic on whether or not
- * to show, and what data to show, it makes sense to create a single tracker here
- */
-@SysUISingleton
-class StatusBarUserInfoTracker @Inject constructor(
-    private val userInfoController: UserInfoController,
-    private val userManager: UserManager,
-    private val dumpManager: DumpManager,
-    @Main private val mainExecutor: Executor,
-    @Background private val backgroundExecutor: Executor
-) : CallbackController<CurrentUserChipInfoUpdatedListener>, Dumpable {
-    var currentUserName: String? = null
-        private set
-    var currentUserAvatar: Drawable? = null
-        private set
-    var userSwitcherEnabled = false
-        private set
-    private var listening = false
-
-    private val listeners = mutableListOf<CurrentUserChipInfoUpdatedListener>()
-
-    private val userInfoChangedListener = OnUserInfoChangedListener { name, picture, _ ->
-        currentUserAvatar = picture
-        currentUserName = name
-        notifyListenersUserInfoChanged()
-    }
-
-    init {
-        dumpManager.registerDumpable(TAG, this)
-    }
-
-    override fun addCallback(listener: CurrentUserChipInfoUpdatedListener) {
-        if (listeners.isEmpty()) {
-            startListening()
-        }
-
-        if (!listeners.contains(listener)) {
-            listeners.add(listener)
-        }
-    }
-
-    override fun removeCallback(listener: CurrentUserChipInfoUpdatedListener) {
-        listeners.remove(listener)
-
-        if (listeners.isEmpty()) {
-            stopListening()
-        }
-    }
-
-    private fun notifyListenersUserInfoChanged() {
-        listeners.forEach {
-            it.onCurrentUserChipInfoUpdated()
-        }
-    }
-
-    private fun notifyListenersSettingChanged() {
-        listeners.forEach {
-            it.onStatusBarUserSwitcherSettingChanged(userSwitcherEnabled)
-        }
-    }
-
-    private fun startListening() {
-        listening = true
-        userInfoController.addCallback(userInfoChangedListener)
-    }
-
-    private fun stopListening() {
-        listening = false
-        userInfoController.removeCallback(userInfoChangedListener)
-    }
-
-    /**
-     * Force a check to [UserManager.isUserSwitcherEnabled], and update listeners if the value has
-     * changed
-     */
-    fun checkEnabled() {
-        backgroundExecutor.execute {
-            // Check on a background thread to avoid main thread Binder calls
-            val wasEnabled = userSwitcherEnabled
-            userSwitcherEnabled = userManager.isUserSwitcherEnabled
-
-            if (wasEnabled != userSwitcherEnabled) {
-                mainExecutor.execute {
-                    notifyListenersSettingChanged()
-                }
-            }
-        }
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("  userSwitcherEnabled=$userSwitcherEnabled")
-        pw.println("  listening=$listening")
-    }
-}
-
-interface CurrentUserChipInfoUpdatedListener {
-    fun onCurrentUserChipInfoUpdated()
-    fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {}
-}
-
-private const val TAG = "StatusBarUserInfoTracker"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
deleted file mode 100644
index e498ae4..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.userswitcher
-
-import android.content.Intent
-import android.os.UserHandle
-import android.view.View
-import com.android.systemui.animation.Expandable
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
-
-import com.android.systemui.qs.user.UserSwitchDialogController
-import com.android.systemui.user.UserSwitcherActivity
-import com.android.systemui.util.ViewController
-
-import javax.inject.Inject
-
-/**
- * ViewController for [StatusBarUserSwitcherContainer]
- */
-class StatusBarUserSwitcherControllerImpl @Inject constructor(
-    view: StatusBarUserSwitcherContainer,
-    private val tracker: StatusBarUserInfoTracker,
-    private val featureController: StatusBarUserSwitcherFeatureController,
-    private val userSwitcherDialogController: UserSwitchDialogController,
-    private val featureFlags: FeatureFlags,
-    private val activityStarter: ActivityStarter,
-    private val falsingManager: FalsingManager
-) : ViewController<StatusBarUserSwitcherContainer>(view),
-        StatusBarUserSwitcherController {
-    private val listener = object : CurrentUserChipInfoUpdatedListener {
-        override fun onCurrentUserChipInfoUpdated() {
-            updateChip()
-        }
-
-        override fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {
-            updateEnabled()
-        }
-    }
-
-    private val featureFlagListener = object : OnUserSwitcherPreferenceChangeListener {
-        override fun onUserSwitcherPreferenceChange(enabled: Boolean) {
-            updateEnabled()
-        }
-    }
-
-    public override fun onViewAttached() {
-        tracker.addCallback(listener)
-        featureController.addCallback(featureFlagListener)
-        mView.setOnClickListener { view: View ->
-            if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return@setOnClickListener
-            }
-
-            if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
-                val intent = Intent(context, UserSwitcherActivity::class.java)
-                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-
-                activityStarter.startActivity(intent, true /* dismissShade */,
-                        null /* ActivityLaunchAnimator.Controller */,
-                        true /* showOverlockscreenwhenlocked */, UserHandle.SYSTEM)
-            } else {
-                userSwitcherDialogController.showDialog(view.context, Expandable.fromView(view))
-            }
-        }
-
-        updateEnabled()
-    }
-
-    override fun onViewDetached() {
-        tracker.removeCallback(listener)
-        featureController.removeCallback(featureFlagListener)
-        mView.setOnClickListener(null)
-    }
-
-    private fun updateChip() {
-        mView.text.text = tracker.currentUserName
-        mView.avatar.setImageDrawable(tracker.currentUserAvatar)
-    }
-
-    private fun updateEnabled() {
-        if (featureController.isStatusBarUserSwitcherFeatureEnabled() &&
-                tracker.userSwitcherEnabled) {
-            mView.visibility = View.VISIBLE
-            updateChip()
-        } else {
-            mView.visibility = View.GONE
-        }
-    }
-}
-
-interface StatusBarUserSwitcherController {
-    fun init()
-}
-
-private const val TAG = "SbUserSwitcherController"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
deleted file mode 100644
index 7bae9ff..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.userswitcher
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.statusbar.policy.CallbackController
-
-import javax.inject.Inject
-
-@SysUISingleton
-class StatusBarUserSwitcherFeatureController @Inject constructor(
-    private val flags: FeatureFlags
-) : CallbackController<OnUserSwitcherPreferenceChangeListener> {
-    private val listeners = mutableListOf<OnUserSwitcherPreferenceChangeListener>()
-
-    init {
-        flags.addListener(Flags.STATUS_BAR_USER_SWITCHER) {
-            it.requestNoRestart()
-            notifyListeners()
-        }
-    }
-
-    fun isStatusBarUserSwitcherFeatureEnabled(): Boolean {
-        return flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER)
-    }
-
-    override fun addCallback(listener: OnUserSwitcherPreferenceChangeListener) {
-        if (!listeners.contains(listener)) {
-            listeners.add(listener)
-        }
-    }
-
-    override fun removeCallback(listener: OnUserSwitcherPreferenceChangeListener) {
-        listeners.remove(listener)
-    }
-
-    private fun notifyListeners() {
-        val enabled = flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER)
-        listeners.forEach {
-            it.onUserSwitcherPreferenceChange(enabled)
-        }
-    }
-}
-
-interface OnUserSwitcherPreferenceChangeListener {
-    fun onUserSwitcherPreferenceChange(enabled: Boolean)
-}
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 38b3769..acdf0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -146,7 +146,13 @@
     }
 
     private String getDeviceString(CachedBluetoothDevice device) {
-        return device.getName() + " " + device.getBondState() + " " + device.isConnected();
+        return device.getName()
+                + " bondState=" + device.getBondState()
+                + " connected=" + device.isConnected()
+                + " active[A2DP]=" + device.isActiveDevice(BluetoothProfile.A2DP)
+                + " active[HEADSET]=" + device.isActiveDevice(BluetoothProfile.HEADSET)
+                + " active[HEARING_AID]=" + device.isActiveDevice(BluetoothProfile.HEARING_AID)
+                + " active[LE_AUDIO]=" + device.isActiveDevice(BluetoothProfile.LE_AUDIO);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 44e5ce8..fb17b69 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -174,7 +174,7 @@
             chipInnerView,
             ViewHierarchyAnimator.Hotspot.TOP,
             Interpolators.EMPHASIZED_DECELERATE,
-            duration = ANIMATION_DURATION,
+            duration = ANIMATION_IN_DURATION,
             includeMargins = true,
             includeFadeIn = true,
             // We can only request focus once the animation finishes.
@@ -187,7 +187,7 @@
             view.requireViewById<ViewGroup>(R.id.chipbar_inner),
             ViewHierarchyAnimator.Hotspot.TOP,
             Interpolators.EMPHASIZED_ACCELERATE,
-            ANIMATION_DURATION,
+            ANIMATION_OUT_DURATION,
             includeMargins = true,
             onAnimationEnd,
         )
@@ -208,4 +208,5 @@
     }
 }
 
-private const val ANIMATION_DURATION = 500L
+private const val ANIMATION_IN_DURATION = 500L
+private const val ANIMATION_OUT_DURATION = 250L
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index ed53de7..4c9b8e4 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -23,6 +23,7 @@
 import android.os.UserManager
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
@@ -79,6 +80,9 @@
     /** Whether we've scheduled the creation of a guest user. */
     val isGuestUserCreationScheduled: AtomicBoolean
 
+    /** Whether to enable the status bar user chip (which launches the user switcher) */
+    val isStatusBarUserChipEnabled: Boolean
+
     /** The user of the secondary service. */
     var secondaryUserId: Int
 
@@ -127,6 +131,9 @@
 
     override val isGuestUserCreationScheduled = AtomicBoolean()
 
+    override val isStatusBarUserChipEnabled: Boolean =
+        appContext.resources.getBoolean(R.bool.flag_user_switcher_chip)
+
     override var secondaryUserId: Int = UserHandle.USER_NULL
 
     override var isRefreshUsersPaused: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index 516c650..83f0711 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -34,15 +34,19 @@
 import com.android.internal.util.UserIcons
 import com.android.systemui.R
 import com.android.systemui.SystemUISecondaryUserService
+import com.android.systemui.animation.Expandable
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.user.UserSwitcherActivity
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.user.data.source.UserRecord
@@ -81,6 +85,7 @@
     private val repository: UserRepository,
     private val activityStarter: ActivityStarter,
     private val keyguardInteractor: KeyguardInteractor,
+    private val featureFlags: FeatureFlags,
     private val manager: UserManager,
     @Application private val applicationScope: CoroutineScope,
     telephonyInteractor: TelephonyInteractor,
@@ -256,6 +261,9 @@
     /** Whether the guest user is currently being reset. */
     val isGuestUserResetting: Boolean = guestUserInteractor.isGuestUserResetting
 
+    /** Whether to enable the user chip in the status bar */
+    val isStatusBarUserChipEnabled: Boolean = repository.isStatusBarUserChipEnabled
+
     private val _dialogShowRequests = MutableStateFlow<ShowDialogRequestModel?>(null)
     val dialogShowRequests: Flow<ShowDialogRequestModel?> = _dialogShowRequests.asStateFlow()
 
@@ -468,6 +476,26 @@
         }
     }
 
+    fun showUserSwitcher(context: Context, expandable: Expandable) {
+        if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
+            showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog)
+            return
+        }
+
+        val intent =
+            Intent(context, UserSwitcherActivity::class.java).apply {
+                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+            }
+
+        activityStarter.startActivity(
+            intent,
+            true /* dismissShade */,
+            expandable.activityLaunchController(),
+            true /* showOverlockscreenwhenlocked */,
+            UserHandle.SYSTEM,
+        )
+    }
+
     private fun showDialog(request: ShowDialogRequestModel) {
         _dialogShowRequests.value = request
     }
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
index 177356e..85c2964 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
@@ -43,4 +43,7 @@
         val onExitGuestUser: (guestId: Int, targetId: Int, forceRemoveGuest: Boolean) -> Unit,
         override val dialogShower: UserSwitchDialogController.DialogShower?,
     ) : ShowDialogRequestModel(dialogShower)
+
+    /** Show the user switcher dialog */
+    object ShowUserSwitcherDialog : ShowDialogRequestModel()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt
new file mode 100644
index 0000000..8e40f68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.user.ui.binder
+
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.ui.binder.TextViewBinder
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@OptIn(InternalCoroutinesApi::class)
+object StatusBarUserChipViewBinder {
+    /** Binds the status bar user chip view model to the given view */
+    @JvmStatic
+    fun bind(
+        view: StatusBarUserSwitcherContainer,
+        viewModel: StatusBarUserChipViewModel,
+    ) {
+        view.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.isChipVisible.collect { isVisible -> view.isVisible = isVisible }
+                }
+
+                launch {
+                    viewModel.userName.collect { name -> TextViewBinder.bind(view.text, name) }
+                }
+
+                launch {
+                    viewModel.userAvatar.collect { avatar -> view.avatar.setImageDrawable(avatar) }
+                }
+
+                bindButton(view, viewModel)
+            }
+        }
+    }
+
+    private fun bindButton(
+        view: StatusBarUserSwitcherContainer,
+        viewModel: StatusBarUserChipViewModel,
+    ) {
+        view.setOnClickListener { viewModel.onClick(Expandable.fromView(view)) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
new file mode 100644
index 0000000..ed25898
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
@@ -0,0 +1,68 @@
+package com.android.systemui.user.ui.dialog
+
+import android.content.Context
+import android.content.Intent
+import android.provider.Settings
+import android.view.LayoutInflater
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.QSUserSwitcherEvent
+import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/**
+ * Extracted from the old UserSwitchDialogController. This is the dialog version of the full-screen
+ * user switcher. See config_enableFullscreenUserSwitcher
+ */
+class UserSwitchDialog(
+    context: Context,
+    adapter: UserDetailView.Adapter,
+    uiEventLogger: UiEventLogger,
+    falsingManager: FalsingManager,
+    activityStarter: ActivityStarter,
+    dialogLaunchAnimator: DialogLaunchAnimator,
+) : SystemUIDialog(context) {
+    init {
+        setShowForAllUsers(true)
+        setCanceledOnTouchOutside(true)
+        setTitle(R.string.qs_user_switch_dialog_title)
+        setPositiveButton(R.string.quick_settings_done) { _, _ ->
+            uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
+        }
+        setNeutralButton(
+            R.string.quick_settings_more_user_settings,
+            { _, _ ->
+                if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                    uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
+                    val controller =
+                        dialogLaunchAnimator.createActivityLaunchController(
+                            getButton(BUTTON_NEUTRAL)
+                        )
+
+                    if (controller == null) {
+                        dismiss()
+                    }
+
+                    activityStarter.postStartActivityDismissingKeyguard(
+                        USER_SETTINGS_INTENT,
+                        0,
+                        controller
+                    )
+                }
+            },
+            false /* dismissOnClick */
+        )
+        val gridFrame =
+            LayoutInflater.from(this.context).inflate(R.layout.qs_user_dialog_content, null)
+        setView(gridFrame)
+
+        adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
+    }
+
+    companion object {
+        private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 58a4473..4141054 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -20,6 +20,7 @@
 import android.app.Dialog
 import android.content.Context
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.users.UserCreatingDialog
 import com.android.systemui.CoreStartable
 import com.android.systemui.animation.DialogCuj
@@ -27,11 +28,14 @@
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.tiles.UserDetailView
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.user.domain.model.ShowDialogRequestModel
 import dagger.Lazy
 import javax.inject.Inject
+import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.launch
@@ -47,6 +51,9 @@
     private val broadcastSender: Lazy<BroadcastSender>,
     private val dialogLaunchAnimator: Lazy<DialogLaunchAnimator>,
     private val interactor: Lazy<UserInteractor>,
+    private val userDetailAdapterProvider: Provider<UserDetailView.Adapter>,
+    private val eventLogger: Lazy<UiEventLogger>,
+    private val activityStarter: Lazy<ActivityStarter>,
 ) : CoreStartable {
 
     private var currentDialog: Dialog? = null
@@ -108,6 +115,21 @@
                                     INTERACTION_JANK_EXIT_GUEST_MODE_TAG,
                                 ),
                             )
+                        is ShowDialogRequestModel.ShowUserSwitcherDialog ->
+                            Pair(
+                                UserSwitchDialog(
+                                    context = context.get(),
+                                    adapter = userDetailAdapterProvider.get(),
+                                    uiEventLogger = eventLogger.get(),
+                                    falsingManager = falsingManager.get(),
+                                    activityStarter = activityStarter.get(),
+                                    dialogLaunchAnimator = dialogLaunchAnimator.get(),
+                                ),
+                                DialogCuj(
+                                    InteractionJankMonitor.CUJ_USER_DIALOG_OPEN,
+                                    INTERACTION_JANK_EXIT_GUEST_MODE_TAG,
+                                ),
+                            )
                     }
                 currentDialog = dialog
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
new file mode 100644
index 0000000..3300e8e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.user.ui.viewmodel
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.user.domain.interactor.UserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.mapLatest
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class StatusBarUserChipViewModel
+@Inject
+constructor(
+    @Application private val context: Context,
+    interactor: UserInteractor,
+) {
+    /** Whether the status bar chip ui should be available */
+    val chipEnabled: Boolean = interactor.isStatusBarUserChipEnabled
+
+    /** Whether or not the chip should be showing, based on the number of users */
+    val isChipVisible: Flow<Boolean> =
+        if (!chipEnabled) {
+            flowOf(false)
+        } else {
+            interactor.users.mapLatest { users -> users.size > 1 }
+        }
+
+    /** The display name of the current user */
+    val userName: Flow<Text> = interactor.selectedUser.mapLatest { userModel -> userModel.name }
+
+    /** Avatar for the current user */
+    val userAvatar: Flow<Drawable> =
+        interactor.selectedUser.mapLatest { userModel -> userModel.image }
+
+    /** Action to execute on click. Should launch the user switcher */
+    val onClick: (Expandable) -> Unit = { interactor.showUserSwitcher(context, it) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
index 6b5556b..0f3eddf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java
@@ -19,7 +19,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 
@@ -53,8 +52,8 @@
     /**
      * Wrapped version of {@link DeviceConfig#enforceReadPermission}.
      */
-    public void enforceReadPermission(Context context, String namespace) {
-        DeviceConfig.enforceReadPermission(context, namespace);
+    public void enforceReadPermission(String namespace) {
+        DeviceConfig.enforceReadPermission(namespace);
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 8bd7163..52b694f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -62,7 +62,6 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -806,7 +805,6 @@
             .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
     }
 
-    @Ignore("b/229838140")
     @Test
     fun testMediaRecommendationDisabled_removesSmartspaceData() {
         // GIVEN a media recommendation card is present
@@ -823,7 +821,9 @@
         tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
 
         // THEN listeners are notified
+        uiExecutor.advanceClockToLast()
         foregroundExecutor.advanceClockToLast()
+        uiExecutor.runAllReady()
         foregroundExecutor.runAllReady()
         verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(true))
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 2c2ddbb..645b1cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -16,10 +16,8 @@
 
 package com.android.systemui.qs.footer.domain.interactor
 
-import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
-import android.os.UserHandle
 import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -30,17 +28,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityLaunchAnimator
 import com.android.systemui.animation.Expandable
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.QSSecurityFooterUtils
 import com.android.systemui.qs.footer.FooterActionsTestUtils
-import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.truth.correspondence.FakeUiEvent
 import com.android.systemui.truth.correspondence.LogMaker
-import com.android.systemui.user.UserSwitcherActivity
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
@@ -156,54 +150,4 @@
         // We only unlock the device.
         verify(activityStarter).postQSRunnableDismissingKeyguard(any())
     }
-
-    @Test
-    fun showUserSwitcher_fullScreenDisabled() {
-        val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
-        val userSwitchDialogController = mock<UserSwitchDialogController>()
-        val underTest =
-            utils.footerActionsInteractor(
-                featureFlags = featureFlags,
-                userSwitchDialogController = userSwitchDialogController,
-            )
-
-        val expandable = mock<Expandable>()
-        underTest.showUserSwitcher(context, expandable)
-
-        // Dialog is shown.
-        verify(userSwitchDialogController).showDialog(context, expandable)
-    }
-
-    @Test
-    fun showUserSwitcher_fullScreenEnabled() {
-        val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
-        val activityStarter = mock<ActivityStarter>()
-        val underTest =
-            utils.footerActionsInteractor(
-                featureFlags = featureFlags,
-                activityStarter = activityStarter,
-            )
-
-        // The clicked expandable.
-        val expandable = mock<Expandable>()
-        underTest.showUserSwitcher(context, expandable)
-
-        // Dialog is shown.
-        val intentCaptor = argumentCaptor<Intent>()
-        verify(activityStarter)
-            .startActivity(
-                intentCaptor.capture(),
-                /* dismissShade= */ eq(true),
-                /* ActivityLaunchAnimator.Controller= */ nullable(),
-                /* showOverLockscreenWhenLocked= */ eq(true),
-                eq(UserHandle.SYSTEM),
-            )
-        assertThat(intentCaptor.value.component)
-            .isEqualTo(
-                ComponentName(
-                    context,
-                    UserSwitcherActivity::class.java,
-                )
-            )
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 6ec5cf8..eb0b9b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -56,14 +56,12 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -112,16 +110,12 @@
     private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider;
     @Mock
     private UserManager mUserManager;
+    @Mock
+    private StatusBarUserChipViewModel mStatusBarUserChipViewModel;
     @Captor
     private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor;
     @Captor
     private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
-    @Mock
-    private StatusBarUserSwitcherFeatureController mStatusBarUserSwitcherFeatureController;
-    @Mock
-    private StatusBarUserSwitcherController mStatusBarUserSwitcherController;
-    @Mock
-    private StatusBarUserInfoTracker mStatusBarUserInfoTracker;
     @Mock private SecureSettings mSecureSettings;
     @Mock private CommandQueue mCommandQueue;
     @Mock private KeyguardLogger mLogger;
@@ -169,9 +163,7 @@
                 mStatusBarStateController,
                 mStatusBarContentInsetsProvider,
                 mUserManager,
-                mStatusBarUserSwitcherFeatureController,
-                mStatusBarUserSwitcherController,
-                mStatusBarUserInfoTracker,
+                mStatusBarUserChipViewModel,
                 mSecureSettings,
                 mCommandQueue,
                 mFakeExecutor,
@@ -479,8 +471,7 @@
     @Test
     public void testNewUserSwitcherDisablesAvatar_newUiOn() {
         // GIVEN the status bar user switcher chip is enabled
-        when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
-                .thenReturn(true);
+        when(mStatusBarUserChipViewModel.getChipEnabled()).thenReturn(true);
 
         // WHEN the controller is created
         mController = createController();
@@ -492,8 +483,7 @@
     @Test
     public void testNewUserSwitcherDisablesAvatar_newUiOff() {
         // GIVEN the status bar user switcher chip is disabled
-        when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
-                .thenReturn(false);
+        when(mStatusBarUserChipViewModel.getChipEnabled()).thenReturn(false);
 
         // WHEN the controller is created
         mController = createController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index a61fba5..320a083 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -27,11 +27,11 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.shade.NotificationPanelViewController
-import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.view.ViewUtil
 import com.google.common.truth.Truth.assertThat
@@ -64,7 +64,7 @@
     @Mock
     private lateinit var configurationController: ConfigurationController
     @Mock
-    private lateinit var userSwitcherController: StatusBarUserSwitcherController
+    private lateinit var userChipViewModel: StatusBarUserChipViewModel
     @Mock
     private lateinit var viewUtil: ViewUtil
 
@@ -79,14 +79,13 @@
         `when`(notificationPanelViewController.view).thenReturn(panelView)
         `when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
             .thenReturn(moveFromCenterAnimation)
-        // create the view on main thread as it requires main looper
+        // create the view and controller on main thread as it requires main looper
         InstrumentationRegistry.getInstrumentation().runOnMainSync {
             val parent = FrameLayout(mContext) // add parent to keep layout params
             view = LayoutInflater.from(mContext)
                 .inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
+            controller = createAndInitController(view)
         }
-
-        controller = createAndInitController(view)
     }
 
     @Test
@@ -106,7 +105,10 @@
         val view = createViewMock()
         val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
         unfoldConfig.isEnabled = true
-        controller = createAndInitController(view)
+        // create the controller on main thread as it requires main looper
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            controller = createAndInitController(view)
+        }
 
         verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
         argumentCaptor.value.onPreDraw()
@@ -126,7 +128,7 @@
         return PhoneStatusBarViewController.Factory(
             Optional.of(sysuiUnfoldComponent),
             Optional.of(progressProvider),
-            userSwitcherController,
+            userChipViewModel,
             viewUtil,
             configurationController
         ).create(view, touchEventHandler).also {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
deleted file mode 100644
index eba3b04..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone.userswitcher
-
-import android.content.Intent
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.user.UserSwitchDialogController
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-@SmallTest
-class StatusBarUserSwitcherControllerOldImplTest : SysuiTestCase() {
-    @Mock
-    private lateinit var tracker: StatusBarUserInfoTracker
-
-    @Mock
-    private lateinit var featureController: StatusBarUserSwitcherFeatureController
-
-    @Mock
-    private lateinit var userSwitcherDialogController: UserSwitchDialogController
-
-    @Mock
-    private lateinit var featureFlags: FeatureFlags
-
-    @Mock
-    private lateinit var activityStarter: ActivityStarter
-
-    @Mock
-    private lateinit var falsingManager: FalsingManager
-
-    private lateinit var statusBarUserSwitcherContainer: StatusBarUserSwitcherContainer
-    private lateinit var controller: StatusBarUserSwitcherControllerImpl
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        statusBarUserSwitcherContainer = StatusBarUserSwitcherContainer(mContext, null)
-        statusBarUserSwitcherContainer
-        controller = StatusBarUserSwitcherControllerImpl(
-                statusBarUserSwitcherContainer,
-                tracker,
-                featureController,
-                userSwitcherDialogController,
-                featureFlags,
-                activityStarter,
-                falsingManager
-        )
-        controller.init()
-        controller.onViewAttached()
-    }
-
-    @Test
-    fun testFalsingManager() {
-        statusBarUserSwitcherContainer.callOnClick()
-        verify(falsingManager).isFalseTap(FalsingManager.LOW_PENALTY)
-    }
-
-    @Test
-    fun testStartActivity() {
-        `when`(featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)).thenReturn(false)
-        statusBarUserSwitcherContainer.callOnClick()
-        verify(userSwitcherDialogController).showDialog(any(), any())
-        `when`(featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)).thenReturn(true)
-        statusBarUserSwitcherContainer.callOnClick()
-        verify(activityStarter).startActivity(any(Intent::class.java),
-                eq(true) /* dismissShade */,
-                eq(null) /* animationController */,
-                eq(true) /* showOverLockscreenWhenLocked */,
-                eq(UserHandle.SYSTEM))
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 463f517..4b49420 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -19,6 +19,7 @@
 
 import android.app.ActivityManager
 import android.app.admin.DevicePolicyManager
+import android.content.ComponentName
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.graphics.Bitmap
@@ -33,7 +34,10 @@
 import com.android.systemui.GuestResumeSessionReceiver
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Text
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -41,6 +45,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.user.UserSwitcherActivity
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.source.UserRecord
@@ -48,9 +53,11 @@
 import com.android.systemui.user.shared.model.UserActionModel
 import com.android.systemui.user.shared.model.UserModel
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
@@ -90,6 +97,7 @@
     private lateinit var userRepository: FakeUserRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var telephonyRepository: FakeTelephonyRepository
+    private lateinit var featureFlags: FakeFeatureFlags
 
     @Before
     fun setUp() {
@@ -104,6 +112,7 @@
             SUPERVISED_USER_CREATION_APP_PACKAGE,
         )
 
+        featureFlags = FakeFeatureFlags()
         userRepository = FakeUserRepository()
         keyguardRepository = FakeKeyguardRepository()
         telephonyRepository = FakeTelephonyRepository()
@@ -147,7 +156,8 @@
                         uiEventLogger = uiEventLogger,
                         resumeSessionReceiver = resumeSessionReceiver,
                         resetOrExitSessionReceiver = resetOrExitSessionReceiver,
-                    )
+                    ),
+                featureFlags = featureFlags,
             )
     }
 
@@ -715,6 +725,52 @@
             job.cancel()
         }
 
+    @Test
+    fun `show user switcher - full screen disabled - shows dialog switcher`() =
+        runBlocking(IMMEDIATE) {
+            featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+
+            var dialogRequest: ShowDialogRequestModel? = null
+            val expandable = mock<Expandable>()
+            underTest.showUserSwitcher(context, expandable)
+
+            val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
+
+            // Dialog is shown.
+            assertThat(dialogRequest).isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog)
+
+            underTest.onDialogShown()
+            assertThat(dialogRequest).isNull()
+
+            job.cancel()
+        }
+
+    @Test
+    fun `show user switcher - full screen enabled - launches activity`() {
+        featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+
+        val expandable = mock<Expandable>()
+        underTest.showUserSwitcher(context, expandable)
+
+        // Dialog is shown.
+        val intentCaptor = argumentCaptor<Intent>()
+        verify(activityStarter)
+            .startActivity(
+                intentCaptor.capture(),
+                /* dismissShade= */ eq(true),
+                /* ActivityLaunchAnimator.Controller= */ nullable(),
+                /* showOverLockscreenWhenLocked= */ eq(true),
+                eq(UserHandle.SYSTEM),
+            )
+        assertThat(intentCaptor.value.component)
+            .isEqualTo(
+                ComponentName(
+                    context,
+                    UserSwitcherActivity::class.java,
+                )
+            )
+    }
+
     private fun assertUsers(
         models: List<UserModel>?,
         count: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
new file mode 100644
index 0000000..db348b80
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2022 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.user.ui.viewmodel
+
+import android.app.ActivityManager
+import android.app.admin.DevicePolicyManager
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.GuestResetOrExitSessionReceiver
+import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
+import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.user.data.model.UserSwitcherSettingsModel
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class StatusBarUserChipViewModelTest : SysuiTestCase() {
+    @Mock private lateinit var activityStarter: ActivityStarter
+    @Mock private lateinit var activityManager: ActivityManager
+    @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
+    @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
+
+    private lateinit var underTest: StatusBarUserChipViewModel
+
+    private val userRepository = FakeUserRepository()
+    private val keyguardRepository = FakeKeyguardRepository()
+    private val featureFlags = FakeFeatureFlags()
+    private lateinit var guestUserInteractor: GuestUserInteractor
+    private lateinit var refreshUsersScheduler: RefreshUsersScheduler
+
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        doAnswer { invocation ->
+                val userId = invocation.arguments[0] as Int
+                when (userId) {
+                    USER_ID_0 -> return@doAnswer USER_IMAGE_0
+                    USER_ID_1 -> return@doAnswer USER_IMAGE_1
+                    USER_ID_2 -> return@doAnswer USER_IMAGE_2
+                    else -> return@doAnswer mock<Bitmap>()
+                }
+            }
+            .`when`(manager)
+            .getUserIcon(anyInt())
+
+        userRepository.isStatusBarUserChipEnabled = true
+
+        refreshUsersScheduler =
+            RefreshUsersScheduler(
+                applicationScope = testScope.backgroundScope,
+                mainDispatcher = testDispatcher,
+                repository = userRepository,
+            )
+        guestUserInteractor =
+            GuestUserInteractor(
+                applicationContext = context,
+                applicationScope = testScope.backgroundScope,
+                mainDispatcher = testDispatcher,
+                backgroundDispatcher = testDispatcher,
+                manager = manager,
+                repository = userRepository,
+                deviceProvisionedController = deviceProvisionedController,
+                devicePolicyManager = devicePolicyManager,
+                refreshUsersScheduler = refreshUsersScheduler,
+                uiEventLogger = uiEventLogger,
+                resumeSessionReceiver = resumeSessionReceiver,
+                resetOrExitSessionReceiver = resetOrExitSessionReceiver,
+            )
+
+        underTest = viewModel()
+    }
+
+    @Test
+    fun `config is false - chip is disabled`() {
+        // the enabled bit is set at SystemUI startup, so recreate the view model here
+        userRepository.isStatusBarUserChipEnabled = false
+        underTest = viewModel()
+
+        assertThat(underTest.chipEnabled).isFalse()
+    }
+
+    @Test
+    fun `config is true - chip is enabled`() {
+        // the enabled bit is set at SystemUI startup, so recreate the view model here
+        userRepository.isStatusBarUserChipEnabled = true
+        underTest = viewModel()
+
+        assertThat(underTest.chipEnabled).isTrue()
+    }
+
+    @Test
+    fun `should show chip criteria - single user`() =
+        testScope.runTest {
+            userRepository.setUserInfos(listOf(USER_0))
+            userRepository.setSelectedUserInfo(USER_0)
+            userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+
+            val values = mutableListOf<Boolean>()
+
+            val job = launch { underTest.isChipVisible.toList(values) }
+            advanceUntilIdle()
+
+            assertThat(values).containsExactly(false)
+
+            job.cancel()
+        }
+
+    @Test
+    fun `should show chip criteria - multiple users`() =
+        testScope.runTest {
+            setMultipleUsers()
+
+            var latest: Boolean? = null
+            val job = underTest.isChipVisible.onEach { latest = it }.launchIn(this)
+            yield()
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun `user chip name - shows selected user info`() =
+        testScope.runTest {
+            setMultipleUsers()
+
+            var latest: Text? = null
+            val job = underTest.userName.onEach { latest = it }.launchIn(this)
+
+            userRepository.setSelectedUserInfo(USER_0)
+            assertThat(latest).isEqualTo(USER_NAME_0)
+
+            userRepository.setSelectedUserInfo(USER_1)
+            assertThat(latest).isEqualTo(USER_NAME_1)
+
+            userRepository.setSelectedUserInfo(USER_2)
+            assertThat(latest).isEqualTo(USER_NAME_2)
+
+            job.cancel()
+        }
+
+    @Test
+    fun `user chip avatar - shows selected user info`() =
+        testScope.runTest {
+            setMultipleUsers()
+
+            // A little hacky. System server passes us bitmaps and we wrap them in the interactor.
+            // Unwrap them to make sure we're always tracking the current user's bitmap
+            var latest: Bitmap? = null
+            val job =
+                underTest.userAvatar
+                    .onEach {
+                        if (it !is BitmapDrawable) {
+                            latest = null
+                        }
+
+                        latest = (it as BitmapDrawable).bitmap
+                    }
+                    .launchIn(this)
+
+            userRepository.setSelectedUserInfo(USER_0)
+            assertThat(latest).isEqualTo(USER_IMAGE_0)
+
+            userRepository.setSelectedUserInfo(USER_1)
+            assertThat(latest).isEqualTo(USER_IMAGE_1)
+
+            userRepository.setSelectedUserInfo(USER_2)
+            assertThat(latest).isEqualTo(USER_IMAGE_2)
+
+            job.cancel()
+        }
+
+    private fun viewModel(): StatusBarUserChipViewModel {
+        return StatusBarUserChipViewModel(
+            context = context,
+            interactor =
+                UserInteractor(
+                    applicationContext = context,
+                    repository = userRepository,
+                    activityStarter = activityStarter,
+                    keyguardInteractor =
+                        KeyguardInteractor(
+                            repository = keyguardRepository,
+                        ),
+                    featureFlags = featureFlags,
+                    manager = manager,
+                    applicationScope = testScope.backgroundScope,
+                    telephonyInteractor =
+                        TelephonyInteractor(
+                            repository = FakeTelephonyRepository(),
+                        ),
+                    broadcastDispatcher = fakeBroadcastDispatcher,
+                    backgroundDispatcher = testDispatcher,
+                    activityManager = activityManager,
+                    refreshUsersScheduler = refreshUsersScheduler,
+                    guestUserInteractor = guestUserInteractor,
+                )
+        )
+    }
+
+    private suspend fun setMultipleUsers() {
+        userRepository.setUserInfos(listOf(USER_0, USER_1, USER_2))
+        userRepository.setSelectedUserInfo(USER_0)
+        userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+    }
+
+    companion object {
+        private const val USER_ID_0 = 0
+        private val USER_IMAGE_0 = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+        private val USER_NAME_0 = Text.Loaded("zero")
+
+        private const val USER_ID_1 = 1
+        private val USER_IMAGE_1 = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+        private val USER_NAME_1 = Text.Loaded("one")
+
+        private const val USER_ID_2 = 2
+        private val USER_IMAGE_2 = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+        private val USER_NAME_2 = Text.Loaded("two")
+
+        private val USER_0 =
+            UserInfo(
+                USER_ID_0,
+                USER_NAME_0.text!!,
+                /* iconPath */ "",
+                /* flags */ 0,
+                /* userType */ UserManager.USER_TYPE_FULL_SYSTEM
+            )
+
+        private val USER_1 =
+            UserInfo(
+                USER_ID_1,
+                USER_NAME_1.text!!,
+                /* iconPath */ "",
+                /* flags */ 0,
+                /* userType */ UserManager.USER_TYPE_FULL_SYSTEM
+            )
+
+        private val USER_2 =
+            UserInfo(
+                USER_ID_2,
+                USER_NAME_2.text!!,
+                /* iconPath */ "",
+                /* flags */ 0,
+                /* userType */ UserManager.USER_TYPE_FULL_SYSTEM
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index db13680..eac7fc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.GuestResumeSessionReceiver
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.Text
+import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
@@ -147,6 +148,7 @@
                                 KeyguardInteractor(
                                     repository = keyguardRepository,
                                 ),
+                            featureFlags = FakeFeatureFlags(),
                             manager = manager,
                             applicationScope = injectedScope,
                             telephonyInteractor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 325da4e..63448e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -28,8 +28,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
@@ -43,7 +41,6 @@
 import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
 import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.security.data.repository.SecurityRepository
 import com.android.systemui.security.data.repository.SecurityRepositoryImpl
 import com.android.systemui.settings.FakeUserTracker
@@ -54,6 +51,7 @@
 import com.android.systemui.statusbar.policy.SecurityController
 import com.android.systemui.statusbar.policy.UserInfoController
 import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.GlobalSettings
@@ -97,13 +95,12 @@
     /** Create a [FooterActionsInteractor] to be used in tests. */
     fun footerActionsInteractor(
         activityStarter: ActivityStarter = mock(),
-        featureFlags: FeatureFlags = FakeFeatureFlags(),
         metricsLogger: MetricsLogger = FakeMetricsLogger(),
         uiEventLogger: UiEventLogger = UiEventLoggerFake(),
         deviceProvisionedController: DeviceProvisionedController = mock(),
         qsSecurityFooterUtils: QSSecurityFooterUtils = mock(),
         fgsManagerController: FgsManagerController = mock(),
-        userSwitchDialogController: UserSwitchDialogController = mock(),
+        userInteractor: UserInteractor = mock(),
         securityRepository: SecurityRepository = securityRepository(),
         foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
         userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
@@ -112,13 +109,12 @@
     ): FooterActionsInteractor {
         return FooterActionsInteractorImpl(
             activityStarter,
-            featureFlags,
             metricsLogger,
             uiEventLogger,
             deviceProvisionedController,
             qsSecurityFooterUtils,
             fgsManagerController,
-            userSwitchDialogController,
+            userInteractor,
             securityRepository,
             foregroundServicesRepository,
             userSwitcherRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index b7c8cbf..ea5a302 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -49,6 +49,8 @@
 
     override val isGuestUserCreationScheduled = AtomicBoolean()
 
+    override var isStatusBarUserChipEnabled: Boolean = false
+
     override var secondaryUserId: Int = UserHandle.USER_NULL
 
     override var isRefreshUsersPaused: Boolean = false
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyFake.java
index 33ece00..21e16a1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyFake.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyFake.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.util;
 
-import android.content.Context;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.OnPropertiesChangedListener;
 import android.provider.DeviceConfig.Properties;
@@ -83,7 +82,7 @@
     }
 
     @Override
-    public void enforceReadPermission(Context context, String namespace) {
+    public void enforceReadPermission(String namespace) {
         // no-op
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 9bce471f..8a22ab9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -837,7 +837,7 @@
     void enableAudioReturnChannel(boolean enabled) {
         assertRunOnServiceThread();
         HdmiDeviceInfo avr = getAvrDeviceInfo();
-        if (avr != null) {
+        if (avr != null && avr.getPortId() != Constants.INVALID_PORT_ID) {
             mService.enableAudioReturnChannel(avr.getPortId(), enabled);
         }
     }
@@ -1336,19 +1336,31 @@
     }
 
     @ServiceThreadOnly
+    private void forceDisableArcOnAllPins() {
+        List<HdmiPortInfo> ports = mService.getPortInfo();
+        for (HdmiPortInfo port : ports) {
+            if (isArcFeatureEnabled(port.getId())) {
+                mService.enableAudioReturnChannel(port.getId(), false);
+            }
+        }
+    }
+
+    @ServiceThreadOnly
     private void disableArcIfExist() {
         assertRunOnServiceThread();
         HdmiDeviceInfo avr = getAvrDeviceInfo();
         if (avr == null) {
             return;
         }
-        disableArc();
 
         // Seq #44.
         removeAllRunningArcAction();
         if (!hasAction(RequestArcTerminationAction.class) && isArcEstablished()) {
             addAndStartAction(new RequestArcTerminationAction(this, avr.getLogicalAddress()));
         }
+
+        // Disable ARC Pin earlier, prevent the case where AVR doesn't send <Terminate ARC> in time
+        forceDisableArcOnAllPins();
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index c97711b..5b837f1 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -81,11 +81,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * Implementation of the methods that update the internal structures of AppsFilter. Because of the
@@ -113,7 +110,7 @@
      */
     @GuardedBy("mQueryableViaUsesPermissionLock")
     @NonNull
-    private HashMap<String, Set<Integer>> mPermissionToUids;
+    private final ArrayMap<String, ArraySet<Integer>> mPermissionToUids;
 
     /**
      * A cache that maps parsed {@link android.R.styleable#AndroidManifestUsesPermission
@@ -123,7 +120,7 @@
      */
     @GuardedBy("mQueryableViaUsesPermissionLock")
     @NonNull
-    private HashMap<String, Set<Integer>> mUsesPermissionToUids;
+    private final ArrayMap<String, ArraySet<Integer>> mUsesPermissionToUids;
 
     /**
      * Ensures an observer is in the list, exactly once. The observer cannot be null.  The
@@ -225,8 +222,8 @@
         mProtectedBroadcasts = new WatchedArraySet<>();
         mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>(
                 mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts");
-        mPermissionToUids = new HashMap<>();
-        mUsesPermissionToUids = new HashMap<>();
+        mPermissionToUids = new ArrayMap<>();
+        mUsesPermissionToUids = new ArrayMap<>();
 
         mSnapshot = new SnapshotCache<AppsFilterSnapshot>(this, this) {
             @Override
@@ -609,7 +606,10 @@
                     // Lookup in the mPermissionToUids cache if installed packages have
                     // defined this permission.
                     if (mPermissionToUids.containsKey(usesPermissionName)) {
-                        for (int targetAppId : mPermissionToUids.get(usesPermissionName)) {
+                        final ArraySet<Integer> permissionDefiners =
+                                mPermissionToUids.get(usesPermissionName);
+                        for (int j = 0; j < permissionDefiners.size(); j++) {
+                            final int targetAppId = permissionDefiners.valueAt(j);
                             if (targetAppId != newPkgSetting.getAppId()) {
                                 mQueryableViaUsesPermission.add(newPkgSetting.getAppId(),
                                         targetAppId);
@@ -619,7 +619,7 @@
                     // Record in mUsesPermissionToUids that a permission was requested
                     // by a new package
                     if (!mUsesPermissionToUids.containsKey(usesPermissionName)) {
-                        mUsesPermissionToUids.put(usesPermissionName, new HashSet<>());
+                        mUsesPermissionToUids.put(usesPermissionName, new ArraySet<>());
                     }
                     mUsesPermissionToUids.get(usesPermissionName).add(newPkgSetting.getAppId());
                 }
@@ -633,7 +633,10 @@
                     // Lookup in the mUsesPermissionToUids cache if installed packages have
                     // requested this permission.
                     if (mUsesPermissionToUids.containsKey(permissionName)) {
-                        for (int queryingAppId : mUsesPermissionToUids.get(permissionName)) {
+                        final ArraySet<Integer> permissionUsers = mUsesPermissionToUids.get(
+                                permissionName);
+                        for (int j = 0; j < permissionUsers.size(); j++) {
+                            final int queryingAppId = permissionUsers.valueAt(j);
                             if (queryingAppId != newPkgSetting.getAppId()) {
                                 mQueryableViaUsesPermission.add(queryingAppId,
                                         newPkgSetting.getAppId());
@@ -642,7 +645,7 @@
                     }
                     // Record in mPermissionToUids that a permission was defined by a new package
                     if (!mPermissionToUids.containsKey(permissionName)) {
-                        mPermissionToUids.put(permissionName, new HashSet<>());
+                        mPermissionToUids.put(permissionName, new ArraySet<>());
                     }
                     mPermissionToUids.get(permissionName).add(newPkgSetting.getAppId());
                 }
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 01a8bd0..4e5a6f9 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageInstaller.SessionParams.USER_ACTION_UNSPECIFIED;
 import static android.content.pm.PackageManager.INSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -114,6 +115,8 @@
 
     @Nullable
     private final PackageMetrics mPackageMetrics;
+    private final int mSessionId;
+    private final int mRequireUserAction;
 
     // New install
     InstallRequest(InstallingSession params) {
@@ -128,6 +131,8 @@
                 params.mDataLoaderType, params.mPackageSource);
         mPackageMetrics = new PackageMetrics(this);
         mIsInstallInherit = params.mIsInherit;
+        mSessionId = params.mSessionId;
+        mRequireUserAction = params.mRequireUserAction;
     }
 
     // Install existing package as user
@@ -141,6 +146,8 @@
         mPostInstallRunnable = runnable;
         mPackageMetrics = new PackageMetrics(this);
         mIsInstallForUsers = true;
+        mSessionId = -1;
+        mRequireUserAction = USER_ACTION_UNSPECIFIED;
     }
 
     // addForInit
@@ -158,6 +165,8 @@
         mScanFlags = scanFlags;
         mScanResult = scanResult;
         mPackageMetrics = null; // No logging from this code path
+        mSessionId = -1;
+        mRequireUserAction = USER_ACTION_UNSPECIFIED;
     }
 
     @Nullable
@@ -565,6 +574,14 @@
         }
     }
 
+    public int getSessionId() {
+        return mSessionId;
+    }
+
+    public int getRequireUserAction() {
+        return mRequireUserAction;
+    }
+
     public void setScanFlags(int scanFlags) {
         mScanFlags = scanFlags;
     }
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index e4a0a3a..d8494db 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -18,6 +18,7 @@
 
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.content.pm.PackageInstaller.SessionParams.MODE_INHERIT_EXISTING;
+import static android.content.pm.PackageInstaller.SessionParams.USER_ACTION_UNSPECIFIED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -95,7 +96,10 @@
     final InstallPackageHelper mInstallPackageHelper;
     final RemovePackageHelper mRemovePackageHelper;
     final boolean mIsInherit;
+    final int mSessionId;
+    final int mRequireUserAction;
 
+    // For move install
     InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
             int installFlags, InstallSource installSource, String volumeUuid,
             UserHandle user, String packageAbiOverride, int packageSource,
@@ -124,9 +128,11 @@
         mPackageSource = packageSource;
         mPackageLite = packageLite;
         mIsInherit = false;
+        mSessionId = -1;
+        mRequireUserAction = USER_ACTION_UNSPECIFIED;
     }
 
-    InstallingSession(File stagedDir, IPackageInstallObserver2 observer,
+    InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer,
             PackageInstaller.SessionParams sessionParams, InstallSource installSource,
             UserHandle user, SigningDetails signingDetails, int installerUid,
             PackageLite packageLite, PackageManagerService pm) {
@@ -155,6 +161,8 @@
         mPackageSource = sessionParams.packageSource;
         mPackageLite = packageLite;
         mIsInherit = sessionParams.mode == MODE_INHERIT_EXISTING;
+        mSessionId = sessionId;
+        mRequireUserAction = sessionParams.requireUserAction;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3cc1907..a2b462a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2566,8 +2566,8 @@
         }
 
         synchronized (mLock) {
-            return new InstallingSession(stageDir, localObserver, params, mInstallSource, user,
-                    mSigningDetails, mInstallerUid, mPackageLite, mPm);
+            return new InstallingSession(sessionId, stageDir, localObserver, params, mInstallSource,
+                    user, mSigningDetails, mInstallerUid, mPackageLite, mPm);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index cb87ff5..0574f73 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -92,7 +92,7 @@
         final long apksSize = getApksSize(ps.getPath());
 
         FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLATION_SESSION_REPORTED,
-                0 /* session_id */,
+                mInstallRequest.getSessionId() /* session_id */,
                 success ? null : packageName /* not report package_name on success */,
                 mInstallRequest.getUid() /* uid */,
                 newUsers /* user_ids */,
@@ -110,7 +110,7 @@
                 installerUid /* installer_package_uid */,
                 -1 /* original_installer_package_uid */,
                 mInstallRequest.getDataLoaderType() /* data_loader_type */,
-                0 /* user_action_required_type */,
+                mInstallRequest.getRequireUserAction() /* user_action_required_type */,
                 mInstallRequest.isInstantInstall() /* is_instant */,
                 mInstallRequest.isInstallReplace() /* is_replace */,
                 mInstallRequest.isInstallSystem() /* is_system */,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 54baf18..82c3401 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -849,4 +849,53 @@
         verify(mAudioManager, never()).setStreamVolume(eq(AudioManager.STREAM_MUSIC), anyInt(),
                 anyInt());
     }
+
+    @Test
+    public void tvSendRequestArcTerminationOnSleep() {
+        // Emulate Audio device on port 0x2000 (supports ARC)
+
+        mNativeWrapper.setPortConnectionStatus(2, true);
+        HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        mNativeWrapper.onCecMessage(hdmiCecMessage);
+        mTestLooper.dispatchAll();
+
+        mHdmiCecLocalDeviceTv.startArcAction(true);
+        mTestLooper.dispatchAll();
+        HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildRequestArcInitiation(
+                ADDR_TV,
+                ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage requestArcTermination = HdmiCecMessageBuilder.buildRequestArcTermination(
+                ADDR_TV,
+                ADDR_AUDIO_SYSTEM);
+        HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+                ADDR_AUDIO_SYSTEM,
+                ADDR_TV);
+        HdmiCecMessage reportArcInitiated = HdmiCecMessageBuilder.buildReportArcInitiated(
+                ADDR_TV,
+                ADDR_AUDIO_SYSTEM);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestArcInitiation);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestArcTermination);
+
+        mNativeWrapper.onCecMessage(initiateArc);
+        mTestLooper.dispatchAll();
+
+        // Finish querying SADs
+        assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // ARC should be established after RequestSadAction is finished
+        assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
+
+        mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestArcTermination);
+    }
+
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 73aceba..8c3ef67 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9540,12 +9540,13 @@
     /**
      * Set the allowed network types of the device and provide the reason triggering the allowed
      * network change.
-     * <p>Requires permission: android.Manifest.MODIFY_PHONE_STATE or
+     * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
      * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
-     * This can be called for following reasons
+     * This can be called for following reasons:
      * <ol>
-     * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
+     * <li>Allowed network types control by USER
+     * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER}
      * <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}
      * </ol>
      * This API will result in allowing an intersection of allowed network types for all reasons,
@@ -9555,7 +9556,13 @@
      * @param allowedNetworkTypes The bitmask of allowed network type
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
-     * @throws SecurityException if the caller does not have the required privileges
+     * @throws SecurityException if the caller does not have the required privileges or if the
+     * caller tries to use one of the following security-based reasons without
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions.
+     * <ol>
+     *     <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}</li>
+     *     <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS}</li>
+     * </ol>
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(