Merge "Temporarily disable AppOpsServiceTest#testShutdown, remove unused imports" into sc-dev
diff --git a/Android.bp b/Android.bp
index 77044d6..f9320eb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -510,15 +510,6 @@
     ],
 }
 
-filegroup {
-    name: "framework-all-sources",
-    srcs: [
-        ":framework-mime-sources",
-        ":framework-non-updatable-sources",
-        ":framework-updatable-sources",
-    ],
-}
-
 // AIDL files under these paths are mixture of public and private ones.
 // They shouldn't be exported across module boundaries.
 java_defaults {
@@ -1391,14 +1382,6 @@
     ],
 }
 
-// Creates an index of AIDL methods; used for adding UnsupportedAppUsage
-// annotations to private apis
-aidl_mapping {
-    name: "framework-aidl-mappings",
-    srcs: [":framework-all-sources"],
-    output: "framework-aidl-mappings.txt",
-}
-
 // Avoid including Parcelable classes as we don't want to have two copies of
 // Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
 // and TeleService app (packages/services/Telephony).
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 547aa16..4dd1b79 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -587,8 +587,28 @@
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
-        // TODO(b/182909475): Implement getStorageInfo
-        throw new UnsupportedOperationException();
+        try {
+            mService.getStorageInfo(
+                    mPackageName,
+                    mDatabaseName,
+                    mUserId,
+                    new IAppSearchResultCallback.Stub() {
+                        public void onResult(AppSearchResult result) {
+                            executor.execute(() -> {
+                                if (result.isSuccess()) {
+                                    Bundle responseBundle = (Bundle) result.getResultValue();
+                                    StorageInfo response =
+                                            new StorageInfo(responseBundle);
+                                    callback.accept(AppSearchResult.newSuccessfulResult(response));
+                                } else {
+                                    callback.accept(result);
+                                }
+                            });
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 48c397f..a8ac27c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -307,6 +307,22 @@
         in IAppSearchResultCallback callback);
 
     /**
+     * Gets the storage info.
+     *
+     * @param packageName The name of the package to get the storage info for.
+     * @param databaseName The databaseName to get the storage info for.
+     * @param userId Id of the calling user
+     * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+     *     {@link AppSearchResult}<{@link Bundle}>, where the value is a
+     *     {@link StorageInfo}.
+     */
+    void getStorageInfo(
+        in String packageName,
+        in String databaseName,
+        in int userId,
+        in IAppSearchResultCallback callback);
+
+    /**
      * Persists all update/delete requests to the disk.
      *
      * @param userId Id of the calling user
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 91ed6cd..991dda7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -33,6 +33,7 @@
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.StorageInfo;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
@@ -610,6 +611,34 @@
         }
 
         @Override
+        public void getStorageInfo(
+                @NonNull String packageName,
+                @NonNull String databaseName,
+                @UserIdInt int userId,
+                @NonNull IAppSearchResultCallback callback) {
+            Preconditions.checkNotNull(packageName);
+            Preconditions.checkNotNull(databaseName);
+            Preconditions.checkNotNull(callback);
+            int callingUid = Binder.getCallingUid();
+            int callingUserId = handleIncomingUser(userId, callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                verifyUserUnlocked(callingUserId);
+                verifyCallingPackage(callingUid, packageName);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(callingUserId);
+                StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, databaseName);
+                Bundle storageInfoBundle = storageInfo.getBundle();
+                invokeCallbackOnResult(
+                        callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
+            } catch (Throwable t) {
+                invokeCallbackOnError(callback, t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
         public void persistToDisk(@UserIdInt int userId) {
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = handleIncomingUser(userId, callingUid);
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index ad68169..8d83309 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,9 @@
   }
 
   public static final class MediaTranscodeManager.TranscodingSession {
+    method public void addClientUid(int);
     method public void cancel();
+    method @NonNull public java.util.List<java.lang.Integer> getClientUids();
     method public int getErrorCode();
     method @IntRange(from=0, to=100) public int getProgress();
     method public int getResult();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index 7f4685e..fef4865 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1352,6 +1352,8 @@
         private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
         @GuardedBy("mLock")
         private boolean mHasRetried = false;
+        @GuardedBy("mLock")
+        private @NonNull List<Integer> mClientUidList = new ArrayList<>();
         // The original request that associated with this session.
         private final TranscodingRequest mRequest;
 
@@ -1370,6 +1372,7 @@
             mListenerExecutor = executor;
             mListener = listener;
             mRequest = request;
+            mClientUidList.add(request.getClientUid());
         }
 
         /**
@@ -1515,6 +1518,36 @@
         }
 
         /**
+         * Adds a client uid that is also waiting for this transcoding session.
+         * <p>
+         * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
+         * uid. Note that the permission check happens on the service side upon starting the
+         * transcoding. If the client does not have the permission, the transcoding will fail.
+         */
+        public void addClientUid(int uid) {
+            if (uid < 0) {
+                throw new IllegalArgumentException("Invalid Uid");
+            }
+            synchronized (mLock) {
+                if (!mClientUidList.contains(uid)) {
+                    // see ag/14023202 for implementation
+                    mClientUidList.add(uid);
+                }
+            }
+        }
+
+        /**
+         * Query all the client that waiting for this transcoding session
+         * @return a list containing all the client uids.
+         */
+        @NonNull
+        public List<Integer> getClientUids() {
+            synchronized (mLock) {
+                return mClientUidList;
+            }
+        }
+
+        /**
          * Gets sessionId of the transcoding session.
          * @return session id.
          */
diff --git a/core/api/current.txt b/core/api/current.txt
index fb32055..f8654478 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18842,9 +18842,14 @@
     method public android.view.Display getDisplay(int);
     method public android.view.Display[] getDisplays();
     method public android.view.Display[] getDisplays(String);
+    method public int getMatchContentFrameRateUserPreference();
     method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+    field public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2; // 0x2
+    field public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0; // 0x0
+    field public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1; // 0x1
+    field public static final int MATCH_CONTENT_FRAMERATE_UNKNOWN = -1; // 0xffffffff
     field public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 16; // 0x10
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 8; // 0x8
     field public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 2; // 0x2
@@ -51545,6 +51550,7 @@
     field public static final int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5; // 0xfffffffb
     field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
     field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
+    field public static final int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6; // 0xfffffffa
     field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
   }
 
@@ -52775,11 +52781,16 @@
   }
 
   public final class TranslationManager {
-    method public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
-    method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
-    method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int);
-    method @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent();
-    method public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+    method public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+    method @Deprecated public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+    method @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext);
+    method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext);
+    method @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getOnDeviceTranslationCapabilities(int, int);
+    method @Nullable public android.app.PendingIntent getOnDeviceTranslationSettingsActivityIntent();
+    method @Deprecated @NonNull @WorkerThread public java.util.Set<android.view.translation.TranslationCapability> getTranslationCapabilities(int, int);
+    method @Deprecated @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent();
+    method public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
+    method @Deprecated public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent);
   }
 
   public final class TranslationRequest implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a9413f5..d0f9094 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -180,6 +180,7 @@
     field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
     field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
+    field public static final String NFC_SET_CONTROLLER_ALWAYS_ON = "android.permission.NFC_SET_CONTROLLER_ALWAYS_ON";
     field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
     field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -320,6 +321,7 @@
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
     field public static final int sdkVersion = 16844304; // 0x1010610
     field public static final int supportsAmbientMode = 16844173; // 0x101058d
+    field public static final int throttleDurationMillis = 16844360; // 0x1010648
     field public static final int userRestriction = 16844164; // 0x1010584
   }
 
@@ -7906,10 +7908,10 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnEnabled();
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnSupported();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setAlwaysOn(boolean);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
     field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
   }
@@ -9793,6 +9795,7 @@
     method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
     method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
     field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
+    field public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
   }
 
 }
@@ -13668,13 +13671,13 @@
     ctor @Deprecated public RcsFeature();
     ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
     method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
-    method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+    method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+    method public void destroyCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
     method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
     method public boolean queryCapabilityConfiguration(int, int);
     method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
-    method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
   }
 
   public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
@@ -13887,7 +13890,7 @@
   }
 
   public class RcsCapabilityExchangeImplBase {
-    ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
+    ctor public RcsCapabilityExchangeImplBase();
     method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
     method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
     method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 561d838d..56b0a9d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1155,7 +1155,6 @@
 
   public final class DisplayManager {
     method public boolean areUserDisabledHdrTypesAllowed();
-    method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType();
     method @NonNull public int[] getUserDisabledHdrTypes();
     method public boolean isMinimalPostProcessingRequested(int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
@@ -1657,6 +1656,7 @@
     field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8
     field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7
     field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f
+    field public static final int NFC_UID = 1027; // 0x403
     field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64
   }
 
@@ -2877,6 +2877,14 @@
 
 }
 
+package android.view.displayhash {
+
+  public final class DisplayHashManager {
+    method @RequiresPermission("android.permission.READ_FRAME_BUFFER") public void setDisplayHashThrottlingEnabled(boolean);
+  }
+
+}
+
 package android.view.inputmethod {
 
   public final class InlineSuggestion implements android.os.Parcelable {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d47c3ca..2fbea28 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5360,6 +5360,11 @@
             // Use different highlighted colors except when low-priority mode prevents that
             if (!p.mReduceHighlights) {
                 pillColor = getAccentTertiaryColor(p);
+                // TODO(b/183710694): The accent tertiary is currently too bright in dark mode, so
+                //  we need to pick a contrasting color.
+                textColor = ColorUtils.setAlphaComponent(
+                        ContrastColorUtil.resolvePrimaryColor(mContext, pillColor, mInNightMode),
+                        0xFF);
             }
             contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 216e7b0..6c2d140 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -38,6 +38,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.util.Pair;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.Surface;
@@ -348,6 +349,37 @@
 
 
     /** @hide */
+    @IntDef(prefix = {"MATCH_CONTENT_FRAMERATE_"}, value = {
+            MATCH_CONTENT_FRAMERATE_UNKNOWN,
+            MATCH_CONTENT_FRAMERATE_NEVER,
+            MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY,
+            MATCH_CONTENT_FRAMERATE_ALWAYS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MatchContentFrameRateType {}
+
+    /**
+     * Match content frame rate user preference is unknown.
+     */
+    public static final int MATCH_CONTENT_FRAMERATE_UNKNOWN = -1;
+
+    /**
+     * No mode switching is allowed.
+     */
+    public static final int MATCH_CONTENT_FRAMERATE_NEVER = 0;
+
+    /**
+     * Only refresh rate switches without visual interruptions are allowed.
+     */
+    public static final int MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY = 1;
+
+    /**
+     * Refresh rate switches between all refresh rates are allowed even if they have visual
+     * interruptions for the user.
+     */
+    public static final int MATCH_CONTENT_FRAMERATE_ALWAYS = 2;
+
+    /** @hide */
     @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
             SWITCHING_TYPE_NONE,
             SWITCHING_TYPE_WITHIN_GROUPS,
@@ -1076,14 +1108,36 @@
     }
 
     /**
-     * Returns the refresh rate switching type.
-     *
-     * @hide
+     * Returns the user preference for "Match content frame rate".
+     * <p>
+     * Never: Even if the app requests it, the device will never try to match its output to the
+     * original frame rate of the content.
+     * </p><p>
+     * Seamless: If the app requests it, the device will match its output to the original frame
+     * rate of the content, ONLY if the display can transition seamlessly.
+     * </p><p>
+     * Always: If the app requests it, the device will match its output to the original
+     * frame rate of the content. This may cause the screen to go blank for a
+     * second when exiting or entering a video playback.
+     * </p>
      */
-    @TestApi
-    @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
-    @SwitchingType public int getRefreshRateSwitchingType() {
-        return mGlobal.getRefreshRateSwitchingType();
+    @MatchContentFrameRateType public int getMatchContentFrameRateUserPreference() {
+        return toMatchContentFrameRateSetting(mGlobal.getRefreshRateSwitchingType());
+    }
+
+    @MatchContentFrameRateType
+    private int toMatchContentFrameRateSetting(@SwitchingType int switchingType) {
+        switch (switchingType) {
+            case SWITCHING_TYPE_NONE:
+                return MATCH_CONTENT_FRAMERATE_NEVER;
+            case SWITCHING_TYPE_WITHIN_GROUPS:
+                return MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY;
+            case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
+                return MATCH_CONTENT_FRAMERATE_ALWAYS;
+            default:
+                Slog.e(TAG, switchingType + " is not a valid value of switching type.");
+                return MATCH_CONTENT_FRAMERATE_UNKNOWN;
+        }
     }
 
     /**
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index bc3d5c4..11445e9 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -72,7 +72,7 @@
     boolean deviceSupportsNfcSecure();
     boolean setNfcSecure(boolean enable);
 
-    boolean setAlwaysOn(boolean value);
-    boolean isAlwaysOnEnabled();
-    boolean isAlwaysOnSupported();
+    boolean setControllerAlwaysOn(boolean value);
+    boolean isControllerAlwaysOn();
+    boolean isControllerAlwaysOnSupported();
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index e85eb93..eed2c77 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -2254,13 +2254,13 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-    public boolean setAlwaysOn(boolean value) {
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public boolean setControllerAlwaysOn(boolean value) {
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
         try {
-            return sService.setAlwaysOn(value);
+            return sService.setControllerAlwaysOn(value);
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             // Try one more time
@@ -2269,7 +2269,7 @@
                 return false;
             }
             try {
-                return sService.setAlwaysOn(value);
+                return sService.setControllerAlwaysOn(value);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover NFC Service.");
             }
@@ -2286,10 +2286,10 @@
      */
 
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-    public boolean isAlwaysOnEnabled() {
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public boolean isControllerAlwaysOn() {
         try {
-            return sService.isAlwaysOnEnabled();
+            return sService.isControllerAlwaysOn();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             // Try one more time
@@ -2298,7 +2298,7 @@
                 return false;
             }
             try {
-                return sService.isAlwaysOnEnabled();
+                return sService.isControllerAlwaysOn();
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover NFC Service.");
             }
@@ -2315,13 +2315,13 @@
      */
 
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-    public boolean isAlwaysOnSupported() {
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public boolean isControllerAlwaysOnSupported() {
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
         try {
-            return sService.isAlwaysOnSupported();
+            return sService.isControllerAlwaysOnSupported();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             // Try one more time
@@ -2330,7 +2330,7 @@
                 return false;
             }
             try {
-                return sService.isAlwaysOnSupported();
+                return sService.isControllerAlwaysOnSupported();
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover NFC Service.");
             }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index bf859d4..f9e4f73 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -131,6 +131,7 @@
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TestApi
     public static final int NFC_UID = 1027;
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 12ff640..22d74ca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9176,6 +9176,22 @@
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
         /**
+         * Whether the assistant can be triggered by a touch gesture.
+         *
+         * @hide
+         */
+        public static final String ASSIST_TOUCH_GESTURE_ENABLED =
+                "assist_touch_gesture_enabled";
+
+        /**
+         * Whether the assistant can be triggered by long-pressing the home button
+         *
+         * @hide
+         */
+        public static final String ASSIST_LONG_PRESS_HOME_ENABLED =
+                "assist_long_press_home_enabled";
+
+        /**
          * Control whether Trust Agents are in active unlock or extend unlock mode.
          * @hide
          */
@@ -9740,6 +9756,12 @@
                 "accessibility_magnification_mode";
 
         /**
+         * Magnification mode value that is a default value for the magnification logging feature.
+         * @hide
+         */
+        public static final int ACCESSIBILITY_MAGNIFICATION_MODE_NONE = 0x0;
+
+        /**
          * Magnification mode value that magnifies whole display.
          * @hide
          */
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java
index 2105d84..d300cf1 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHasherService.java
@@ -52,6 +52,16 @@
             "android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
 
     /**
+     * Name under which a DisplayHasherService component publishes information
+     * about itself.  This meta-data must reference an XML resource containing a
+     * {@link com.android.internal.R.styleable#DisplayHasherService} tag.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
+
+    /**
      * The {@link Intent} action that must be declared as handled by a service in its manifest
      * for the system to recognize it as a DisplayHash providing service.
      *
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 572e50a..913ceae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -70,7 +70,7 @@
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
         DEFAULT_FLAGS.put("settings_silky_home", "true");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
-        DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
+        DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
         DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "false");
     }
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index 547bc9d..b55d9b3 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -26,6 +26,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Pair;
+import android.view.inputmethod.InputContentInfo;
 
 import com.android.internal.util.Preconditions;
 
@@ -141,6 +142,10 @@
     private final Uri mLinkUri;
     @Nullable
     private final Bundle mExtras;
+    @Nullable
+    private final InputContentInfo mInputContentInfo;
+    @Nullable
+    private final DragAndDropPermissions mDragAndDropPermissions;
 
     private ContentInfo(Builder b) {
         this.mClip = Objects.requireNonNull(b.mClip);
@@ -149,6 +154,23 @@
         this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
         this.mLinkUri = b.mLinkUri;
         this.mExtras = b.mExtras;
+        this.mInputContentInfo = b.mInputContentInfo;
+        this.mDragAndDropPermissions = b.mDragAndDropPermissions;
+    }
+
+    /**
+     * If the content came from a source that supports proactive release of URI permissions
+     * (e.g. IME), releases permissions; otherwise a no-op.
+     *
+     * @hide
+     */
+    public void releasePermissions() {
+        if (mInputContentInfo != null) {
+            mInputContentInfo.releasePermission();
+        }
+        if (mDragAndDropPermissions != null) {
+            mDragAndDropPermissions.release();
+        }
     }
 
     @NonNull
@@ -275,6 +297,10 @@
         private Uri mLinkUri;
         @Nullable
         private Bundle mExtras;
+        @Nullable
+        private InputContentInfo mInputContentInfo;
+        @Nullable
+        private DragAndDropPermissions mDragAndDropPermissions;
 
         /**
          * Creates a new builder initialized with the data from the given builder.
@@ -285,6 +311,8 @@
             mFlags = other.mFlags;
             mLinkUri = other.mLinkUri;
             mExtras = other.mExtras;
+            mInputContentInfo = other.mInputContentInfo;
+            mDragAndDropPermissions = other.mDragAndDropPermissions;
         }
 
         /**
@@ -355,6 +383,31 @@
         }
 
         /**
+         * Set the {@link InputContentInfo} object if the content is coming from the IME. This can
+         * be used for proactive cleanup of permissions.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setInputContentInfo(@Nullable InputContentInfo inputContentInfo) {
+            mInputContentInfo = inputContentInfo;
+            return this;
+        }
+
+        /**
+         * Set the {@link DragAndDropPermissions} object if the content is coming via drag-and-drop.
+         * This can be used for proactive cleanup of permissions.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setDragAndDropPermissions(@Nullable DragAndDropPermissions permissions) {
+            mDragAndDropPermissions = permissions;
+            return this;
+        }
+
+
+        /**
          * @return A new {@link ContentInfo} instance with the data from this builder.
          */
         @NonNull
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e1f13f2..d0a3e4b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -777,6 +777,15 @@
     VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash);
 
     /**
+     * Call to enable or disable the throttling when generating a display hash. This should only be
+     * used for testing. Throttling is enabled by default.
+     *
+     * Must be called from a process that has {@link android.Manifest.permission#READ_FRAME_BUFFER}
+     * permission.
+     */
+     void setDisplayHashThrottlingEnabled(boolean enable);
+
+    /**
      * Registers a listener for a {@link android.window.WindowContext} to handle configuration
      * changes from the server side.
      * <p>
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 8db6456..0686104 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -51,8 +51,8 @@
     }
 
     @Override
-    public void onWindowFocusGained() {
-        super.onWindowFocusGained();
+    public void onWindowFocusGained(boolean hasViewFocus) {
+        super.onWindowFocusGained(hasViewFocus);
         getImm().registerImeConsumer(this);
     }
 
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a68f528..c001ec9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1302,8 +1302,8 @@
     /**
      * Called when current window gains focus.
      */
-    public void onWindowFocusGained() {
-        getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+    public void onWindowFocusGained(boolean hasViewFocused) {
+        getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused);
     }
 
     /**
@@ -1366,8 +1366,9 @@
             final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
             // Skip showing animation once that made by system for some reason.
             // (e.g. starting window with IME snapshot)
-            if (imeControl != null && show) {
-                skipAnim = imeControl.getAndClearSkipAnimationOnce();
+            if (imeControl != null) {
+                skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
+                        && consumer.hasViewFocusWhenWindowFocusGain();
             }
         }
         applyAnimation(types, show, fromIme, skipAnim);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index fd1c3b8..bc50dbe 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -81,6 +81,11 @@
     private final Supplier<Transaction> mTransactionSupplier;
     private @Nullable InsetsSourceControl mSourceControl;
     private boolean mHasWindowFocus;
+
+    /**
+     * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}.
+     */
+    private boolean mHasViewFocusWhenWindowFocusGain;
     private Rect mPendingFrame;
     private Rect mPendingVisibleFrame;
 
@@ -223,8 +228,9 @@
     /**
      * Called when current window gains focus
      */
-    public void onWindowFocusGained() {
+    public void onWindowFocusGained(boolean hasViewFocus) {
         mHasWindowFocus = true;
+        mHasViewFocusWhenWindowFocusGain = hasViewFocus;
     }
 
     /**
@@ -238,6 +244,10 @@
         return mHasWindowFocus;
     }
 
+    boolean hasViewFocusWhenWindowFocusGain() {
+        return mHasViewFocusWhenWindowFocusGain;
+    }
+
     boolean applyLocalVisibilityOverride() {
         final InsetsSource source = mState.peekSource(mType);
         final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
diff --git a/core/java/android/view/RoundedCorner.java b/core/java/android/view/RoundedCorner.java
index 56b4383..d10fc44 100644
--- a/core/java/android/view/RoundedCorner.java
+++ b/core/java/android/view/RoundedCorner.java
@@ -29,16 +29,10 @@
 
 /**
  * Represents a rounded corner of the display.
- *
- * <code>
- *       ________
- *     /        ^
- *    /         | Radius
- *    |         v
- *    |       X <- Center point
- *    |<----->
- *     Radius
- * </code>
+ * <p>
+ * <img src="{@docRoot}reference/android/images/rounded_corner/rounded-corner-info.png" height="120"
+ * alt="A figure to describe what the rounded corner radius and the center point are. "/>
+ * </p>
  *
  * <p>Note: The rounded corner formed by the radius and the center is an approximation.</p>
  *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 615dd82..63a7dea 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -26801,8 +26801,10 @@
             if (permissions != null) {
                 permissions.takeTransient();
             }
-            final ContentInfo payload = new ContentInfo.Builder(
-                    event.getClipData(), SOURCE_DRAG_AND_DROP).build();
+            final ContentInfo payload =
+                    new ContentInfo.Builder(event.getClipData(), SOURCE_DRAG_AND_DROP)
+                            .setDragAndDropPermissions(permissions)
+                            .build();
             ContentInfo remainingPayload = performReceiveContent(payload);
             // Return true unless none of the payload was consumed.
             return remainingPayload != payload;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0a246a6..426c950 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3344,8 +3344,9 @@
         }
         // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
         // config changes.
+        final View focusedView = mView != null ? mView.findFocus() : null;
         if (hasWindowFocus) {
-            mInsetsController.onWindowFocusGained();
+            mInsetsController.onWindowFocusGained(focusedView != null /* hasViewFocused */);
         } else {
             mInsetsController.onWindowFocusLost();
         }
@@ -3394,8 +3395,7 @@
 
             // Note: must be done after the focus change callbacks,
             // so all of the view state is set up correctly.
-            mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null,
-                    hasWindowFocus, mWindowAttributes);
+            mImeFocusController.onPostWindowFocus(focusedView, hasWindowFocus, mWindowAttributes);
 
             if (hasWindowFocus) {
                 // Clear the forward bit.  We can just do this directly, since
diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java
index 6b0c1a6..69dfc38 100644
--- a/core/java/android/view/displayhash/DisplayHashManager.java
+++ b/core/java/android/view/displayhash/DisplayHashManager.java
@@ -21,7 +21,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Log;
@@ -94,4 +96,19 @@
             return null;
         }
     }
+
+    /**
+     * Call to enable or disable the throttling when generating a display hash. This should only be
+     * used for testing. Throttling is enabled by default.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
+    public void setDisplayHashThrottlingEnabled(boolean enable) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().setDisplayHashThrottlingEnabled(enable);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 04d29ee..6e3d9a8 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -72,13 +72,20 @@
      */
     int DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM = -5;
 
+    /**
+     * The caller requested to generate the hash too frequently. The caller should try again at a
+     * after some time has passed to ensure the system isn't overloaded.
+     */
+    int DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS = -6;
+
     /** @hide */
     @IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
             DISPLAY_HASH_ERROR_UNKNOWN,
             DISPLAY_HASH_ERROR_INVALID_BOUNDS,
             DISPLAY_HASH_ERROR_MISSING_WINDOW,
             DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN,
-            DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM
+            DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM,
+            DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface DisplayHashErrorCode {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index c5bce28..9f796c0 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -959,10 +959,10 @@
         }
         final ClipData clip = new ClipData(inputContentInfo.getDescription(),
                 new ClipData.Item(inputContentInfo.getContentUri()));
-        final ContentInfo payload =
-                new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
+        final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
                 .setLinkUri(inputContentInfo.getLinkUri())
                 .setExtras(opts)
+                .setInputContentInfo(inputContentInfo)
                 .build();
         return mTargetView.performReceiveContent(payload) == null;
     }
diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java
index dfa7095..b89488b 100644
--- a/core/java/android/view/translation/TranslationManager.java
+++ b/core/java/android/view/translation/TranslationManager.java
@@ -121,17 +121,16 @@
     }
 
     /**
-     * Create a Translator for translation.
+     * Creates an on-device Translator for natural language translation.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
      * @param translationContext {@link TranslationContext} containing the specs for creating the
      *                                                     Translator.
-     * @return a {@link Translator} to be used for calling translation APIs.
      */
     @Nullable
     @WorkerThread
-    public Translator createTranslator(@NonNull TranslationContext translationContext) {
+    public Translator createOnDeviceTranslator(@NonNull TranslationContext translationContext) {
         Objects.requireNonNull(translationContext, "translationContext cannot be null");
 
         synchronized (mLock) {
@@ -166,8 +165,16 @@
         }
     }
 
+    /** @deprecated Use {@link #createOnDeviceTranslator(TranslationContext)} */
+    @Deprecated
+    @Nullable
+    @WorkerThread
+    public Translator createTranslator(@NonNull TranslationContext translationContext) {
+        return createOnDeviceTranslator(translationContext);
+    }
+
     /**
-     * Returns a set of {@link TranslationCapability}s describing the capabilities for
+     * Returns a set of {@link TranslationCapability}s describing the capabilities for on-device
      * {@link Translator}s.
      *
      * <p>These translation capabilities contains a source and target {@link TranslationSpec}
@@ -184,7 +191,7 @@
      */
     @NonNull
     @WorkerThread
-    public Set<TranslationCapability> getTranslationCapabilities(
+    public Set<TranslationCapability> getOnDeviceTranslationCapabilities(
             @TranslationSpec.DataFormat int sourceFormat,
             @TranslationSpec.DataFormat int targetFormat) {
         try {
@@ -206,8 +213,18 @@
         }
     }
 
+    /** @deprecated Use {@link #getOnDeviceTranslationCapabilities(int, int)} */
+    @Deprecated
+    @NonNull
+    @WorkerThread
+    public Set<TranslationCapability> getTranslationCapabilities(
+            @TranslationSpec.DataFormat int sourceFormat,
+            @TranslationSpec.DataFormat int targetFormat) {
+        return getOnDeviceTranslationCapabilities(sourceFormat, targetFormat);
+    }
+
     /**
-     * Registers a {@link PendingIntent} to listen for updates on states of
+     * Registers a {@link PendingIntent} to listen for updates on states of on-device
      * {@link TranslationCapability}s.
      *
      * <p>IMPORTANT: the pending intent must be called to start a service, or a broadcast if it is
@@ -217,7 +234,7 @@
      * @param targetFormat data format for the expected translated output data.
      * @param pendingIntent the pending intent to invoke when updates are received.
      */
-    public void addTranslationCapabilityUpdateListener(
+    public void addOnDeviceTranslationCapabilityUpdateListener(
             @TranslationSpec.DataFormat int sourceFormat,
             @TranslationSpec.DataFormat int targetFormat,
             @NonNull PendingIntent pendingIntent) {
@@ -231,14 +248,26 @@
     }
 
     /**
-     * Unregisters a {@link PendingIntent} to listen for updates on states of
+     * @deprecated Use {@link #addOnDeviceTranslationCapabilityUpdateListener(int, int,
+     *  PendingIntent)}
+     */
+    @Deprecated
+    public void addTranslationCapabilityUpdateListener(
+            @TranslationSpec.DataFormat int sourceFormat,
+            @TranslationSpec.DataFormat int targetFormat,
+            @NonNull PendingIntent pendingIntent) {
+        addOnDeviceTranslationCapabilityUpdateListener(sourceFormat, targetFormat, pendingIntent);
+    }
+
+    /**
+     * Unregisters a {@link PendingIntent} to listen for updates on states of on-device
      * {@link TranslationCapability}s.
      *
      * @param sourceFormat data format for the input data to be translated.
      * @param targetFormat data format for the expected translated output data.
      * @param pendingIntent the pending intent to unregister
      */
-    public void removeTranslationCapabilityUpdateListener(
+    public void removeOnDeviceTranslationCapabilityUpdateListener(
             @TranslationSpec.DataFormat int sourceFormat,
             @TranslationSpec.DataFormat int targetFormat,
             @NonNull PendingIntent pendingIntent) {
@@ -262,10 +291,24 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(int, int,
+     *  PendingIntent)}
+     */
+    @Deprecated
+    public void removeTranslationCapabilityUpdateListener(
+            @TranslationSpec.DataFormat int sourceFormat,
+            @TranslationSpec.DataFormat int targetFormat,
+            @NonNull PendingIntent pendingIntent) {
+        removeOnDeviceTranslationCapabilityUpdateListener(
+                sourceFormat, targetFormat, pendingIntent);
+    }
+
     //TODO: Add method to propagate updates to mTCapabilityUpdateListeners
 
     /**
-     * Returns an immutable PendingIntent which can used by apps to launch translation settings.
+     * Returns an immutable PendingIntent which can be used to launch an activity to view/edit
+     * on-device translation settings.
      *
      * @return An immutable PendingIntent or {@code null} if one of reason met:
      * <ul>
@@ -274,7 +317,7 @@
      * </ul>
      **/
     @Nullable
-    public PendingIntent getTranslationSettingsActivityIntent() {
+    public PendingIntent getOnDeviceTranslationSettingsActivityIntent() {
         final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         try {
             mService.getServiceSettingsActivity(resultReceiver, mContext.getUserId());
@@ -289,6 +332,13 @@
         }
     }
 
+    /** @deprecated Use {@link #getOnDeviceTranslationSettingsActivityIntent()} */
+    @Deprecated
+    @Nullable
+    public PendingIntent getTranslationSettingsActivityIntent() {
+        return getOnDeviceTranslationSettingsActivityIntent();
+    }
+
     void removeTranslator(int id) {
         synchronized (mLock) {
             mTranslators.remove(id);
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 6ba40c3..0fa6e16 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -254,7 +254,7 @@
                 viewsResult.put(translatedResult.keyAt(i), result);
             } else {
                 final boolean isVirtualViewAdded =
-                        virtualViewsResult.indexOfKey(autofillId.getViewId()) > 0;
+                        virtualViewsResult.indexOfKey(autofillId.getViewId()) >= 0;
                 final LongSparseArray<ViewTranslationResponse> childIds =
                         isVirtualViewAdded ? virtualViewsResult.get(autofillId.getViewId())
                                 : new LongSparseArray<>();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 238ce85..48e25c51 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2894,8 +2894,8 @@
             final int originalLength = mTextView.getText().length();
             Selection.setSelection((Spannable) mTextView.getText(), offset);
             final ClipData clip = event.getClipData();
-            final ContentInfo payload =
-                    new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+            final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
+                    .setDragAndDropPermissions(permissions)
                     .build();
             mTextView.performReceiveContent(payload);
             if (dragDropIntoItself) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index cb2bba1..29c78b5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6342,6 +6342,9 @@
     /**
      * Set the ID of the top-level view of the XML layout.
      *
+     * The view's ID is changed right after inflation, before it gets added to its parent. The ID
+     * of a given view can never change after the initial inflation.
+     *
      * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
      *
      * @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 7baa53b..a600a94 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -133,6 +133,17 @@
                 duration);
     }
 
+    /**
+     * Logs the activated mode of the magnification when the IME window is shown on the screen.
+     * Calls this when the magnification is enabled and the IME window is shown on the screen.
+     *
+     * @param mode The activated magnification mode.
+     */
+    public static void logMagnificationModeWithImeOn(int mode) {
+        FrameworkStatsLog.write(FrameworkStatsLog.MAGNIFICATION_MODE_WITH_IME_ON_REPORTED,
+                convertToLoggingMagnificationMode(mode));
+    }
+
     private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) {
         switch (shortcutType) {
             case ACCESSIBILITY_BUTTON:
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index f0badbe..530cb44 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -123,6 +123,8 @@
         optional SettingProto gesture_silence_alerts_enabled = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto gesture_wake_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Assist assist = 7;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 994fe6b..00b165e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2016,6 +2016,12 @@
         android:label="@string/permlab_preferredPaymentInfo"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi Allows access to set NFC controller always on states.
+         <p>Protection level: signature|privileged
+         @hide -->
+    <permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
          Applications holding this permission can access OMAPI reset system API
          and bypass OMAPI AccessControlEnforcer.
diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml
index 16ffaa4..def429e 100644
--- a/core/res/res/layout/chooser_action_button.xml
+++ b/core/res/res/layout/chooser_action_button.xml
@@ -19,13 +19,13 @@
     android:paddingStart="12dp"
     android:paddingEnd="12dp"
     android:drawablePadding="8dp"
-    android:textColor="?android:textColorPrimary"
+    android:textColor="@color/text_color_primary_device_default_light"
     android:textSize="12sp"
     android:maxWidth="192dp"
     android:singleLine="true"
     android:clickable="true"
     android:background="@drawable/chooser_action_button_bg"
-    android:drawableTint="?android:textColorPrimary"
+    android:drawableTint="@color/text_color_primary_device_default_light"
     android:drawableTintMode="src_in"
     style="?android:attr/borderlessButtonStyle"
     />
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 42c2c69..ca549ae 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -30,9 +30,7 @@
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
     <!-- Height of area above QQS where battery/time go -->
-    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
-    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
-    <dimen name="quick_qs_total_height">152dp</dimen>
+    <dimen name="quick_qs_offset_height">48dp</dimen>
     <!-- Default height of an action bar. -->
     <dimen name="action_bar_default_height">40dip</dimen>
     <!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 74f8ff6..eea3799 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9539,4 +9539,10 @@
     <attr name="iconfactoryBadgeSize" format="dimension"/>
     <!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
     <attr name="lStar" format="float"/>
+
+    <declare-styleable name="DisplayHasherService">
+        <!-- The throttle duration for display hash requests
+             @hide @SystemApi -->
+        <attr name="throttleDurationMillis" format="integer" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index afbbe46..cf5b4e1 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -47,10 +47,6 @@
     <dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
     <!-- Height of area above QQS where battery/time go -->
     <dimen name="quick_qs_offset_height">48dp</dimen>
-    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
-    <dimen name="quick_qs_total_height">176dp</dimen>
-    <!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) -->
-    <dimen name="quick_qs_total_height_with_media">224dp</dimen>
     <!-- Height of the bottom navigation / system bar. -->
     <dimen name="navigation_bar_height">48dp</dimen>
     <!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 49039f9..aea9d60 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3096,6 +3096,8 @@
     <!-- @hide @SystemApi -->
     <public name="playHomeTransitionSound" />
     <public name="lStar" />
+    <!-- @hide @SystemApi -->
+    <public name="throttleDurationMillis" />
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b834a06..ce60633 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -745,7 +745,7 @@
 
     <!-- Text shown when viewing channel settings for notifications related to accessibility
          security policy. [CHAR_LIMIT=NONE]-->
-    <string name="notification_channel_accessibility_security_policy">Accessibility security policy</string>
+    <string name="notification_channel_accessibility_security_policy">Accessibility usage</string>
 
     <!-- Label for foreground service notification when one app is running.
     [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8621147..beed9a3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1716,8 +1716,6 @@
   <java-symbol type="dimen" name="status_bar_height" />
   <java-symbol type="dimen" name="display_cutout_touchable_region_size" />
   <java-symbol type="dimen" name="quick_qs_offset_height" />
-  <java-symbol type="dimen" name="quick_qs_total_height" />
-  <java-symbol type="dimen" name="quick_qs_total_height_with_media" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_off" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_on" />
   <java-symbol type="drawable" name="ic_jog_dial_unlock" />
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 47556c3..34a1fd8 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -97,7 +97,7 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // test if setVisibility can show IME
-            mImeConsumer.onWindowFocusGained();
+            mImeConsumer.onWindowFocusGained(true);
             mController.show(WindowInsets.Type.ime(), true /* fromIme */);
             mController.cancelExistingAnimations();
             assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -116,7 +116,7 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // Request IME visible before control is available.
-            mImeConsumer.onWindowFocusGained();
+            mImeConsumer.onWindowFocusGained(true);
             mController.show(WindowInsets.Type.ime(), true /* fromIme */);
 
             // set control and verify visibility is applied.
@@ -132,24 +132,58 @@
     }
 
     @Test
-    public void testImeGetAndClearSkipAnimationOnce() {
+    public void testImeGetAndClearSkipAnimationOnce_expectSkip() {
+        // Expect IME animation will skipped when the IME is visible at first place.
+        verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, true /* hasViewFocus */,
+                true /* expectSkipAnim */);
+    }
+
+    @Test
+    public void testImeGetAndClearSkipAnimationOnce_expectNoSkip() {
+        // Expect IME animation will not skipped if previously no view focused when gained the
+        // window focus and requesting the IME visible next time.
+        verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, false /* hasViewFocus */,
+                false /* expectSkipAnim */);
+    }
+
+    private void verifyImeGetAndClearSkipAnimationOnce(boolean hasWindowFocus, boolean hasViewFocus,
+            boolean expectSkipAnim) {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // Request IME visible before control is available.
-            mImeConsumer.onWindowFocusGained();
-            mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+            mImeConsumer.onWindowFocusGained(hasWindowFocus);
+            final boolean imeVisible = hasWindowFocus && hasViewFocus;
+            if (imeVisible) {
+                mController.show(WindowInsets.Type.ime(), true /* fromIme */);
+            }
 
             // set control and verify visibility is applied.
             InsetsSourceControl control = Mockito.spy(
                     new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE));
             // Simulate IME source control set this flag when the target has starting window.
             control.setSkipAnimationOnce(true);
-            mController.onControlsChanged(new InsetsSourceControl[] { control });
-            // Verify IME show animation should be triggered when control becomes available and
-            // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
-            verify(control).getAndClearSkipAnimationOnce();
-            verify(mController).applyAnimation(
-                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
-                    eq(true) /* skipAnim */);
+
+            if (imeVisible) {
+                // Verify IME applyAnimation should be triggered when control becomes available,
+                // and expect skip animation state after getAndClearSkipAnimationOnce invoked.
+                mController.onControlsChanged(new InsetsSourceControl[]{ control });
+                verify(control).getAndClearSkipAnimationOnce();
+                verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+                        eq(true) /* show */, eq(false) /* fromIme */,
+                        eq(expectSkipAnim) /* skipAnim */);
+            }
+
+            // If previously hasViewFocus is false, verify when requesting the IME visible next
+            // time will not skip animation.
+            if (!hasViewFocus) {
+                mController.show(WindowInsets.Type.ime(), true);
+                mController.onControlsChanged(new InsetsSourceControl[]{ control });
+                // Verify IME show animation should be triggered when control becomes available and
+                // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
+                verify(control).getAndClearSkipAnimationOnce();
+                verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+                        eq(true) /* show */, eq(true) /* fromIme */,
+                        eq(false) /* skipAnim */);
+            }
         });
     }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 4390546..6301f32 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -234,7 +234,7 @@
         InsetsSourceControl ime = controls[2];
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
             mController.show(Type.ime(), true /* fromIme */);
             mController.show(Type.all());
@@ -260,7 +260,7 @@
         InsetsSourceControl ime = createControl(ITYPE_IME);
         mController.onControlsChanged(new InsetsSourceControl[] { ime });
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
+            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
             mController.show(Type.ime(), true /* fromIme */);
             mController.cancelExistingAnimations();
             assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 549e074..5aacfdd 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -13,3 +13,4 @@
 yamasani@google.com
 
 per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
+per-file services.core.protolog.json =  file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/docs/html/reference/images/rounded_corner/rounded-corner-info.png b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
new file mode 100644
index 0000000..312d935
--- /dev/null
+++ b/docs/html/reference/images/rounded_corner/rounded-corner-info.png
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index bacff78..b64c796 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -27,9 +27,10 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.WindowManager;
 
 import androidx.annotation.Nullable;
 
@@ -75,7 +76,7 @@
         mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
 
         mRootBounds.set(configuration.windowConfiguration.getBounds());
-        mDividerSnapAlgorithm = getSnapAlgorithm(context.getResources(), mRootBounds);
+        mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
         resetDividerPosition();
     }
 
@@ -114,7 +115,7 @@
         mContext = mContext.createConfigurationContext(configuration);
         mSplitWindowManager.setConfiguration(configuration);
         mRootBounds.set(rootBounds);
-        mDividerSnapAlgorithm = getSnapAlgorithm(mContext.getResources(), mRootBounds);
+        mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
         resetDividerPosition();
 
         // Don't inflate divider bar if it is not initialized.
@@ -217,15 +218,15 @@
         return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
     }
 
-    private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) {
+    private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
         final boolean isLandscape = isLandscape(rootBounds);
         return new DividerSnapAlgorithm(
-                resources,
+                context.getResources(),
                 rootBounds.width(),
                 rootBounds.height(),
                 mDividerSize,
                 !isLandscape,
-                new Rect() /* insets */,
+                getDisplayInsets(context),
                 isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
     }
 
@@ -250,6 +251,15 @@
         animator.start();
     }
 
+    private static Rect getDisplayInsets(Context context) {
+        return context.getSystemService(WindowManager.class)
+                .getMaximumWindowMetrics()
+                .getWindowInsets()
+                .getInsets(WindowInsets.Type.navigationBars()
+                        | WindowInsets.Type.statusBars()
+                        | WindowInsets.Type.displayCutout()).toRect();
+    }
+
     private static boolean isLandscape(Rect bounds) {
         return bounds.width() > bounds.height();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index e7cd38f..01a81d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -40,15 +40,17 @@
     void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
             WindowContainerTransaction wct) {
         final WindowContainerToken rootToken = mRootTaskInfo.token;
-        wct.setHidden(rootToken, false)
-                .setBounds(rootToken, rootBounds)
+        wct.setBounds(rootToken, rootBounds)
                 .reparent(task.token, rootToken, true /* onTop*/)
                 // Moving the root task to top after the child tasks were repareted , or the root
                 // task cannot be visible and focused.
-                .reorder(rootToken, true);
+                .reorder(rootToken, true /* onTop */);
     }
 
     boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+        // No matter if the root task is empty or not, moving the root to bottom because it no
+        // longer preserves visible child task.
+        wct.reorder(mRootTaskInfo.token, false /* onTop */);
         if (mChildrenTaskInfo.size() == 0) return false;
         wct.reparentTasks(
                 mRootTaskInfo.token,
@@ -62,10 +64,7 @@
     boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
         final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
         if (task == null) return false;
-
-        wct.setHidden(mRootTaskInfo.token, true)
-                .reorder(mRootTaskInfo.token, false)
-                .reparent(task.token, newParent, false /* onTop */);
+        wct.reparent(task.token, newParent, false /* onTop */);
         return true;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 55d0b7d..1302314 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -126,6 +126,7 @@
             labelRes = app.labelRes;
         }
 
+        final int taskId = taskInfo.taskId;
         Context context = mContext;
         // replace with the default theme if the application didn't set
         final int theme = windowInfo.splashScreenThemeResId != 0
@@ -153,6 +154,7 @@
             } catch (PackageManager.NameNotFoundException e) {
                 Slog.w(TAG, "Failed creating package context with package name "
                         + activityInfo.packageName + " for user " + taskInfo.userId, e);
+                return;
             }
         }
 
@@ -167,15 +169,21 @@
             final TypedArray typedArray = overrideContext.obtainStyledAttributes(
                     com.android.internal.R.styleable.Window);
             final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
-            if (resId != 0 && overrideContext.getDrawable(resId) != null) {
-                // We want to use the windowBackground for the override context if it is
-                // available, otherwise we use the default one to make sure a themed starting
-                // window is displayed for the app.
-                if (DEBUG_SPLASH_SCREEN) {
-                    Slog.d(TAG, "addSplashScreen: apply overrideConfig"
-                            + taskConfig + " to starting window resId=" + resId);
+            try {
+                if (resId != 0 && overrideContext.getDrawable(resId) != null) {
+                    // We want to use the windowBackground for the override context if it is
+                    // available, otherwise we use the default one to make sure a themed starting
+                    // window is displayed for the app.
+                    if (DEBUG_SPLASH_SCREEN) {
+                        Slog.d(TAG, "addSplashScreen: apply overrideConfig"
+                                + taskConfig + " to starting window resId=" + resId);
+                    }
+                    context = overrideContext;
                 }
-                context = overrideContext;
+            } catch (Resources.NotFoundException e) {
+                Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: "
+                        + taskId, e);
+                return;
             }
             typedArray.recycle();
         }
@@ -258,7 +266,6 @@
         params.setTitle("Splash Screen " + activityInfo.packageName);
 
         // TODO(b/173975965) tracking performance
-        final int taskId = taskInfo.taskId;
         SplashScreenView sView = null;
         try {
             final View view = win.getDecorView();
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 35bab7a..fcd333f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -73,7 +73,8 @@
 fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
+        visibleRegion(primaryLayerName)
+            .coversExactly(getPrimaryRegion(dividerRegion, rotation))
     }
 }
 
@@ -83,7 +84,8 @@
 ) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName)
+        visibleRegion(primaryLayerName)
+            .coversExactly(getPrimaryRegion(dividerRegion, rotation))
     }
 }
 
@@ -93,7 +95,8 @@
 ) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
+        visibleRegion(secondaryLayerName)
+            .coversExactly(getSecondaryRegion(dividerRegion, rotation))
     }
 }
 
@@ -103,7 +106,8 @@
 ) {
     assertLayersEnd {
         val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName)
+        visibleRegion(secondaryLayerName)
+            .coversExactly(getSecondaryRegion(dividerRegion, rotation))
     }
 }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 63e9a78..614530b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -82,10 +82,10 @@
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
             val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            this.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
-                primaryApp.defaultWindowName)
-                .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
-                    secondaryApp.defaultWindowName)
+            visibleRegion(primaryApp.defaultWindowName)
+                .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
+            visibleRegion(secondaryApp.defaultWindowName)
+                .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 234dda4..ef68ed6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -87,10 +87,10 @@
     fun appsStartingBounds() {
         testSpec.assertLayersStart {
             val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion),
-                primaryApp.defaultWindowName)
-            coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion),
-                secondaryApp.defaultWindowName)
+            visibleRegion(primaryApp.defaultWindowName)
+                .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
+            visibleRegion(secondaryApp.defaultWindowName)
+                .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index f40a08c..33ade38 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -183,8 +183,8 @@
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
-            this.coversExactly(topAppBounds, "SimpleActivity")
-                .coversExactly(bottomAppBounds, "ImeActivity")
+            visibleRegion("SimpleActivity").coversExactly(topAppBounds)
+            visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
         }
     }
 
@@ -203,8 +203,8 @@
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
 
-            this.coversExactly(topAppBounds, sSimpleActivity)
-                .coversExactly(bottomAppBounds, sImeActivity)
+            visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
+            visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 0333227..1ba1f3b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -90,8 +90,8 @@
     @Test
     fun testAppCoversFullScreenWithPipOnDisplay() {
         testSpec.assertLayersStart {
-            coversExactly(displayBounds, testApp.defaultWindowName)
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
+            visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
         }
     }
 
@@ -99,7 +99,7 @@
     @Test
     fun pipAppCoversFullScreen() {
         testSpec.assertLayersEnd {
-            coversExactly(displayBounds, pipApp.defaultWindowName)
+            visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index ba88ee5..dd6195c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -116,7 +116,7 @@
     @Test
     fun pipAppLayerHidesTestApp() {
         testSpec.assertLayersStart {
-            coversExactly(startingBounds, pipApp.defaultWindowName)
+            visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
             isInvisible(testApp.defaultWindowName)
         }
     }
@@ -125,7 +125,7 @@
     @Test
     fun testAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
-            coversExactly(endingBounds, testApp.defaultWindowName)
+            visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index bf148bc..6166721 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -116,8 +116,8 @@
     @Test
     fun bothAppLayersVisible() {
         testSpec.assertLayersEnd {
-            coversAtMost(displayBounds, testApp.defaultWindowName)
-            coversAtMost(displayBounds, imeApp.defaultWindowName)
+            visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index f554ca3..acc013a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -63,7 +63,7 @@
     @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 8ceef8a..852ee47 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -26,11 +26,11 @@
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -91,8 +91,8 @@
     @Test
     fun appLayerRotates_StartingBounds() {
         testSpec.assertLayersStart {
-            coversExactly(startingBounds, fixedApp.defaultWindowName)
-            coversAtMost(startingBounds, pipApp.defaultWindowName)
+            visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
+            visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
         }
     }
 
@@ -100,8 +100,8 @@
     @Test
     fun appLayerRotates_EndingBounds() {
         testSpec.assertLayersEnd {
-            coversExactly(endingBounds, fixedApp.defaultWindowName)
-            coversAtMost(endingBounds, pipApp.defaultWindowName)
+            visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
+            visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 945a20b..6f17a2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -83,7 +83,7 @@
     @Test
     fun testAppCoversFullScreen() {
         testSpec.assertLayersStart {
-            coversExactly(displayBounds, pipApp.defaultWindowName)
+            visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 102af92..9aab7f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -91,7 +91,7 @@
     @Test
     fun pipWindowInsideDisplay() {
         testSpec.assertWmStart {
-            coversAtMost(startingBounds, pipApp.defaultWindowName)
+            frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
         }
     }
 
@@ -107,7 +107,7 @@
     @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
-            coversAtMost(startingBounds, pipApp.defaultWindowName)
+            visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
         }
     }
 
@@ -121,7 +121,7 @@
     @Test
     fun pipAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
-            coversExactly(endingBounds, pipApp.defaultWindowName)
+            visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 8ae0a73..78b3d4e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -112,7 +112,8 @@
         spyOn(context);
         spyOn(realWindowManager);
         try {
-            doReturn(context).when(context).createPackageContext(anyString(), anyInt());
+            doReturn(context).when(context)
+                    .createPackageContextAsUser(anyString(), anyInt(), any());
         } catch (PackageManager.NameNotFoundException e) {
             //
         }
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 168a863..1e90b7c 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1379,6 +1379,11 @@
     enum : uint32_t {
         // Additional flag indicating an entry is public.
         SPEC_PUBLIC = 0x40000000u,
+
+        // Additional flag indicating the resource id for this resource may change in a future
+        // build. If this flag is set, the SPEC_PUBLIC flag is also set since the resource must be
+        // public to be exposed as an API to other applications.
+        SPEC_STAGED_API = 0x20000000u,
     };
 };
 
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 0a9560a..7428c6e 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -311,6 +311,7 @@
     field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
     field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
     field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+    field public static final int NET_CAPABILITY_HEAD_UNIT = 32; // 0x20
     field public static final int NET_CAPABILITY_IA = 7; // 0x7
     field public static final int NET_CAPABILITY_IMS = 4; // 0x4
     field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
@@ -334,6 +335,7 @@
     field public static final int TRANSPORT_CELLULAR = 0; // 0x0
     field public static final int TRANSPORT_ETHERNET = 3; // 0x3
     field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+    field public static final int TRANSPORT_USB = 8; // 0x8
     field public static final int TRANSPORT_VPN = 4; // 0x4
     field public static final int TRANSPORT_WIFI = 1; // 0x1
     field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 970a236..2065559 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -179,10 +179,12 @@
   }
 
   public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
-    ctor public VpnTransportInfo(int);
+    ctor public VpnTransportInfo(int, @Nullable String);
     method public int describeContents();
+    method @NonNull public android.net.VpnTransportInfo makeCopy(long);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+    field @Nullable public final String sessionId;
     field public final int type;
   }
 
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 95df8b8..6c3b620 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -219,7 +219,7 @@
     method public void onAutomaticReconnectDisabled();
     method public void onBandwidthUpdateRequested();
     method public void onNetworkCreated();
-    method public void onNetworkDisconnected();
+    method public void onNetworkDestroyed();
     method public void onNetworkUnwanted();
     method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
     method public void onQosCallbackUnregistered(int);
@@ -238,6 +238,7 @@
     method public final void sendQosSessionLost(int, int, int);
     method public final void sendSocketKeepaliveEvent(int, int);
     method @Deprecated public void setLegacySubtype(int, @NonNull String);
+    method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int);
     method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
     method public void unregister();
     field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 3939c7f..350b47f 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -924,6 +924,7 @@
             BLOCKED_REASON_DOZE,
             BLOCKED_REASON_APP_STANDBY,
             BLOCKED_REASON_RESTRICTED_MODE,
+            BLOCKED_REASON_LOCKDOWN_VPN,
             BLOCKED_METERED_REASON_DATA_SAVER,
             BLOCKED_METERED_REASON_USER_RESTRICTED,
             BLOCKED_METERED_REASON_ADMIN_DISABLED,
@@ -3715,7 +3716,8 @@
         public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
 
         /**
-         * Called when access to the specified network is blocked or unblocked.
+         * Called when access to the specified network is blocked or unblocked, or the reason for
+         * access being blocked changes.
          *
          * If a NetworkCallback object implements this method,
          * {@link #onBlockedStatusChanged(Network, boolean)} will not be called.
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
index f9d3994..d941d4b 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl
@@ -47,5 +47,5 @@
     void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
     void onQosCallbackUnregistered(int qosCallbackId);
     void onNetworkCreated();
-    void onNetworkDisconnected();
+    void onNetworkDestroyed();
 }
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
index cbd6193..26cb1ed 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -41,4 +41,5 @@
     void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes);
     void sendQosSessionLost(int qosCallbackId, in QosSession session);
     void sendQosCallbackError(int qosCallbackId, int exceptionType);
+    void sendTeardownDelayMs(int teardownDelayMs);
 }
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 6b55bb7..c57da53 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -185,6 +185,20 @@
     public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5;
 
     /**
+     * Sent by the NetworkAgent to ConnectivityService to pass the current value of the teardown
+     * delay.
+     * arg1 = teardown delay in milliseconds
+     * @hide
+     */
+    public static final int EVENT_TEARDOWN_DELAY_CHANGED = BASE + 6;
+
+    /**
+     * The maximum value for the teardown delay, in milliseconds.
+     * @hide
+     */
+    public static final int MAX_TEARDOWN_DELAY_MS = 5000;
+
+    /**
      * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
      * networks status - whether we could use the network or could not, due to
      * either a bad network configuration (no internet link) or captive portal.
@@ -197,7 +211,6 @@
      */
     public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
 
-
     /**
      * Network validation suceeded.
      * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
@@ -376,7 +389,7 @@
      *
      * @hide
      */
-    public static final int CMD_NETWORK_DISCONNECTED = BASE + 23;
+    public static final int CMD_NETWORK_DESTROYED = BASE + 23;
 
     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
         final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
@@ -581,8 +594,8 @@
                     onNetworkCreated();
                     break;
                 }
-                case CMD_NETWORK_DISCONNECTED: {
-                    onNetworkDisconnected();
+                case CMD_NETWORK_DESTROYED: {
+                    onNetworkDestroyed();
                     break;
                 }
             }
@@ -732,8 +745,8 @@
         }
 
         @Override
-        public void onNetworkDisconnected() {
-            mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DISCONNECTED));
+        public void onNetworkDestroyed() {
+            mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DESTROYED));
         }
     }
 
@@ -851,6 +864,29 @@
     }
 
     /**
+     * Sets the value of the teardown delay.
+     *
+     * The teardown delay is the time between when the network disconnects and when the native
+     * network corresponding to this {@code NetworkAgent} is destroyed. By default, the native
+     * network is destroyed immediately. If {@code teardownDelayMs} is non-zero, then when this
+     * network disconnects, the system will instead immediately mark the network as restricted
+     * and unavailable to unprivileged apps, but will defer destroying the native network until the
+     * teardown delay timer expires.
+     *
+     * The interfaces in use by this network will remain in use until the native network is
+     * destroyed and cannot be reused until {@link #onNetworkDestroyed()} is called.
+     *
+     * This method may be called at any time while the network is connected. It has no effect if
+     * the network is already disconnected and the teardown delay timer is running.
+     *
+     * @param teardownDelayMs the teardown delay to set, or 0 to disable teardown delay.
+     */
+    public void setTeardownDelayMs(
+            @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMs) {
+        queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMs));
+    }
+
+    /**
      * Change the legacy subtype of this network agent.
      *
      * This is only for backward compatibility and should not be used by non-legacy network agents,
@@ -1053,7 +1089,7 @@
     /**
      * Called when ConnectivityService has successfully destroy this NetworkAgent's native network.
      */
-    public void onNetworkDisconnected() {}
+    public void onNetworkDestroyed() {}
 
     /**
      * Requests that the network hardware send the specified packet at the specified interval.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index ca69f16..4f95ccc 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -275,6 +275,7 @@
             NET_CAPABILITY_ENTERPRISE,
             NET_CAPABILITY_VSIM,
             NET_CAPABILITY_BIP,
+            NET_CAPABILITY_HEAD_UNIT,
     })
     public @interface NetCapability { }
 
@@ -508,8 +509,13 @@
     @SystemApi
     public static final int NET_CAPABILITY_BIP = 31;
 
+    /**
+     * Indicates that this network is connected to an automotive head unit.
+     */
+    public static final int NET_CAPABILITY_HEAD_UNIT = 32;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_BIP;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_HEAD_UNIT;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -527,7 +533,10 @@
             | (1 << NET_CAPABILITY_NOT_SUSPENDED)
             | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
             | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
-            | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+            | (1 << NET_CAPABILITY_NOT_VCN_MANAGED)
+            // The value of NET_CAPABILITY_HEAD_UNIT is 32, which cannot use int to do bit shift,
+            // otherwise there will be an overflow. Use long to do bit shift instead.
+            | (1L << NET_CAPABILITY_HEAD_UNIT);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -867,6 +876,7 @@
             TRANSPORT_WIFI_AWARE,
             TRANSPORT_LOWPAN,
             TRANSPORT_TEST,
+            TRANSPORT_USB,
     })
     public @interface Transport { }
 
@@ -913,10 +923,15 @@
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final int TRANSPORT_TEST = 7;
 
+    /**
+     * Indicates this network uses a USB transport.
+     */
+    public static final int TRANSPORT_USB = 8;
+
     /** @hide */
     public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
     /** @hide */
-    public static final int MAX_TRANSPORT = TRANSPORT_TEST;
+    public static final int MAX_TRANSPORT = TRANSPORT_USB;
 
     /** @hide */
     public static boolean isValidTransport(@Transport int transportType) {
@@ -931,7 +946,8 @@
         "VPN",
         "WIFI_AWARE",
         "LOWPAN",
-        "TEST"
+        "TEST",
+        "USB"
     };
 
     /**
@@ -2107,6 +2123,7 @@
             case NET_CAPABILITY_ENTERPRISE:           return "ENTERPRISE";
             case NET_CAPABILITY_VSIM:                 return "VSIM";
             case NET_CAPABILITY_BIP:                  return "BIP";
+            case NET_CAPABILITY_HEAD_UNIT:            return "HEAD_UNIT";
             default:                                  return Integer.toString(capability);
         }
     }
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index cd8f4c0..ba83a44 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -17,11 +17,14 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.util.Objects;
 
@@ -38,8 +41,26 @@
     /** Type of this VPN. */
     public final int type;
 
-    public VpnTransportInfo(int type) {
+    @Nullable
+    public final String sessionId;
+
+    @Override
+    public long getApplicableRedactions() {
+        return REDACT_FOR_NETWORK_SETTINGS;
+    }
+
+    /**
+     * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary.
+     */
+    @NonNull
+    public VpnTransportInfo makeCopy(long redactions) {
+        return new VpnTransportInfo(type,
+            ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : sessionId);
+    }
+
+    public VpnTransportInfo(int type, @Nullable String sessionId) {
         this.type = type;
+        this.sessionId = sessionId;
     }
 
     @Override
@@ -47,17 +68,17 @@
         if (!(o instanceof VpnTransportInfo)) return false;
 
         VpnTransportInfo that = (VpnTransportInfo) o;
-        return this.type == that.type;
+        return (this.type == that.type) && TextUtils.equals(this.sessionId, that.sessionId);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(type);
+        return Objects.hash(type, sessionId);
     }
 
     @Override
     public String toString() {
-        return String.format("VpnTransportInfo{type=%d}", type);
+        return String.format("VpnTransportInfo{type=%d, sessionId=%s}", type, sessionId);
     }
 
     @Override
@@ -68,12 +89,13 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(type);
+        dest.writeString(sessionId);
     }
 
     public static final @NonNull Creator<VpnTransportInfo> CREATOR =
             new Creator<VpnTransportInfo>() {
         public VpnTransportInfo createFromParcel(Parcel in) {
-            return new VpnTransportInfo(in.readInt());
+            return new VpnTransportInfo(in.readInt(), in.readString());
         }
         public VpnTransportInfo[] newArray(int size) {
             return new VpnTransportInfo[size];
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index b44128b..518f198 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -72,7 +72,7 @@
         "ServiceConnectivityResources",
     ],
     static_libs: [
-        "dnsresolver_aidl_interface-V7-java",
+        "dnsresolver_aidl_interface-V8-java",
         "modules-utils-os",
         "net-utils-device-common",
         "net-utils-framework-common",
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index f45105d..31b3fe5 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -70,4 +70,13 @@
         android:scaleY="2"
         android:layout_marginTop="4dp"
         android:max="100"/>
+
+    <TextView
+        android:id="@+id/bottom_summary"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:ellipsize="marquee"
+        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
+        android:textSize="14sp"/>
 </LinearLayout>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index 2185950..a2b1de2 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -44,6 +44,7 @@
 
     private CharSequence mUsageSummary;
     private CharSequence mTotalSummary;
+    private CharSequence mBottomSummary;
     private ImageView mCustomImageView;
     private int mPercent = -1;
 
@@ -101,6 +102,15 @@
         notifyChanged();
     }
 
+    /** Set bottom summary. */
+    public void setBottomSummary(CharSequence bottomSummary) {
+        if (TextUtils.equals(mBottomSummary, bottomSummary)) {
+            return;
+        }
+        mBottomSummary = bottomSummary;
+        notifyChanged();
+    }
+
     /** Set percentage of the progress bar. */
     public void setPercent(long usage, long total) {
         if (total == 0L || usage >  total) {
@@ -147,6 +157,14 @@
             totalSummary.setText(mTotalSummary);
         }
 
+        final TextView bottomSummary = (TextView) holder.findViewById(R.id.bottom_summary);
+        if (TextUtils.isEmpty(mBottomSummary)) {
+            bottomSummary.setVisibility(View.GONE);
+        } else {
+            bottomSummary.setVisibility(View.VISIBLE);
+            bottomSummary.setText(mBottomSummary);
+        }
+
         final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress);
         if (mPercent < 0) {
             progressBar.setIndeterminate(true);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
index fe76b06..cd78add 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -92,6 +92,28 @@
     }
 
     @Test
+    public void setBottomSummary_getCorrectSummary() {
+        final String expectedText = "Should last until about 7:45 PM";
+        mUsageProgressBarPreference.setBottomSummary(expectedText);
+
+        mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+        final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
+        assertThat(bottomSummary.getText()).isEqualTo(expectedText);
+        assertThat(bottomSummary.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setBottomSummary_emptyText_isGone() {
+        mUsageProgressBarPreference.setBottomSummary(null);
+
+        mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+        final TextView bottomSummary = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
+        assertThat(bottomSummary.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
     public void setPercent_getCorrectProgress() {
         mUsageProgressBarPreference.setPercent(31, 80);
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index db9b83e..53920f0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -165,6 +165,8 @@
         VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
         VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 9c67e9c..4119dc9f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1881,6 +1881,12 @@
         dumpSetting(s, p,
                 Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
                 SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED,
+                SecureSettingsProto.Assist.TOUCH_GESTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
+                SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
         p.end(assistToken);
 
         final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
diff --git a/packages/SystemUI/docs/falsing.md b/packages/SystemUI/docs/falsing.md
new file mode 100644
index 0000000..e224ca8
--- /dev/null
+++ b/packages/SystemUI/docs/falsing.md
@@ -0,0 +1,240 @@
+# Falsing in SystemUI
+
+Phones are easily and often accidentally-activated in owners' pockets ("falsing" or "pocket 
+dialing"). Because a phone's screen can be turned on with a single tap, and because we have further
+actions that be activated with basic tapping and swiping, it is critical that we
+analyze touch events on the screen for intentional vs accidental behavior. With analysis, 
+features within SystemUI have an opportunity to ignore or even undo accidental interactions as they
+are occurring.
+
+## Technical Details
+
+The `FalsingManager` tracks all touch interactions happening on a phone's lock screen.
+
+If you support any sort of touch gestures on the lock screen, you **must**, at a
+minimum, inform the `FalsingManager` of what touches are on touch targets vs not (things that may be
+ intentional). If you do not tell the `FalsingManager`, it will assume touches on your feature are
+always accidental and penalize the session accordingly.
+
+Individual touch targets do not _have_ to be separated out; it's acceptable to
+wrap your whole feature in one virtual block that reports touches to the
+`FalsingManager`, however more granular tracking will result in better results
+across the whole lock screen.
+
+You can _act_ on the results of the `FalsingManager`. Instead of only telling
+the `FalsingManager` that touch events were on touch targets, you can further use the
+returned results to decide if you want to respond to an owner's touch, if you
+want to prompt them to confirm their action, or if you simply want to ignore the
+touch.
+
+The flow through the system looks like such:
+
+1. Gesture on the screen.
+2. The `FalsingManager` makes a note of all of the `MotionEvents`.
+    * If no feature/touch target receives the `MotionEvents`, skip to 4. 
+3. Your touch target receives the `MotionEvents`.
+    * Once your feature is ready to respond to the gesture in a substantive manner, it queries
+      the `FalsingManager`.
+      - Dragging animations, touch ripples, and other purely visual effects should not query.
+      - Query once you are ready to launch a new feature or dialogue, or are otherwise going to
+        change the state of the UI. 
+      - Generally, wait until `MotionEvent.ACTION_UP` to query or `View.OnClickListener#onClick`.
+      - Only query once per gesture, at the end.
+    * If the `FalsingManager` says it looks good, respond to the touch.
+4. The `FalsingManager` checks to see if anyone queried about the gesture. If not, mark it as 
+   accidental. 
+   
+There is also an event fired by the `FalsingManager` that can be listened to by anyone, that 
+indicates that the the `FalsingManager` believes the phone is actively being pocket-dialed. When
+fired, modal features, such as quick settings, keyguard bouncer, and others should retract 
+themselves to prevent further pocket-dialing.  
+
+## Falsing "Belief" and History
+
+The `FalsingManager` maintains a recent history of false analyses. Using
+Bayesian statistics, it updates a "belief" in  whether recent
+gestures are intentional or not. Any gesture that it is not explicitly queried about is treated as
+accidental, increasing the overall belief in
+false-iness. Gestures that are explicitly queried and that pass the relevant heuristics
+reduce belief that falsing is occurring. This information is tracked within the `HistoryTracker`.
+
+Changes in belief may influence internal heurstics within the `FalsingManager`,
+making it easier or harder for an owner to interact with their device. (An owner
+will always be able to interact with their device, but we may require double
+taps, or more deliberate swipes.)
+
+## Responding to Touch Events
+
+The methods below inform the `FalsingManager` that a tap is occurring within an expected touch 
+target. Match the methods with the gesture you expect the device owner to use.
+
+### Single Tap
+
+`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This
+method tells the `FalsingManager` that you want to validate a single tap. It
+returns true if it thinks the tap should be rejected (i.e. the tap looks more
+like a swipe) and false otherwise.
+
+`robustCheck` determines what heuristics are used. If set to false, the method
+performs a only very basic checking, checking that observed `MotionEvent`s are
+all within some small x & y region ("touch slop").
+
+When `robustCheck` is set to true, several more advanced rules are additionally
+applied:
+
+1.  If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
+2.  If the tap is the _second_ tap in recent history and looks like a valid Double Tap
+    the tap is **accepted**. This works exactly like `FalsingManager#isFalseDoubleTap`.
+3.  If the `HistoryTracker` reports strong belief in recent falsing, the tap is
+    **rejected**.
+4.  Otherwise the tap is **accepted**.
+
+All the above rules are applied only after first confirming the gesture does
+in fact look like a basic tap.
+
+`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be
+penalized in the event that the tap is rejected. This value is only used if
+`robustCheck` is set to true.
+
+A value of `0` means no change in belief. A value of `1` means a _very_ strong
+confidence in a false tap. In general, as a single tap on the screen is not
+verifiable, a small value should be supplied - on the order of `0.1`. Pass `0`
+if you don't want to penalize belief at all. Pass a higher value
+the earlier in the UX flow your interaction occurs. Once an owner is farther
+along in a UX flow (multiple taps or swipes), its safer to assume that a single
+accidental tap should cause less of a penalty.
+
+### Double Tap
+
+`FalsingManager#isFalseDoubleTap()`. This method tells the `FalsingManager` that
+your UI wants to validate a double tap. There are no parameters to pass to this method.
+Call this when you explicitly receive and want to verify a double tap, _not_ a single tap.
+
+Note that `FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`
+will also check for double taps when `robustCheck` is set to true. If you are
+willing to use single taps, use that instead.
+
+### Swipes and Other Gestures
+
+`FalsingManager#isFalseTouch(@Classifier.InteractionType int interactionType)`.
+Use this for any non-tap interactions. This includes expanding notifications,
+expanding quick settings, pulling up the bouncer, and more. You must pass
+the type of interaction you are evaluating when calling it. A large set of
+heuristics will be applied to analyze the gesture, and the exact rules vary depending upon
+the `InteractionType`.
+
+### Ignoring A Gesture
+
+`FalsingCollector#avoidGesture()`. Tell the `FalsingManager` to pretend like the
+observed gesture never happened. **This method must be called when the observed
+`MotionEvent` is `MotionEvent.ACTION_DOWN`.** Attempting to call this method
+later in a gesture will not work.
+
+Notice that this method is actually a method on `FalsingCollector`. It is
+forcefully telling the `FalsingManager` to wholly pretend the gesture never
+happened. This is intended for security and PII sensitive gestures, such as
+password inputs. Please don't use this as a shortcut for avoiding the
+FalsingManager. Falsing works better the more behavior it is told about.
+
+### Other Considerations
+
+Please try to call the `FalsingManager` only once per gesture. Wait until you
+are ready to act on the owner's action, and then query the `FalsingManager`. The `FalsingManager`
+will update its belief in pocket dialing based only on the last call made, so multiple calls per
+gesture are not well defined.
+
+The `FalsingManager` does not update its belief in pocket-dialing until a new
+gesture starts. That is to say, if the owner makes a bad tap on your feature,
+the belief in pocket dialing will not incorporate this new data until the
+following gesture begins.
+
+If you expect a mix of taps, double taps, and swipes on your feature, segment them
+accordingly. Figure out which `FalsingManager` method you need to call first, rather than relying
+on multiple calls to the `FalsingManager` to act as a sieve.
+
+Don't:
+```
+if (!mFalsingManager.isFalseTap(false, 0)) {
+  // its a tap
+} else if (!mFalsingManager.isFalseTouch(GESTURE_A) {
+  // do thing a
+} else if (!mFalsingManager.isFalseTouch(GESTURE_B) {
+  // do thing b
+} else {
+  // must be a false.
+}
+``` 
+
+Do:
+```
+void onTap() {
+  if (!mFalsingManager.isFalseTap(false, 0)) {
+    // its a tap
+}
+
+void onGestureA() {
+  if (!mFalsingManager.isFalseTouch(GESTURE_A) {
+    // do thing a
+  }
+}
+
+void onGestureB() {
+  if (!mFalsingManager.isFalseTouch(GESTURE_B) {
+    // do thing b
+  }
+}
+``` 
+
+
+## Influencing Belief
+
+`FalsingCollector#updateFalseConfidence(FalsingClassifier.Result result)`. This
+method allows you to directly change the `FalsingManager`'s belief in the state
+of pocket dialing. If the owner does something unusual with their phone that you
+think indicates pocket dialing, you can call:
+
+```
+    mFalsingCollector.updateFalseConfidence(
+      FalsingClassifier.Result.falsed(0.6, "Owner is doing something fishy"));
+```
+
+A belief value of `1` indicates a 100% confidence of false behavior. A belief
+value of `0` would make no change in the `FalsingManager` and should be avoided
+as it simply creates noise in the logs. Generally, a middle value between the
+two extremes makes sense.
+
+A good example of where this is used is in the "Pattern" password input. We
+avoid recording those gestures in the `FalsingManager`, but we have the pattern input update
+the `FalsingManager` directly in some cases. If the owner simply taps on the pattern input, we 
+record it as a false, (patterns are always 4 "cells" long, so single "cell" inputs are penalized).
+
+Conversely, if you think the owner does something that deserves a nice reward:
+
+```
+    mFalsingCollector.updateFalseConfidence(
+       FalsingClassifier.Result.passed(0.6));
+```
+
+Again, useful on password inputs where the FalsingManager is avoiding recording
+the gesture. This is used on the "pin" password input, to recognize successful
+taps on the input buttons.
+
+## Global Falsing Event
+
+If the `FalsingManager`'s belief in falsing crosses some internally defined
+threshold, it will fire an event that other parts of the system can listen for.
+This even indicates that the owner is likely actively pocket-dialing, and any
+currently open activities on the phone should retract themselves.
+
+To subscribe to this event, call
+`FalsingManager#addFalsingBeliefListener(FalsingBeliefListener listener)`.
+`FalsingBeliefListener` is a simple one method interface that will be called
+after when activities should retract themselves.
+
+**Do Listen For This**. Your code will work without it, but it is a handy,
+universal signal that will save the phone owner a lot of accidents. A simple
+implementation looks like:
+
+```
+    mFalsingManager.addFalsingBeliefListener(MyFeatureClass::hide);
+```
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 44f52ef..bbf69a9 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -18,32 +18,52 @@
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/quick_qs_status_icons"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingTop="@dimen/qs_header_top_padding"
-    android:paddingBottom="@dimen/qs_header_bottom_padding"
-    android:layout_below="@id/quick_status_bar_system_icons"
+    android:layout_height="@*android:dimen/quick_qs_offset_height"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:minHeight="20dp"
+    android:minHeight="48dp"
     android:clickable="false"
     android:focusable="true"
     android:theme="@style/QSHeaderTheme">
 
-    <com.android.systemui.statusbar.policy.DateView
-        android:id="@+id/date"
+    <com.android.systemui.statusbar.policy.Clock
+        android:id="@+id/clock"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="start|center_vertical"
-        android:gravity="center_vertical"
+        android:layout_height="match_parent"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:gravity="center_vertical|start"
+        android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+        android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
         android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.QS.Status"
-        systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+
+    <View
+        android:layout_height="match_parent"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        />
+
+    <!-- Will hold security footer in landscape with media -->
+    <FrameLayout
+        android:id="@+id/header_text_container"
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:gravity="center"
+        />
+
+    <include layout="@layout/qs_carrier_group"
+        android:id="@+id/carrier_group"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minHeight="48dp"
+        android:layout_gravity="end|center_vertical"
+        android:focusable="false"/>
 
     <com.android.systemui.statusbar.phone.StatusIconContainer
         android:id="@+id/statusIcons"
-        android:layout_width="0dp"
+        android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_weight="1"
         android:paddingEnd="@dimen/signal_cluster_battery_padding" />
 
     <com.android.systemui.BatteryMeterView
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
deleted file mode 100644
index fb82304..0000000
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/header_text_container"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/qs_header_tooltip_height"
-    android:layout_below="@id/quick_status_bar_system_icons"
-    android:visibility="invisible"
-    android:theme="@style/QSHeaderTheme"
-    android:forceHasOverlappingRendering="false">
-
-        <com.android.systemui.qs.QSHeaderInfoLayout
-            android:id="@+id/status_container"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="match_parent">
-
-            <LinearLayout
-                android:id = "@+id/alarm_container"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:gravity="center_vertical"
-                android:focusable="true"
-                android:clickable="true">
-
-                <ImageView
-                    android:id="@+id/next_alarm_icon"
-                    android:layout_width="@dimen/qs_header_alarm_icon_size"
-                    android:layout_height="@dimen/qs_header_alarm_icon_size"
-                    android:src="@drawable/ic_alarm"
-                    android:contentDescription="@string/accessibility_quick_settings_alarm_set"
-                    android:visibility="gone"/>
-
-                <com.android.systemui.util.AutoMarqueeTextView
-                    android:id="@+id/next_alarm_text"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:singleLine="true"
-                    android:ellipsize="marquee"
-                    android:marqueeRepeatLimit="marquee_forever"
-                    android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
-                    android:textAppearance="@style/TextAppearance.QS.Status"
-                    android:visibility="gone"/>
-            </LinearLayout>
-
-            <View
-                android:id="@+id/status_separator"
-                android:layout_width="@dimen/qs_header_separator_width"
-                android:layout_height="match_parent"
-                android:visibility="gone"/>
-
-            <LinearLayout
-                android:id = "@+id/ringer_container"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:gravity="center_vertical"
-                android:focusable="true"
-                android:clickable="true">
-
-                <ImageView
-                    android:id="@+id/ringer_mode_icon"
-                    android:layout_width="@dimen/qs_header_alarm_icon_size"
-                    android:layout_height="@dimen/qs_header_alarm_icon_size"
-                    android:visibility="gone"/>
-
-                <com.android.systemui.util.AutoMarqueeTextView
-                    android:id="@+id/ringer_mode_text"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:singleLine="true"
-                    android:ellipsize="marquee"
-                    android:marqueeRepeatLimit="marquee_forever"
-                    android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
-                    android:textAppearance="@style/TextAppearance.QS.Status"
-                    android:visibility="gone"/>
-            </LinearLayout>
-        </com.android.systemui.qs.QSHeaderInfoLayout>
-
-        <include layout="@layout/qs_carrier_group"
-                 android:id="@+id/carrier_group"
-                 android:layout_width="wrap_content"
-                 android:layout_height="match_parent"
-                 android:layout_marginStart="@dimen/qs_status_separator"
-                 android:layout_gravity="end|center_vertical"
-                 android:focusable="false"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 059bda3..5bf6919 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,26 +32,32 @@
     android:paddingStart="0dp"
     android:elevation="4dp" >
 
-    <!-- The clock -->
-    <include layout="@layout/quick_status_bar_header_system_icons" />
+    <!-- Date and privacy. Only visible in QS -->
+    <include layout="@layout/quick_status_bar_header_date_privacy"/>
 
-    <!-- Status icons within the panel itself (and not in the top-most status bar) -->
-    <include layout="@layout/quick_qs_status_icons" />
-
-    <!-- Layout containing tooltips, alarm text, etc. -->
-    <include layout="@layout/quick_settings_header_info" />
+    <RelativeLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+    <!-- Time, icons and Carrier (only in QS) -->
+    <include layout="@layout/quick_qs_status_icons"/>
 
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_below="@id/quick_qs_status_icons"
-        android:accessibilityTraversalAfter="@+id/date_time_group"
+        android:layout_marginTop="8dp"
+        android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
         android:accessibilityTraversalBefore="@id/expand_indicator"
         android:clipChildren="false"
         android:clipToPadding="false"
         android:focusable="true"
         android:paddingBottom="10dp"
         android:importantForAccessibility="yes" />
+    </RelativeLayout>
 
 </com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
similarity index 72%
rename from packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
rename to packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index f663ab4e..22cf2cb 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -17,33 +17,35 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/quick_status_bar_system_icons"
+    android:id="@+id/quick_status_bar_date_privacy"
     android:layout_width="match_parent"
     android:layout_height="@*android:dimen/quick_qs_offset_height"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:gravity="center"
+    android:layout_gravity="top"
     android:orientation="horizontal"
     android:clickable="true"
-    android:paddingTop="@dimen/status_bar_padding_top" >
+    android:paddingTop="@dimen/status_bar_padding_top"
+    android:minHeight="48dp">
 
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="match_parent"
+        android:minHeight="48dp"
         android:layout_weight="1"
         android:orientation="horizontal"
         android:gravity="center_vertical|start" >
 
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:minWidth="48dp"
-        android:gravity="center_vertical|start"
-        android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-        android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
+        <com.android.systemui.statusbar.policy.DateView
+            android:id="@+id/date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:gravity="center_vertical"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.QS.Status"
+            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
     </LinearLayout>
 
     <android.widget.Space
@@ -56,6 +58,7 @@
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="match_parent"
+        android:minHeight="48dp"
         android:layout_weight="1"
         android:orientation="horizontal"
         android:gravity="center_vertical|end" >
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index c7ad3e8..47b41f5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -37,6 +37,7 @@
     public static final int GENERIC = 7;
     public static final int BOUNCER_UNLOCK = 8;
     public static final int PULSE_EXPAND = 9;
+    public static final int BRIGHTNESS_SLIDER = 10;
 
     @IntDef({
             QUICK_SETTINGS,
@@ -48,7 +49,8 @@
             RIGHT_AFFORDANCE,
             GENERIC,
             BOUNCER_UNLOCK,
-            PULSE_EXPAND
+            PULSE_EXPAND,
+            BRIGHTNESS_SLIDER
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 5a9c386..80d7863 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -148,6 +148,10 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
+        if (interactionType == Classifier.BRIGHTNESS_SLIDER) {
+            return Result.passed(0);
+        }
+
         return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index ac330f0..6f80010 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -17,6 +17,7 @@
 package com.android.systemui.classifier;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 
 import android.provider.DeviceConfig;
@@ -115,7 +116,7 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == QUICK_SETTINGS) {
+        if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index 427c2ef..f665565 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -18,6 +18,7 @@
 
 
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
 import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
@@ -45,6 +46,7 @@
         boolean up = isUp();
         boolean right = isRight();
 
+        double confidence = 1;
         boolean wrongDirection = true;
         switch (interactionType) {
             case QUICK_SETTINGS:
@@ -52,6 +54,11 @@
             case NOTIFICATION_DRAG_DOWN:
                 wrongDirection = !vertical || up;
                 break;
+            case BRIGHTNESS_SLIDER:
+                confidence = 0;  // Owners may return to original brightness.
+                // A more sophisticated thing to do here would be to look at the size of the
+                // vertical change relative to the screen size. _Some_ amount of vertical
+                // change should be expected.
             case NOTIFICATION_DISMISS:
                 wrongDirection = vertical;
                 break;
@@ -70,7 +77,7 @@
                 break;
         }
 
-        return wrongDirection ? falsed(1, getReason(interactionType)) : Result.passed(0.5);
+        return wrongDirection ? falsed(confidence, getReason(interactionType)) : Result.passed(0.5);
     }
 
     private String getReason(int interactionType) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 1d413af..d9197ef 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -20,6 +20,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
 
 import android.graphics.Point;
 import android.provider.DeviceConfig;
@@ -87,6 +88,10 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
+        if (interactionType == BRIGHTNESS_SLIDER) {
+            return Result.passed(0);
+        }
+
         List<MotionEvent> motionEvents = getRecentMotionEvents();
         // Rotate horizontal gestures to be horizontal between their first and last point.
         // Rotate vertical gestures to be vertical between their first and last point.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9d43e0c..c8dfde1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -209,6 +209,8 @@
     private @TransitionMode int mNavigationBarMode;
     private ContentResolver mContentResolver;
     private boolean mAssistantAvailable;
+    private boolean mLongPressHomeEnabled;
+    private boolean mAssistantTouchGestureEnabled;
 
     private int mDisabledFlags1;
     private int mDisabledFlags2;
@@ -309,7 +311,7 @@
 
             // Send the assistant availability upon connection
             if (isConnected) {
-                sendAssistantAvailability(mAssistantAvailable);
+                updateAssistantEntrypoints();
             }
         }
 
@@ -404,12 +406,7 @@
             new Handler(Looper.getMainLooper())) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            boolean available = mAssistManagerLazy.get()
-                    .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
-            if (mAssistantAvailable != available) {
-                sendAssistantAvailability(available);
-                mAssistantAvailable = available;
-            }
+            updateAssistantEntrypoints();
         }
     };
 
@@ -531,6 +528,13 @@
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
                 false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+                false, mAssistContentObserver, UserHandle.USER_ALL);
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+                false, mAssistContentObserver, UserHandle.USER_ALL);
+        updateAssistantEntrypoints();
 
         if (savedState != null) {
             mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -823,7 +827,7 @@
                 || mNavigationBarView.getHomeButton().getCurrentView() == null) {
             return;
         }
-        if (mHomeButtonLongPressDurationMs.isPresent()) {
+        if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
             mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false);
             mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false);
             mNavigationBarView.getHomeButton().setOnLongClickListener(null);
@@ -845,6 +849,8 @@
         pw.println("  mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
         pw.println("  mCurrentRotation=" + mCurrentRotation);
         pw.println("  mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
+        pw.println("  mLongPressHomeEnabled=" + mLongPressHomeEnabled);
+        pw.println("  mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
 
         if (mNavigationBarView != null) {
             pw.println("  mNavigationBarWindowState="
@@ -1206,9 +1212,11 @@
                         return true;
                     }
                 }
-                mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
-                    mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
-                });
+                if (mLongPressHomeEnabled) {
+                    mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> {
+                        mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration);
+                    });
+                }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
@@ -1480,15 +1488,23 @@
                 | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
     }
 
-    private void sendAssistantAvailability(boolean available) {
+    private void updateAssistantEntrypoints() {
+        mAssistantAvailable = mAssistManagerLazy.get()
+                .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+        mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, 1) != 0;
+        mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, 1) != 0;
         if (mOverviewProxyService.getProxy() != null) {
             try {
-                mOverviewProxyService.getProxy().onAssistantAvailable(available
+                mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
+                        && mAssistantTouchGestureEnabled
                         && QuickStepContract.isGesturalMode(mNavBarMode));
             } catch (RemoteException e) {
                 Log.w(TAG, "Unable to send assistant availability data to launcher");
             }
         }
+        reconfigureHomeLongClick();
     }
 
     // ----- Methods that DisplayNavigationBarController talks to -----
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index b8823e1..ee0b0c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -65,12 +65,14 @@
     private final QuickQSPanel mQuickQsPanel;
     private final QSPanelController mQsPanelController;
     private final QuickQSPanelController mQuickQSPanelController;
+    private final QuickStatusBarHeader mQuickStatusBarHeader;
     private final QSSecurityFooter mSecurityFooter;
     private final QS mQs;
 
     private PagedTileLayout mPagedLayout;
 
     private boolean mOnFirstPage = true;
+    private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
     private TouchAnimator mFirstPageAnimator;
     private TouchAnimator mFirstPageDelayedAnimator;
     private TouchAnimator mTranslationXAnimator;
@@ -98,19 +100,22 @@
     private final FeatureFlags mFeatureFlags;
 
     @Inject
-    public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanelController qsPanelController,
+    public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+            QSPanelController qsPanelController,
             QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
             QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags, QSExpansionPathInterpolator qsExpansionPathInterpolator) {
         mQs = qs;
         mQuickQsPanel = quickPanel;
         mQsPanelController = qsPanelController;
         mQuickQSPanelController = quickQSPanelController;
+        mQuickStatusBarHeader = quickStatusBarHeader;
         mSecurityFooter = securityFooter;
         mHost = qsTileHost;
         mExecutor = executor;
         mTunerService = tunerService;
         mFeatureFlags = featureFlags;
+        mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
         mHost.addCallback(this);
         mQsPanelController.addOnAttachStateChangeListener(this);
         qs.getView().addOnLayoutChangeListener(this);
@@ -252,7 +257,9 @@
                 if (count < tileLayout.getNumVisibleTiles()) {
                     getRelativePosition(loc1, quickTileView, view);
                     getRelativePosition(loc2, tileView, view);
-                    int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
+                    int yOffset = qsSideLabelsEnabled
+                            ? loc2[1] - loc1[1]
+                            : mQuickStatusBarHeader.getOffsetTranslation();
                     // Move the quick tile right from its location to the new one.
                     View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
                     translationXBuilder.addFloat(v, "translationX", 0, xDiff);
@@ -266,8 +273,14 @@
                     translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
 
                     if (qsSideLabelsEnabled) {
-                        translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
-                        translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+                        // Offset the translation animation on the views
+                        // (that goes from 0 to getOffsetTranslation)
+                        int offsetWithQSBHTranslation =
+                                yOffset - mQuickStatusBarHeader.getOffsetTranslation();
+                        translationYBuilder.addFloat(quickTileView, "translationY", 0,
+                                offsetWithQSBHTranslation);
+                        translationYBuilder.addFloat(tileView, "translationY",
+                                -offsetWithQSBHTranslation, 0);
 
                         if (mQQSTileHeightAnimator == null) {
                             mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
@@ -375,22 +388,23 @@
             }
 
             float px = 0;
-            float py = 1;
             if (tiles.size() <= 3) {
                 px = 1;
             } else if (tiles.size() <= 6) {
                 px = .4f;
             }
-            PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py);
-            translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
-            translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
+            mQSExpansionPathInterpolator.setControlX2(px);
+            translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+            translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
             mTranslationXAnimator = translationXBuilder.build();
             mTranslationYAnimator = translationYBuilder.build();
             if (mQQSTileHeightAnimator != null) {
-                mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+                mQQSTileHeightAnimator.setInterpolator(
+                        mQSExpansionPathInterpolator.getYInterpolator());
             }
             if (mOtherTilesExpandAnimator != null) {
-                mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+                mOtherTilesExpandAnimator.setInterpolator(
+                        mQSExpansionPathInterpolator.getYInterpolator());
             }
         }
         mNonfirstPageAnimator = new TouchAnimator.Builder()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
new file mode 100644
index 0000000..d351b89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.view.animation.Interpolator
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class QSExpansionPathInterpolator @Inject constructor() {
+
+    private var pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, 0f, 1f)
+    private var lastX = 0f
+    val xInterpolator: Interpolator
+        get() = pathInterpolatorBuilder.xInterpolator
+
+    val yInterpolator: Interpolator
+        get() = pathInterpolatorBuilder.yInterpolator
+
+    fun setControlX2(value: Float) {
+        if (value != lastX) {
+            lastX = value
+            pathInterpolatorBuilder = PathInterpolatorBuilder(0f, 0f, lastX, 1f)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
deleted file mode 100644
index c654621..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHeaderInfoLayout.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.qs
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
-import com.android.systemui.R
-
-/**
- * Container for the Next Alarm and Ringer status texts in [QuickStatusBarHeader].
- *
- * If both elements are visible, it splits the available space according to the following rules:
- * * If both views add up to less than the total space, they take all the space they need.
- * * If both views are larger than half the space, each view takes half the space.
- * * Otherwise, the smaller view takes the space it needs and the larger one takes all remaining
- * space.
- */
-class QSHeaderInfoLayout @JvmOverloads constructor(
-        context: Context,
-        attrs: AttributeSet? = null,
-        defStyle: Int = 0,
-        defStyleRes: Int = 0
-) : FrameLayout(context, attrs, defStyle, defStyleRes) {
-
-    private lateinit var alarmContainer: View
-    private lateinit var ringerContainer: View
-    private lateinit var statusSeparator: View
-    private val location = Location(0, 0)
-
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-        alarmContainer = findViewById(R.id.alarm_container)
-        ringerContainer = findViewById(R.id.ringer_container)
-        statusSeparator = findViewById(R.id.status_separator)
-    }
-
-    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
-        // At most one view is there
-        if (statusSeparator.visibility == View.GONE) super.onLayout(changed, l, t, r, b)
-        else {
-            val layoutRTL = isLayoutRtl
-            val width = r - l
-            val height = b - t
-            var offset = 0
-
-            offset += alarmContainer.layoutView(width, height, offset, layoutRTL)
-            offset += statusSeparator.layoutView(width, height, offset, layoutRTL)
-            ringerContainer.layoutView(width, height, offset, layoutRTL)
-        }
-    }
-
-    private fun View.layoutView(pWidth: Int, pHeight: Int, offset: Int, RTL: Boolean): Int {
-        location.setLocationFromOffset(pWidth, offset, this.measuredWidth, RTL)
-        layout(location.left, 0, location.right, pHeight)
-        return this.measuredWidth
-    }
-
-    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        super.onMeasure(
-                MeasureSpec.makeMeasureSpec(
-                        MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST),
-                heightMeasureSpec)
-        val width = MeasureSpec.getSize(widthMeasureSpec)
-        // Once we measure the views, using as much space as they need, we need to remeasure them
-        // assigning them their final width. This is because TextViews decide whether to MARQUEE
-        // after onMeasure.
-        if (statusSeparator.visibility != View.GONE) {
-            val alarmWidth = alarmContainer.measuredWidth
-            val separatorWidth = statusSeparator.measuredWidth
-            val ringerWidth = ringerContainer.measuredWidth
-            val availableSpace = MeasureSpec.getSize(width) - separatorWidth
-            if (alarmWidth < availableSpace / 2) {
-                measureChild(
-                        ringerContainer,
-                        MeasureSpec.makeMeasureSpec(
-                                Math.min(ringerWidth, availableSpace - alarmWidth),
-                                MeasureSpec.AT_MOST),
-                        heightMeasureSpec)
-            } else if (ringerWidth < availableSpace / 2) {
-                measureChild(alarmContainer,
-                        MeasureSpec.makeMeasureSpec(
-                                Math.min(alarmWidth, availableSpace - ringerWidth),
-                                MeasureSpec.AT_MOST),
-                        heightMeasureSpec)
-            } else {
-                measureChild(
-                        alarmContainer,
-                        MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
-                        heightMeasureSpec)
-                measureChild(
-                        ringerContainer,
-                        MeasureSpec.makeMeasureSpec(availableSpace / 2, MeasureSpec.AT_MOST),
-                        heightMeasureSpec)
-            }
-        }
-        setMeasuredDimension(width, measuredHeight)
-    }
-
-    private data class Location(var left: Int, var right: Int) {
-        /**
-         * Sets the [left] and [right] with the correct values for laying out the child, respecting
-         * RTL. Only set the variable through here to prevent concurrency issues.
-         * This is done to prevent allocation of [Pair] in [onLayout].
-         */
-        fun setLocationFromOffset(parentWidth: Int, offset: Int, width: Int, RTL: Boolean) {
-            if (RTL) {
-                left = parentWidth - offset - width
-                right = parentWidth - offset
-            } else {
-                left = offset
-                right = offset + width
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c794a21..95f7e20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -391,20 +391,13 @@
         index++;
 
         if (mSecurityFooter != null) {
-            LinearLayout.LayoutParams layoutParams =
-                    (LayoutParams) mSecurityFooter.getLayoutParams();
             if (mUsingHorizontalLayout && mHeaderContainer != null) {
                 // Adding the security view to the header, that enables us to avoid scrolling
-                layoutParams.width = 0;
-                layoutParams.weight = 1.6f;
-                switchToParent(mSecurityFooter, mHeaderContainer, 1 /* always in second place */);
+                switchToParent(mSecurityFooter, mHeaderContainer, 0);
             } else {
-                layoutParams.width = LayoutParams.WRAP_CONTENT;
-                layoutParams.weight = 0;
                 switchToParent(mSecurityFooter, parent, index);
                 index++;
             }
-            mSecurityFooter.setLayoutParams(layoutParams);
         }
 
         if (mFooter != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a0bf584..82ae2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,15 +17,14 @@
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import android.annotation.ColorInt;
-import android.app.AlarmManager.AlarmClockInfo;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
+import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
+
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.media.AudioManager;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.util.Pair;
@@ -34,66 +33,46 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
 import android.widget.Space;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.privacy.OngoingPrivacyChip;
 import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
 
-import java.util.Locale;
-import java.util.Objects;
-
 /**
- * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
- * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
- * contents.
+ * View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
+ * battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
  */
-public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner {
-
-    public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
+public class QuickStatusBarHeader extends FrameLayout {
 
     private boolean mExpanded;
     private boolean mQsDisabled;
 
+    private TouchAnimator mAlphaAnimator;
+    private TouchAnimator mTranslationAnimator;
+
     protected QuickQSPanel mHeaderQsPanel;
-    private TouchAnimator mStatusIconsAlphaAnimator;
-    private TouchAnimator mHeaderTextContainerAlphaAnimator;
+    private View mDatePrivacyView;
+    private View mClockIconsView;
+    private View mContainer;
 
-    private View mSystemIconsView;
-    private View mQuickQsStatusIcons;
-    private View mHeaderTextContainerView;
-
-    private ImageView mNextAlarmIcon;
-    /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
-    private TextView mNextAlarmTextView;
-    private View mNextAlarmContainer;
-    private View mStatusSeparator;
-    private ImageView mRingerModeIcon;
-    private TextView mRingerModeTextView;
-    private View mRingerContainer;
+    private View mQSCarriers;
     private Clock mClockView;
-    private OngoingPrivacyChip mPrivacyChip;
     private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
-    private TintedIconManager mTintedIconManager;
+    private StatusIconContainer mIconContainer;
 
-    // Used for RingerModeTracker
-    private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+
+    private TintedIconManager mTintedIconManager;
+    private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
 
     private int mStatusBarPaddingTop = 0;
     private int mRoundedCornerPadding = 0;
@@ -102,12 +81,26 @@
     private int mWaterfallTopInset;
     private int mCutOutPaddingLeft;
     private int mCutOutPaddingRight;
-    private float mExpandedHeaderAlpha = 1.0f;
+    private float mClockIconsAlpha = 1.0f;
+    private float mDatePrivacyAlpha = 1.0f;
     private float mKeyguardExpansionFraction;
     private int mTextColorPrimary = Color.TRANSPARENT;
+    private int mTopViewMeasureHeight;
+
+    private final String mMobileSlotName;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
+    }
+
+    /**
+     * How much the view containing the clock and QQS will translate down when QS is fully expanded.
+     *
+     * This matches the measured height of the view containing the date and privacy icons.
+     */
+    public int getOffsetTranslation() {
+        return mTopViewMeasureHeight;
     }
 
     @Override
@@ -115,19 +108,12 @@
         super.onFinishInflate();
 
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
-        mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
-        mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
+        mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
+        mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+        mQSCarriers = findViewById(R.id.carrier_group);
+        mContainer = findViewById(R.id.container);
+        mIconContainer = findViewById(R.id.statusIcons);
 
-        // Views corresponding to the header info section (e.g. ringer and next alarm).
-        mHeaderTextContainerView = findViewById(R.id.header_text_container);
-        mStatusSeparator = findViewById(R.id.status_separator);
-        mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
-        mNextAlarmTextView = findViewById(R.id.next_alarm_text);
-        mNextAlarmContainer = findViewById(R.id.alarm_container);
-        mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
-        mRingerModeTextView = findViewById(R.id.ringer_mode_text);
-        mRingerContainer = findViewById(R.id.ringer_container);
-        mPrivacyChip = findViewById(R.id.privacy_chip);
         mClockView = findViewById(R.id.clock);
         mSpace = findViewById(R.id.space);
         // Tint for the battery icons are handled in setupHost()
@@ -140,79 +126,34 @@
         // QS will always show the estimate, and BatteryMeterView handles the case where
         // it's unavailable or charging
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
-        mRingerModeTextView.setSelected(true);
-        mNextAlarmTextView.setSelected(true);
     }
 
-    void onAttach(TintedIconManager iconManager) {
+    void onAttach(TintedIconManager iconManager,
+            QSExpansionPathInterpolator qsExpansionPathInterpolator) {
         mTintedIconManager = iconManager;
         int fillColor = Utils.getColorAttrDefaultColor(getContext(),
                 android.R.attr.textColorPrimary);
 
         // Set the correct tint for the status icons so they contrast
         iconManager.setTint(fillColor);
-        mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
-        mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
+
+        mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
+        updateAnimators();
     }
 
     public QuickQSPanel getHeaderQsPanel() {
         return mHeaderQsPanel;
     }
 
-    void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger,
-            boolean use24HourFormat) {
-        boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger)
-                || updateAlarmStatus(nextAlarm, use24HourFormat);
-
-        if (changed) {
-            boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
-            boolean ringerVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
-            mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE
-                    : View.GONE);
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
+            mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
+            updateAnimators();
         }
     }
 
-    private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) {
-        boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
-        CharSequence originalRingerText = mRingerModeTextView.getText();
-
-        boolean ringerVisible = false;
-        if (!zenOverridingRinger) {
-            if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-                mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
-                mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
-                ringerVisible = true;
-            } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-                mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
-                mRingerModeTextView.setText(R.string.qs_status_phone_muted);
-                ringerVisible = true;
-            }
-        }
-        mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
-        mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
-        mRingerContainer.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
-
-        return isOriginalVisible != ringerVisible ||
-                !Objects.equals(originalRingerText, mRingerModeTextView.getText());
-    }
-
-    private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) {
-        boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
-        CharSequence originalAlarmText = mNextAlarmTextView.getText();
-
-        boolean alarmVisible = false;
-        if (nextAlarm != null) {
-            alarmVisible = true;
-            mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat));
-        }
-        mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
-        mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
-        mNextAlarmContainer.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
-
-        return isOriginalVisible != alarmVisible ||
-                !Objects.equals(originalAlarmText, mNextAlarmTextView.getText());
-    }
-
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -225,40 +166,27 @@
         updateResources();
     }
 
-    /**
-     * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
-     * when there is a notch involved the status bar can remain a fixed pixel size.
-     */
-    private void updateMinimumHeight() {
-        int sbHeight = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
-        int qqsHeight = mContext.getResources().getDimensionPixelSize(
-                R.dimen.qs_quick_header_panel_height);
-
-        setMinimumHeight(sbHeight + qqsHeight);
-    }
-
     void updateResources() {
         Resources resources = mContext.getResources();
-        updateMinimumHeight();
 
         mRoundedCornerPadding = resources.getDimensionPixelSize(
                 R.dimen.rounded_corner_content_padding);
         mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top);
 
-        // Update height for a few views, especially due to landscape mode restricting space.
-        mHeaderTextContainerView.getLayoutParams().height =
-                resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
-        mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
-
-        mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
+        int qsOffsetHeight = resources.getDimensionPixelSize(
                 com.android.internal.R.dimen.quick_qs_offset_height);
-        mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+
+        mDatePrivacyView.getLayoutParams().height =
+                Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
+        mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
+
+        mClockIconsView.getLayoutParams().height =
+                Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
+        mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
 
         ViewGroup.LayoutParams lp = getLayoutParams();
         if (mQsDisabled) {
-            lp.height = resources.getDimensionPixelSize(
-                    com.android.internal.R.dimen.quick_qs_offset_height);
+            lp.height = mClockIconsView.getLayoutParams().height;
         } else {
             lp.height = WRAP_CONTENT;
         }
@@ -276,21 +204,45 @@
             mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary,
                     mTextColorPrimary);
         }
-
-        updateStatusIconAlphaAnimator();
-        updateHeaderTextContainerAlphaAnimator();
+        updateHeadersPadding();
+        updateAnimators();
     }
 
-    private void updateStatusIconAlphaAnimator() {
-        mStatusIconsAlphaAnimator = new TouchAnimator.Builder()
-                .addFloat(mQuickQsStatusIcons, "alpha", 1, 0, 0)
+    private void updateAnimators() {
+        updateAlphaAnimator();
+        int offset = mTopViewMeasureHeight;
+
+        mTranslationAnimator = new TouchAnimator.Builder()
+                .addFloat(mContainer, "translationY", 0, offset)
+                .setInterpolator(mQSExpansionPathInterpolator != null
+                        ? mQSExpansionPathInterpolator.getYInterpolator()
+                        : null)
                 .build();
     }
 
-    private void updateHeaderTextContainerAlphaAnimator() {
-        mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
-                .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha)
-                .build();
+    private void updateAlphaAnimator() {
+        StatusBarMobileView icon =
+                ((StatusBarMobileView) mIconContainer.getViewForSlot(mMobileSlotName));
+        TouchAnimator.Builder builder = new TouchAnimator.Builder()
+                .addFloat(mQSCarriers, "alpha", 0, 1)
+                .addFloat(mDatePrivacyView, "alpha", 0, mDatePrivacyAlpha);
+        if (icon != null) {
+            builder.addFloat(icon, "alpha", 1, 0);
+            builder.setListener(new TouchAnimator.ListenerAdapter() {
+                @Override
+                public void onAnimationAtEnd() {
+                    icon.forceHidden(true);
+                    icon.setVisibleState(STATE_HIDDEN);
+                }
+
+                @Override
+                public void onAnimationStarted() {
+                    icon.forceHidden(false);
+                    icon.setVisibleState(STATE_ICON);
+                }
+            });
+        }
+        mAlphaAnimator = builder.build();
     }
 
     /** */
@@ -312,25 +264,19 @@
     public void setExpansion(boolean forceExpanded, float expansionFraction,
                              float panelTranslationY) {
         final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction;
-        if (mStatusIconsAlphaAnimator != null) {
-            mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction);
-        }
 
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.setPosition(keyguardExpansionFraction);
+        }
+        if (mTranslationAnimator != null) {
+            mTranslationAnimator.setPosition(keyguardExpansionFraction);
+        }
+        // If forceExpanded (we are opening QS from lockscreen), the animators have been set to
+        // position = 1f.
         if (forceExpanded) {
-            // If the keyguard is showing, we want to offset the text so that it comes in at the
-            // same time as the panel as it slides down.
-            mHeaderTextContainerView.setTranslationY(panelTranslationY);
+            setTranslationY(panelTranslationY);
         } else {
-            mHeaderTextContainerView.setTranslationY(0f);
-        }
-
-        if (mHeaderTextContainerAlphaAnimator != null) {
-            mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
-            if (keyguardExpansionFraction > 0) {
-                mHeaderTextContainerView.setVisibility(VISIBLE);
-            } else {
-                mHeaderTextContainerView.setVisibility(INVISIBLE);
-            }
+            setTranslationY(0);
         }
 
         mKeyguardExpansionFraction = keyguardExpansionFraction;
@@ -341,28 +287,21 @@
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
         mHeaderQsPanel.setDisabledByPolicy(disabled);
-        mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-        mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
         updateResources();
     }
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        // Handle padding of the clock
+        // Handle padding of the views
         DisplayCutout cutout = insets.getDisplayCutout();
         Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
                 cutout, getDisplay());
         Pair<Integer, Integer> padding =
                 StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
                         cutout, cornerCutoutPadding, -1);
-        if (padding == null) {
-            mSystemIconsView.setPaddingRelative(
-                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
-                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
-        } else {
-            mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
-
-        }
+        mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
+        mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
         LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
         boolean cornerCutout = cornerCutoutPadding != null
                 && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
@@ -380,13 +319,13 @@
         mCutOutPaddingLeft = padding.first;
         mCutOutPaddingRight = padding.second;
         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
-        updateClockPadding();
+        updateHeadersPadding();
         return super.onApplyWindowInsets(insets);
     }
 
-    private void updateClockPadding() {
-        int clockPaddingLeft = 0;
-        int clockPaddingRight = 0;
+    private void updateHeadersPadding() {
+        int paddingLeft = 0;
+        int paddingRight = 0;
 
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
         int leftMargin = lp.leftMargin;
@@ -399,18 +338,22 @@
             // if there's a cutout, let's use at least the rounded corner inset
             int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
             int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
-            clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
+            paddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
         }
         if (mCutOutPaddingRight > 0) {
             // if there's a cutout, let's use at least the rounded corner inset
             int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
             int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
-            clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
+            paddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
         }
 
-        mSystemIconsView.setPadding(clockPaddingLeft,
+        mDatePrivacyView.setPadding(paddingLeft,
                 mWaterfallTopInset + mStatusBarPaddingTop,
-                clockPaddingRight,
+                paddingRight,
+                0);
+        mClockIconsView.setPadding(paddingLeft,
+                mWaterfallTopInset + mStatusBarPaddingTop,
+                paddingRight,
                 0);
     }
 
@@ -422,26 +365,6 @@
         mHeaderQsPanel.setCallback(qsPanelCallback);
     }
 
-    private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) {
-        if (info == null) {
-            return "";
-        }
-        String skeleton = use24HourFormat ? "EHm" : "Ehma";
-        String pattern = android.text.format.DateFormat
-                .getBestDateTimePattern(Locale.getDefault(), skeleton);
-        return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
-    }
-
-    public static float getColorIntensity(@ColorInt int color) {
-        return color == Color.WHITE ? 0 : 1;
-    }
-
-    @NonNull
-    @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycle;
-    }
-
     /** */
     public void setContentMargins(int marginStart, int marginEnd,
             QuickQSPanelController quickQSPanelController) {
@@ -459,24 +382,39 @@
                 view.setLayoutParams(lp);
             }
         }
-        updateClockPadding();
+        updateHeadersPadding();
     }
 
+    /**
+     * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
+     *
+     * For a given scroll level, this method does the following:
+     * <ol>
+     *     <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
+     *         expanded.</li>
+     *     <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
+     *     <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
+     *         the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
+     *         expanded), matching the animator.</li>
+     * </ol>
+     *
+     * @param scrollY the scroll of the QSPanel container
+     */
     public void setExpandedScrollAmount(int scrollY) {
         // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
         // but would overlap content, we're fading it out.
         float newAlpha = 1.0f;
-        if (mHeaderTextContainerView.getHeight() > 0) {
-            newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f,
+        if (mClockIconsView.getHeight() > 0) {
+            newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
                     scrollY);
             newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
         }
-        mHeaderTextContainerView.setScrollY(scrollY);
-        if (newAlpha != mExpandedHeaderAlpha) {
-            mExpandedHeaderAlpha = newAlpha;
-            mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha,
+        mClockIconsView.setScrollY(scrollY);
+        if (newAlpha != mClockIconsAlpha) {
+            mClockIconsAlpha = newAlpha;
+            mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
                     mKeyguardExpansionFraction));
-            updateHeaderTextContainerAlphaAnimator();
+            updateAlphaAnimator();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index b1689f6..3aafea98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -16,21 +16,13 @@
 
 package com.android.systemui.qs;
 
-import android.app.AlarmManager.AlarmClockInfo;
 import android.content.Intent;
-import android.media.AudioManager;
 import android.os.Bundle;
 import android.provider.AlarmClock;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 
 import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
@@ -47,15 +39,9 @@
 import com.android.systemui.privacy.logging.PrivacyLogger;
 import com.android.systemui.qs.carrier.QSCarrierGroupController;
 import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
-import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.ViewController;
 
 import java.util.ArrayList;
@@ -70,75 +56,29 @@
 class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
     private static final String TAG = "QuickStatusBarHeader";
 
-    private final ZenModeController mZenModeController;
-    private final NextAlarmController mNextAlarmController;
     private final PrivacyItemController mPrivacyItemController;
-    private final RingerModeTracker mRingerModeTracker;
     private final ActivityStarter mActivityStarter;
     private final UiEventLogger mUiEventLogger;
     private final QSCarrierGroupController mQSCarrierGroupController;
     private final QuickQSPanelController mHeaderQsPanelController;
-    private final LifecycleRegistry mLifecycle;
     private final OngoingPrivacyChip mPrivacyChip;
     private final Clock mClockView;
-    private final View mNextAlarmContainer;
-    private final View mRingerContainer;
-    private final QSTileHost mQSTileHost;
     private final StatusBarIconController mStatusBarIconController;
     private final DemoModeController mDemoModeController;
-    private final UserTracker mUserTracker;
     private final StatusIconContainer mIconContainer;
     private final StatusBarIconController.TintedIconManager mIconManager;
     private final DemoMode mDemoModeReceiver;
     private final PrivacyLogger mPrivacyLogger;
     private final PrivacyDialogController mPrivacyDialogController;
+    private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
 
     private boolean mListening;
-    private AlarmClockInfo mNextAlarm;
     private boolean mMicCameraIndicatorsEnabled;
     private boolean mLocationIndicatorsEnabled;
     private boolean mPrivacyChipLogged;
 
     private SysuiColorExtractor mColorExtractor;
     private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
-    private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
-
-    private final ZenModeController.Callback mZenModeControllerCallback = new Callback() {
-        @Override
-        public void onZenChanged(int zen) {
-            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
-                    use24HourFormat());
-        }
-
-        @Override
-        public void onConfigChanged(ZenModeConfig config) {
-            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
-                    use24HourFormat());
-        }
-    };
-
-    private boolean use24HourFormat() {
-        return android.text.format.DateFormat.is24HourFormat(
-                mView.getContext(), mUserTracker.getUserId());
-
-    }
-
-    private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
-        @Override
-        public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
-            mNextAlarm = nextAlarm;
-            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
-                    use24HourFormat());
-        }
-    };
-
-    private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() {
-        @NonNull
-        @Override
-        public Lifecycle getLifecycle() {
-            return mLifecycle;
-        }
-    };
 
     private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
         @Override
@@ -176,63 +116,43 @@
             if (v == mClockView) {
                 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                         AlarmClock.ACTION_SHOW_ALARMS), 0);
-            } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
-                if (mNextAlarm.getShowIntent() != null) {
-                    mActivityStarter.postStartActivityDismissingKeyguard(
-                            mNextAlarm.getShowIntent());
-                } else {
-                    Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
-                    mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                            AlarmClock.ACTION_SHOW_ALARMS), 0);
-                }
             } else if (v == mPrivacyChip) {
                 // If the privacy chip is visible, it means there were some indicators
                 mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
                 mPrivacyDialogController.showDialog(getContext());
-            } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
-                mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                        Settings.ACTION_SOUND_SETTINGS), 0);
             }
         }
     };
 
     @Inject
     QuickStatusBarHeaderController(QuickStatusBarHeader view,
-            ZenModeController zenModeController, NextAlarmController nextAlarmController,
-            PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
+            PrivacyItemController privacyItemController,
             ActivityStarter activityStarter, UiEventLogger uiEventLogger,
-            QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
+            StatusBarIconController statusBarIconController,
             DemoModeController demoModeController,
-            UserTracker userTracker, QuickQSPanelController quickQSPanelController,
+            QuickQSPanelController quickQSPanelController,
             QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
             PrivacyLogger privacyLogger,
             SysuiColorExtractor colorExtractor,
-            PrivacyDialogController privacyDialogController) {
+            PrivacyDialogController privacyDialogController,
+            QSExpansionPathInterpolator qsExpansionPathInterpolator) {
         super(view);
-        mZenModeController = zenModeController;
-        mNextAlarmController = nextAlarmController;
         mPrivacyItemController = privacyItemController;
-        mRingerModeTracker = ringerModeTracker;
         mActivityStarter = activityStarter;
         mUiEventLogger = uiEventLogger;
-        mQSTileHost = qsTileHost;
         mStatusBarIconController = statusBarIconController;
         mDemoModeController = demoModeController;
-        mUserTracker = userTracker;
-        mLifecycle = new LifecycleRegistry(mLifecycleOwner);
         mHeaderQsPanelController = quickQSPanelController;
         mPrivacyLogger = privacyLogger;
         mPrivacyDialogController = privacyDialogController;
+        mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
 
         mQSCarrierGroupController = qsCarrierGroupControllerBuilder
                 .setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
                 .build();
 
-
         mPrivacyChip = mView.findViewById(R.id.privacy_chip);
-        mNextAlarmContainer = mView.findViewById(R.id.alarm_container);
         mClockView = mView.findViewById(R.id.clock);
-        mRingerContainer = mView.findViewById(R.id.ringer_container);
         mIconContainer = mView.findViewById(R.id.statusIcons);
 
         mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer);
@@ -247,15 +167,7 @@
 
     @Override
     protected void onViewAttached() {
-        mRingerModeTracker.getRingerModeInternal().observe(mLifecycleOwner, ringer -> {
-            mRingerMode = ringer;
-            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
-                    use24HourFormat());
-        });
-
         mClockView.setOnClickListener(mOnClickListener);
-        mNextAlarmContainer.setOnClickListener(mOnClickListener);
-        mRingerContainer.setOnClickListener(mOnClickListener);
         mPrivacyChip.setOnClickListener(mOnClickListener);
 
         mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
@@ -268,18 +180,15 @@
 
         setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
 
-        mView.onAttach(mIconManager);
+        mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
 
         mDemoModeController.addCallback(mDemoModeReceiver);
     }
 
     @Override
     protected void onViewDetached() {
-        mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner);
         mClockView.setOnClickListener(null);
         mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
-        mNextAlarmContainer.setOnClickListener(null);
-        mRingerContainer.setOnClickListener(null);
         mPrivacyChip.setOnClickListener(null);
         mStatusBarIconController.removeIconGroup(mIconManager);
         mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -304,17 +213,11 @@
         }
 
         if (listening) {
-            mZenModeController.addCallback(mZenModeControllerCallback);
-            mNextAlarmController.addCallback(mNextAlarmChangeCallback);
-            mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
             // Get the most up to date info
             mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
             mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
         } else {
-            mZenModeController.removeCallback(mZenModeControllerCallback);
-            mNextAlarmController.removeCallback(mNextAlarmChangeCallback);
-            mLifecycle.setCurrentState(Lifecycle.State.CREATED);
             mPrivacyItemController.removeCallback(mPICCallback);
             mPrivacyChipLogged = false;
         }
@@ -357,11 +260,6 @@
         return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
     }
 
-    private boolean isZenOverridingRinger() {
-        return ZenModeConfig.isZenOverridingRinger(mZenModeController.getZen(),
-                mZenModeController.getConsolidatedPolicy());
-    }
-
     public void setContentMargins(int contentPaddingStart, int contentPaddingEnd) {
         mView.setContentMargins(contentPaddingStart, contentPaddingEnd, mHeaderQsPanelController);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 0ebadfd..d1deeca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.util.AttributeSet
+import com.android.systemui.R
 
 open class SideLabelTileLayout(
     context: Context,
@@ -26,7 +27,7 @@
 
     override fun updateResources(): Boolean {
         return super.updateResources().also {
-            mMaxAllowedRows = 4
+            mMaxAllowedRows = context.resources.getInteger(R.integer.quick_settings_max_rows)
         }
     }
 
@@ -44,4 +45,19 @@
         val row = index / mColumns
         return getRowTop(row)
     }
+
+    override fun updateMaxRows(allowedHeight: Int, tilesCount: Int): Boolean {
+        val previousRows = mRows
+        mRows = mMaxAllowedRows
+        // We want at most mMaxAllowedRows, but it could be that we don't have enough tiles to fit
+        // that many rows. In that case, we want
+        // `tilesCount = (mRows - 1) * mColumns + X`
+        // where X is some remainder between 1 and `mColumns - 1`
+        // Adding `mColumns - 1` will guarantee that the final value F will satisfy
+        // `mRows * mColumns <= F < (mRows + 1) * mColumns
+        if (mRows > (tilesCount + mColumns - 1) / mColumns) {
+            mRows = (tilesCount + mColumns - 1) / mColumns
+        }
+        return previousRows != mRows
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index f9d1def..a17aeba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -56,7 +56,6 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
-import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -67,7 +66,6 @@
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSEvent;
 import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QuickStatusBarHeader;
 import com.android.systemui.qs.logging.QSLogger;
 
 import java.io.FileDescriptor;
@@ -300,11 +298,6 @@
                 getInstanceId());
         mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
         mHandler.sendEmptyMessage(H.LONG_CLICK);
-
-        Prefs.putInt(
-                mContext,
-                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
-                QuickStatusBarHeader.MAX_TOOLTIP_SHOWN_COUNT);
     }
 
     public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 0b40e22..5d964a4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -27,7 +27,10 @@
 import androidx.annotation.Nullable;
 
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.util.ViewController;
 
@@ -42,9 +45,7 @@
  *
  * @see BrightnessMirrorController
  */
-public class BrightnessSlider
-        extends ViewController<View>
-        implements ToggleSlider {
+public class BrightnessSlider extends ViewController<View> implements ToggleSlider {
 
     private Listener mListener;
     private ToggleSlider mMirror;
@@ -52,15 +53,34 @@
     private BrightnessMirrorController mMirrorController;
     private boolean mTracking;
     private final boolean mUseMirror;
+    private final FalsingManager mFalsingManager;
+
+    private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            int action = ev.getActionMasked();
+            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+                mFalsingManager.isFalseTouch(Classifier.BRIGHTNESS_SLIDER);
+            }
+
+            return false;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            return false;
+        }
+    };
 
     BrightnessSlider(
             View rootView,
             BrightnessSliderView brightnessSliderView,
-            boolean useMirror
-    ) {
+            boolean useMirror,
+            FalsingManager falsingManager) {
         super(rootView);
         mBrightnessSliderView = brightnessSliderView;
         mUseMirror = useMirror;
+        mFalsingManager = falsingManager;
     }
 
     /**
@@ -78,6 +98,7 @@
     protected void onViewAttached() {
         mBrightnessSliderView.setOnSeekBarChangeListener(mSeekListener);
         mBrightnessSliderView.setOnCheckedChangeListener(mCheckListener);
+        mBrightnessSliderView.setOnInterceptListener(mOnInterceptListener);
     }
 
     @Override
@@ -85,6 +106,7 @@
         mBrightnessSliderView.setOnSeekBarChangeListener(null);
         mBrightnessSliderView.setOnCheckedChangeListener(null);
         mBrightnessSliderView.setOnDispatchTouchEventListener(null);
+        mBrightnessSliderView.setOnInterceptListener(null);
     }
 
     @Override
@@ -247,10 +269,12 @@
     public static class Factory {
 
         BrightnessControllerSettings mSettings;
+        private final FalsingManager mFalsingManager;
 
         @Inject
-        public Factory(BrightnessControllerSettings settings) {
+        public Factory(BrightnessControllerSettings settings, FalsingManager falsingManager) {
             mSettings = settings;
+            mFalsingManager = falsingManager;
         }
 
         /**
@@ -270,7 +294,7 @@
         private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) {
             BrightnessSliderView v = root.requireViewById(R.id.brightness_slider);
 
-            return new BrightnessSlider(root, v, useMirror);
+            return new BrightnessSlider(root, v, useMirror, mFalsingManager);
         }
 
         /** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index cbf4e88..5b71c62 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -31,6 +31,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 
 /**
@@ -54,6 +55,7 @@
     private TextView mLabel;
     private final CharSequence mText;
     private DispatchTouchEventListener mListener;
+    private Gefingerpoken mOnInterceptListener;
 
     public BrightnessSliderView(Context context) {
         this(context, null);
@@ -105,6 +107,15 @@
         return super.dispatchTouchEvent(ev);
     }
 
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        // We prevent disallowing on this view, but bubble it up to our parents.
+        // We need interception to handle falsing.
+        if (mParent != null) {
+            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
+        }
+    }
+
     /**
      * Attaches a listener to the {@link ToggleSeekBar} in the view so changes can be observed
      * @param seekListener use {@code null} to remove listener
@@ -195,6 +206,18 @@
         return mSlider.getProgress();
     }
 
+    public void setOnInterceptListener(Gefingerpoken onInterceptListener) {
+        mOnInterceptListener = onInterceptListener;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (mOnInterceptListener != null) {
+            return mOnInterceptListener.onInterceptTouchEvent(ev);
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
     /**
      * Interface to attach a listener for {@link View#dispatchTouchEvent}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 138c811..ab17ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -59,6 +59,7 @@
     private View mMobileRoamingSpace;
     private int mVisibleState = -1;
     private DualToneHandler mDualToneHandler;
+    private boolean mForceHidden;
 
     public static StatusBarMobileView fromContext(Context context, String slot) {
         LayoutInflater inflater = LayoutInflater.from(context);
@@ -150,7 +151,7 @@
 
     private void initViewState() {
         setContentDescription(mState.contentDescription);
-        if (!mState.visible) {
+        if (!mState.visible || mForceHidden) {
             mMobileGroup.setVisibility(View.GONE);
         } else {
             mMobileGroup.setVisibility(View.VISIBLE);
@@ -176,8 +177,9 @@
         boolean needsLayout = false;
 
         setContentDescription(state.contentDescription);
-        if (mState.visible != state.visible) {
-            mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+        int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
+        if (newVisibility != mMobileGroup.getVisibility()) {
+            mMobileGroup.setVisibility(newVisibility);
             needsLayout = true;
         }
         if (mState.strengthId != state.strengthId) {
@@ -252,7 +254,7 @@
 
     @Override
     public boolean isIconVisible() {
-        return mState.visible;
+        return mState.visible && !mForceHidden;
     }
 
     @Override
@@ -279,6 +281,23 @@
         }
     }
 
+    /**
+     * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
+     *
+     * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
+     * is enabled.
+     * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
+     *                                state.
+     *               {@code false} if the icon should show as determined by its controller.
+     */
+    public void forceHidden(boolean forceHidden) {
+        if (mForceHidden != forceHidden) {
+            mForceHidden = forceHidden;
+            updateState(mState);
+            requestLayout();
+        }
+    }
+
     @Override
     public int getVisibleState() {
         return mVisibleState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index c6f7983..779ba1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.util.leak.RotationUtils;
 
 /**
@@ -91,6 +93,7 @@
      * @param roundedCornerContentPadding
      * @return
      */
+    @NonNull
     public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
             DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
             int roundedCornerContentPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index f65f97a..64a497d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -262,6 +262,25 @@
     }
 
     /**
+     * Returns the view corresponding to a particular slot.
+     *
+     * Use it solely to manipulate how it is presented.
+     * @param slot name of the slot to find. Names are defined in
+     *            {@link com.android.internal.R.config_statusBarIcons}
+     * @return a view for the slot if this container has it, else {@code null}
+     */
+    public View getViewForSlot(String slot) {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (child instanceof StatusIconDisplayable
+                    && ((StatusIconDisplayable) child).getSlot().equals(slot)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 6e78059..5a78ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -921,13 +921,18 @@
                 if (clip.getItemCount() > 1
                         || description.getMimeTypeCount() < 1
                         || remainingItems != null) {
-                    // TODO(b/172363500): Update to loop over all the items
+                    // Direct-reply in notifications currently only supports only one uri item
+                    // at a time and requires the MIME type to be set.
+                    Log.w(TAG, "Invalid payload: " + payload);
                     return payload;
                 }
                 Uri contentUri = clip.getItemAt(0).getUri();
                 String mimeType = description.getMimeType(0);
                 Intent dataIntent =
                         mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri);
+                // We can release the uri permissions granted to us as soon as we've created the
+                // grant for the target app in the call above.
+                payload.releasePermissions();
                 mRemoteInputView.sendRemoteInput(dataIntent);
             }
             return remainingItems;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index db06199..c912419 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.classifier;
 
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.testing.AndroidTestingRunner;
@@ -96,13 +98,9 @@
     }
 
     @Test
-    public void testPass_swipe() {
-
+    public void testPass_BrightnessSliderAlwaysPasses() {
         mClassifier.onTouchEvent(appendDownEvent(1, 1));
-        assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isTrue();
-
-        mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
-        mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
-        assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse())
+                .isFalse();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
index 52423bd..60786f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.classifier;
 
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
@@ -72,7 +73,7 @@
     public void testPass_uncovered() {
         touchDown();
         touchUp(10);
-        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
     }
 
     @Test
@@ -81,7 +82,7 @@
         mClassifier.onProximityEvent(createSensorEvent(true, 1));
         mClassifier.onProximityEvent(createSensorEvent(false, 2));
         touchUp(20);
-        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
     }
 
     @Test
@@ -90,7 +91,17 @@
         mClassifier.onProximityEvent(createSensorEvent(true, 1));
         mClassifier.onProximityEvent(createSensorEvent(false, 11));
         touchUp(10);
-        assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse(), is(false));
+        assertThat(mClassifier.classifyGesture(QUICK_SETTINGS, 0.5, 0).isFalse()).isFalse();
+    }
+
+    @Test
+    public void testPass_brightnessSlider() {
+        touchDown();
+        mClassifier.onProximityEvent(createSensorEvent(true, 1));
+        mClassifier.onProximityEvent(createSensorEvent(false, 11));
+        touchUp(10);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse())
+                .isFalse();
     }
 
     @Test
@@ -99,7 +110,7 @@
         mClassifier.onProximityEvent(createSensorEvent(true, 1));
         mClassifier.onProximityEvent(createSensorEvent(false, 11));
         touchUp(10);
-        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
+        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
     }
 
     @Test
@@ -110,7 +121,7 @@
         mClassifier.onProximityEvent(createSensorEvent(true, 96));
         mClassifier.onProximityEvent(createSensorEvent(false, 100));
         touchUp(100);
-        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(true));
+        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isTrue();
     }
 
     @Test
@@ -120,7 +131,7 @@
         mClassifier.onProximityEvent(createSensorEvent(false, 11));
         touchUp(10);
         when(mDistanceClassifier.isLongSwipe()).thenReturn(mPassedResult);
-        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse(), is(false));
+        assertThat(mClassifier.classifyGesture(GENERIC, 0.5, 0).isFalse()).isFalse();
     }
 
     private void touchDown() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 068a1e5..32537b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.classifier;
 
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
 import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
@@ -277,4 +278,46 @@
         when(mDataProvider.isRight()).thenReturn(true);
         assertThat(mClassifier.classifyGesture(RIGHT_AFFORDANCE, 0.5, 0).isFalse()).isTrue();
     }
+
+    @Test
+    public void testPass_BrightnessSlider() {
+        when(mDataProvider.isVertical()).thenReturn(false);
+
+        when(mDataProvider.isUp()).thenReturn(false);  // up and right should cause no effect.
+        when(mDataProvider.isRight()).thenReturn(false);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+        when(mDataProvider.isUp()).thenReturn(true);
+        when(mDataProvider.isRight()).thenReturn(false);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+        when(mDataProvider.isUp()).thenReturn(false);
+        when(mDataProvider.isRight()).thenReturn(true);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+
+        when(mDataProvider.isUp()).thenReturn(true);
+        when(mDataProvider.isRight()).thenReturn(true);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isFalse();
+    }
+
+    @Test
+    public void testFalse_BrightnessSlider() {
+        when(mDataProvider.isVertical()).thenReturn(true);
+
+        when(mDataProvider.isUp()).thenReturn(false);  // up and right should cause no effect.
+        when(mDataProvider.isRight()).thenReturn(false);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+        when(mDataProvider.isUp()).thenReturn(true);
+        when(mDataProvider.isRight()).thenReturn(false);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+        when(mDataProvider.isUp()).thenReturn(false);
+        when(mDataProvider.isRight()).thenReturn(true);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+
+        when(mDataProvider.isUp()).thenReturn(true);
+        when(mDataProvider.isRight()).thenReturn(true);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
index e004c30..c343c20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.classifier;
 
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.testing.AndroidTestingRunner;
@@ -82,6 +84,13 @@
         assertThat(mClassifier.classifyGesture(0, 0.5, 1).isFalse()).isFalse();
     }
 
+    @Test
+    public void testPass_brightnessSliderAlwaysPasses() {
+        appendMoveEvent(0, 0);
+        appendMoveEvent(0, 100);
+        appendMoveEvent(0, 1);
+        assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse()).isFalse();
+    }
 
     @Test
     public void testFail_minimumTouchesVertical() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 3595095..3c1b36e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -21,9 +21,9 @@
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.colorextraction.SysuiColorExtractor
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.colorextraction.SysuiColorExtractor
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
@@ -32,16 +32,12 @@
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusIconContainer
 import com.android.systemui.statusbar.policy.Clock
-import com.android.systemui.statusbar.policy.NextAlarmController
-import com.android.systemui.util.RingerModeTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
-import com.android.systemui.utils.leaks.FakeZenModeController
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -61,26 +57,16 @@
     @Mock
     private lateinit var view: QuickStatusBarHeader
     @Mock
-    private lateinit var zenModeController: FakeZenModeController
-    @Mock
-    private lateinit var nextAlarmController: NextAlarmController
-    @Mock
     private lateinit var privacyItemController: PrivacyItemController
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private lateinit var ringerModeTracker: RingerModeTracker
     @Mock
     private lateinit var activityStarter: ActivityStarter
     @Mock
     private lateinit var uiEventLogger: UiEventLogger
     @Mock
-    private lateinit var qsTileHost: QSTileHost
-    @Mock
     private lateinit var statusBarIconController: StatusBarIconController
     @Mock
     private lateinit var demoModeController: DemoModeController
     @Mock
-    private lateinit var userTracker: UserTracker
-    @Mock
     private lateinit var quickQSPanelController: QuickQSPanelController
     @Mock(answer = Answers.RETURNS_SELF)
     private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
@@ -105,6 +91,8 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private lateinit var context: Context
 
+    private val qsExpansionPathInterpolator = QSExpansionPathInterpolator()
+
     private lateinit var controller: QuickStatusBarHeaderController
 
     @Before
@@ -119,21 +107,17 @@
 
         controller = QuickStatusBarHeaderController(
                 view,
-                zenModeController,
-                nextAlarmController,
                 privacyItemController,
-                ringerModeTracker,
                 activityStarter,
                 uiEventLogger,
-                qsTileHost,
                 statusBarIconController,
                 demoModeController,
-                userTracker,
                 quickQSPanelController,
                 qsCarrierGroupControllerBuilder,
                 privacyLogger,
                 colorExtractor,
-                privacyDialogController
+                privacyDialogController,
+                qsExpansionPathInterpolator
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 022dc84..104b625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -21,10 +21,13 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.FeatureFlagUtils;
 import android.view.LayoutInflater;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
@@ -39,6 +42,7 @@
 
     private QSCarrier mQSCarrier;
     private TestableLooper mTestableLooper;
+    private int mSignalIconId;
 
     @Before
     public void setUp() throws Exception {
@@ -46,18 +50,26 @@
         LayoutInflater inflater = LayoutInflater.from(mContext);
         mTestableLooper.runWithLooper(() ->
                 mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+
+        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            // In this case, the id is an actual drawable id
+            mSignalIconId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
+        } else {
+            // In this case, the id is a level
+            mSignalIconId = SignalDrawable.getEmptyState(5);
+        }
     }
 
     @Test
     public void testUpdateState_first() {
-        CellSignalState c = new CellSignalState(true, 0, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c));
     }
 
     @Test
     public void testUpdateState_same() {
-        CellSignalState c = new CellSignalState(true, 0, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c));
         assertFalse(mQSCarrier.updateState(c));
@@ -65,7 +77,7 @@
 
     @Test
     public void testUpdateState_changed() {
-        CellSignalState c = new CellSignalState(true, 0, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
 
         assertTrue(mQSCarrier.updateState(c));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
index 0cc2072..95e9d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.RestrictedLockUtils
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.statusbar.policy.BrightnessMirrorController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
@@ -75,6 +76,7 @@
     private lateinit var checkedChangeCaptor: ArgumentCaptor<CompoundButton.OnCheckedChangeListener>
     @Mock
     private lateinit var compoundButton: CompoundButton
+    private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
 
     private lateinit var mController: BrightnessSlider
 
@@ -85,7 +87,7 @@
         whenever(mirrorController.toggleSlider).thenReturn(mirror)
         whenever(motionEvent.copy()).thenReturn(motionEvent)
 
-        mController = BrightnessSlider(rootView, brightnessSliderView, true)
+        mController = BrightnessSlider(rootView, brightnessSliderView, true, mFalsingManager)
         mController.init()
         mController.setOnChangedListener(listener)
     }
@@ -160,7 +162,8 @@
 
     @Test
     fun testSettingMirrorWhenNotUseMirrorIsNoOp() {
-        val otherController = BrightnessSlider(rootView, brightnessSliderView, false)
+        val otherController = BrightnessSlider(
+                rootView, brightnessSliderView, false, mFalsingManager)
         otherController.init()
 
         otherController.setMirrorControllerAndMirror(mirrorController)
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 5b74cbd..1f66bfd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -79,7 +79,7 @@
 
     private final ScreenStateObserver mScreenStateObserver;
 
-    private final MagnificationRequestObserver mMagnificationRequestObserver;
+    private final MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
 
     private int mUserId;
 
@@ -284,6 +284,14 @@
             mControllerCtx.getHandler().sendMessage(m);
         }
 
+        @Override
+        public void onImeWindowVisibilityChanged(boolean shown) {
+            final Message m = PooledLambda.obtainMessage(
+                    FullScreenMagnificationController::notifyImeWindowVisibilityChanged,
+                    FullScreenMagnificationController.this, shown);
+            mControllerCtx.getHandler().sendMessage(m);
+        }
+
         /**
          * Update our copy of the current magnification region
          *
@@ -329,7 +337,7 @@
             final boolean lastMagnificationActivated = mMagnificationActivated;
             mMagnificationActivated = spec.scale > 1.0f;
             if (mMagnificationActivated != lastMagnificationActivated) {
-                mMagnificationRequestObserver.onFullScreenMagnificationActivationState(
+                mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
                         mMagnificationActivated);
             }
         }
@@ -498,7 +506,7 @@
             sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
             if (isMagnifying() && (id != INVALID_ID)) {
                 mIdOfLastServiceToMagnify = id;
-                mMagnificationRequestObserver.onRequestMagnificationSpec(mDisplayId,
+                mMagnificationInfoChangedCallback.onRequestMagnificationSpec(mDisplayId,
                         mIdOfLastServiceToMagnify);
             }
             return changed;
@@ -631,12 +639,12 @@
      */
     public FullScreenMagnificationController(@NonNull Context context,
             @NonNull AccessibilityManagerService ams, @NonNull Object lock,
-            @NonNull MagnificationRequestObserver magnificationRequestObserver) {
+            @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
         this(new ControllerContext(context, ams,
                 LocalServices.getService(WindowManagerInternal.class),
                 new Handler(context.getMainLooper()),
                 context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
-                magnificationRequestObserver);
+                magnificationInfoChangedCallback);
     }
 
     /**
@@ -645,12 +653,12 @@
     @VisibleForTesting
     public FullScreenMagnificationController(@NonNull ControllerContext ctx,
             @NonNull Object lock,
-            @NonNull MagnificationRequestObserver magnificationRequestObserver) {
+            @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
         mControllerCtx = ctx;
         mLock = lock;
         mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
         mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
-        mMagnificationRequestObserver = magnificationRequestObserver;
+        mMagnificationInfoChangedCallback = magnificationInfoChangedCallback;
     }
 
     /**
@@ -1168,6 +1176,16 @@
     }
 
     /**
+     * Notifies that the IME window visibility changed.
+     *
+     * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+     *                           hidden.
+     */
+    void notifyImeWindowVisibilityChanged(boolean shown) {
+        mMagnificationInfoChangedCallback.onImeWindowVisibilityChanged(shown);
+    }
+
+    /**
      * Returns {@code true} if the magnifiable regions of the display is forced to be shown.
      *
      * @param displayId The logical display id.
@@ -1528,7 +1546,7 @@
         return animate ? STUB_ANIMATION_CALLBACK : null;
     }
 
-    interface  MagnificationRequestObserver {
+    interface  MagnificationInfoChangedCallback {
 
         /**
          * Called when the {@link MagnificationSpec} is changed with non-default
@@ -1545,7 +1563,13 @@
          *
          * @param activated {@code true} if the magnification is activated, otherwise {@code false}.
          */
-        @GuardedBy("mLock")
         void onFullScreenMagnificationActivationState(boolean activated);
+
+        /**
+         * Called when the IME window visibility changed.
+         * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+         *                           hidden.
+         */
+        void onImeWindowVisibilityChanged(boolean shown);
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 2073c70..878ebc5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -18,6 +18,7 @@
 
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 
 import android.annotation.NonNull;
@@ -33,6 +34,7 @@
 import android.view.accessibility.MagnificationAnimationCallback;
 
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.accessibility.AccessibilityManagerService;
 
@@ -58,7 +60,7 @@
  */
 public class MagnificationController implements WindowMagnificationManager.Callback,
         MagnificationGestureHandler.Callback,
-        FullScreenMagnificationController.MagnificationRequestObserver {
+        FullScreenMagnificationController.MagnificationInfoChangedCallback {
 
     private static final boolean DEBUG = false;
     private static final String TAG = "MagnificationController";
@@ -73,6 +75,10 @@
     private WindowMagnificationManager mWindowMagnificationMgr;
     private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 
+    @GuardedBy("mLock")
+    private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+    @GuardedBy("mLock")
+    private boolean mImeWindowVisible = false;
     private long mWindowModeEnabledTime = 0;
     private long mFullScreenModeEnabledTime = 0;
 
@@ -216,9 +222,18 @@
     public void onWindowMagnificationActivationState(boolean activated) {
         if (activated) {
             mWindowModeEnabledTime = SystemClock.uptimeMillis();
+
+            synchronized (mLock) {
+                mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+            }
+            logMagnificationModeWithImeOnIfNeeded();
         } else {
             logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
                     SystemClock.uptimeMillis() - mWindowModeEnabledTime);
+
+            synchronized (mLock) {
+                mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+            }
         }
     }
 
@@ -226,12 +241,29 @@
     public void onFullScreenMagnificationActivationState(boolean activated) {
         if (activated) {
             mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
+
+            synchronized (mLock) {
+                mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+            }
+            logMagnificationModeWithImeOnIfNeeded();
         } else {
             logMagnificationUsageState(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
                     SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
+
+            synchronized (mLock) {
+                mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+            }
         }
     }
 
+    @Override
+    public void onImeWindowVisibilityChanged(boolean shown) {
+        synchronized (mLock) {
+            mImeWindowVisible = shown;
+        }
+        logMagnificationModeWithImeOnIfNeeded();
+    }
+
     /**
      * Wrapper method of logging the magnification activated mode and its duration of the usage
      * when the magnification is disabled.
@@ -245,6 +277,17 @@
     }
 
     /**
+     * Wrapper method of logging the activated mode of the magnification when the IME window
+     * is shown on the screen.
+     *
+     * @param mode The activated magnification mode.
+     */
+    @VisibleForTesting
+    public void logMagnificationModeWithIme(int mode) {
+        AccessibilityStatsLogUtils.logMagnificationModeWithImeOn(mode);
+    }
+
+    /**
      * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
      * WindowMagnificationManager}.
      *
@@ -295,6 +338,18 @@
         }
     }
 
+    private void logMagnificationModeWithImeOnIfNeeded() {
+        final int mode;
+
+        synchronized (mLock) {
+            if (!mImeWindowVisible || mActivatedMode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
+                return;
+            }
+            mode = mActivatedMode;
+        }
+        logMagnificationModeWithIme(mode);
+    }
+
     /**
      * Getter of {@link FullScreenMagnificationController}.
      *
diff --git a/services/api/current.txt b/services/api/current.txt
index a3e6715..a0b1e33 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -105,6 +105,14 @@
 
 }
 
+package com.android.server.stats {
+
+  public final class StatsHelper {
+    method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
+  }
+
+}
+
 package com.android.server.wifi {
 
   public class SupplicantManager {
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
index f01c182..475dcf5 100644
--- a/services/api/non-updatable-current.txt
+++ b/services/api/non-updatable-current.txt
@@ -52,6 +52,14 @@
 
 }
 
+package com.android.server.stats {
+
+  public final class StatsHelper {
+    method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
+  }
+
+}
+
 package com.android.server.wifi {
 
   public class SupplicantManager {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 641b38d..1e3f12a 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,7 +158,7 @@
         "android.hardware.power.stats-V1-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
-        "dnsresolver_aidl_interface-V7-java",
+        "dnsresolver_aidl_interface-V8-java",
         "icu4j_calendar_astronomer",
         "netd-client",
         "overlayable_policy_aidl-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bbe59c9..ca01c8e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1570,16 +1570,16 @@
         mNetworkInfoBlockingLogs.log(action + " " + uid);
     }
 
-    private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
-            boolean blocked) {
+    private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net, int blocked) {
         if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
             return;
         }
-        final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+        final String action = (blocked != 0) ? "BLOCKED" : "UNBLOCKED";
         final int requestId = nri.getActiveRequest() != null
                 ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
         mNetworkInfoBlockingLogs.log(String.format(
-                "%s %d(%d) on netId %d", action, nri.mAsUid, requestId, net.getNetId()));
+                "%s %d(%d) on netId %d: %s", action, nri.mAsUid, requestId, net.getNetId(),
+                blockedReasonsToString(blocked)));
     }
 
     /**
@@ -3137,6 +3137,13 @@
                     }
                     break;
                 }
+                case NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED: {
+                    if (msg.arg1 >= 0 && msg.arg1 <= NetworkAgent.MAX_TEARDOWN_DELAY_MS) {
+                        nai.teardownDelayMs = msg.arg1;
+                    } else {
+                        logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1);
+                    }
+                }
             }
         }
 
@@ -3708,6 +3715,23 @@
         mLegacyTypeTracker.remove(nai, wasDefault);
         rematchAllNetworksAndRequests();
         mLingerMonitor.noteDisconnect(nai);
+
+        // Immediate teardown.
+        if (nai.teardownDelayMs == 0) {
+            destroyNetwork(nai);
+            return;
+        }
+
+        // Delayed teardown.
+        try {
+            mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error marking network restricted during teardown: " + e);
+        }
+        mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs);
+    }
+
+    private void destroyNetwork(NetworkAgentInfo nai) {
         if (nai.created) {
             // Tell netd to clean up the configuration for this network
             // (routing rules, DNS, etc).
@@ -3720,7 +3744,7 @@
             mDnsManager.removeNetwork(nai.network);
         }
         mNetIdManager.releaseNetId(nai.network.getNetId());
-        nai.onNetworkDisconnected();
+        nai.onNetworkDestroyed();
     }
 
     private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -7354,7 +7378,7 @@
                 break;
             }
             case ConnectivityManager.CALLBACK_BLK_CHANGED: {
-                maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1 != 0);
+                maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1);
                 msg.arg1 = arg1;
                 break;
             }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 88dca0c..282a12d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -80,9 +80,12 @@
     private final @NonNull AudioService mAudioService;
     private final @NonNull Context mContext;
 
-    /** Forced device usage for communications sent to AudioSystem */
-    private AudioDeviceAttributes mPreferredCommunicationDevice;
+    /** ID for Communication strategy retrieved form audio policy manager */
     private int mCommunicationStrategyId = -1;
+    /** Active communication device reported by audio policy manager */
+    private AudioDeviceInfo mActiveCommunicationDevice;
+    /** Last preferred device set for communication strategy */
+    private AudioDeviceAttributes mPreferredCommunicationDevice;
 
     // Manages all connected devices, only ever accessed on the message loop
     private final AudioDeviceInventory mDeviceInventory;
@@ -153,8 +156,9 @@
     private void init() {
         setupMessaging(mContext);
 
-        mPreferredCommunicationDevice = null;
         initCommunicationStrategyId();
+        mPreferredCommunicationDevice = null;
+        updateActiveCommunicationDevice();
 
         mSystemServer.registerUserStartedReceiver(mContext);
     }
@@ -300,7 +304,6 @@
                                         + " from API: " + eventSource)).printLog(TAG));
 
         final boolean wasBtScoRequested = isBluetoothScoRequested();
-        final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
         CommunicationRouteClient client;
 
 
@@ -341,16 +344,6 @@
             mBtHelper.stopBluetoothSco(eventSource);
         }
 
-        if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
-            try {
-                mContext.sendBroadcastAsUser(
-                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
-                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
-            } catch (Exception e) {
-                Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
-            }
-        }
-
         sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
     }
 
@@ -386,66 +379,119 @@
      * Returns the device currently requested for communication use case.
      * @return AudioDeviceInfo the requested device for communication.
      */
-    AudioDeviceInfo getCommunicationDevice() {
+    /* package */ AudioDeviceInfo getCommunicationDevice() {
         synchronized (mDeviceStateLock) {
-            AudioDeviceAttributes device = requestedCommunicationDevice();
-            if (device == null) {
-                AudioAttributes attr =
-                        AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
-                                AudioSystem.STREAM_VOICE_CALL);
-                List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
-                if (devices.isEmpty()) {
-                    if (mAudioService.isPlatformVoice()) {
-                        Log.w(TAG, "getCommunicationDevice(): no device for phone strategy");
-                    }
-                    return null;
-                }
-                device = devices.get(0);
-            }
-            return AudioManager.getDeviceInfoFromTypeAndAddress(
-                    device.getType(), device.getAddress());
+            updateActiveCommunicationDevice();
+            return mActiveCommunicationDevice;
         }
     }
 
     /**
-     * Helper method on top of requestedCommunicationDevice() indicating if
+     * Updates currently active communication device (mActiveCommunicationDevice).
+     */
+    @GuardedBy("mDeviceStateLock")
+    void updateActiveCommunicationDevice() {
+        AudioDeviceAttributes device = preferredCommunicationDevice();
+        if (device == null) {
+            AudioAttributes attr =
+                    AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
+                            AudioSystem.STREAM_VOICE_CALL);
+            List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
+            if (devices.isEmpty()) {
+                if (mAudioService.isPlatformVoice()) {
+                    Log.w(TAG,
+                            "updateActiveCommunicationDevice(): no device for phone strategy");
+                }
+                mActiveCommunicationDevice = null;
+                return;
+            }
+            device = devices.get(0);
+        }
+        mActiveCommunicationDevice = AudioManager.getDeviceInfoFromTypeAndAddress(
+                device.getType(), device.getAddress());
+    }
+
+    /**
+     * Indicates if the device which type is passed as argument is currently resquested to be used
+     * for communication.
+     * @param deviceType the device type the query applies to.
+     * @return true if this device type is requested for communication.
+     */
+    private boolean isDeviceRequestedForCommunication(int deviceType) {
+        synchronized (mDeviceStateLock) {
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null && device.getType() == deviceType;
+        }
+    }
+
+    /**
+     * Indicates if the device which type is passed as argument is currently either resquested
+     * to be used for communication or selected for an other reason (e.g bluetooth SCO audio
+     * is active for SCO device).
+     * @param deviceType the device type the query applies to.
+     * @return true if this device type is requested for communication.
+     */
+    private boolean isDeviceOnForCommunication(int deviceType) {
+        synchronized (mDeviceStateLock) {
+            AudioDeviceAttributes device = preferredCommunicationDevice();
+            return device != null && device.getType() == deviceType;
+        }
+    }
+
+    /**
+     * Indicates if the device which type is passed as argument is active for communication.
+     * Active means not only currently used by audio policy manager for communication strategy
+     * but also explicitly requested for use by communication strategy.
+     * @param deviceType the device type the query applies to.
+     * @return true if this device type is requested for communication.
+     */
+    private boolean isDeviceActiveForCommunication(int deviceType) {
+        return mActiveCommunicationDevice != null
+                && mActiveCommunicationDevice.getType() == deviceType
+                && mPreferredCommunicationDevice != null
+                && mPreferredCommunicationDevice.getType() == deviceType;
+    }
+
+    /**
+     * Helper method on top of isDeviceRequestedForCommunication() indicating if
      * speakerphone ON is currently requested or not.
      * @return true if speakerphone ON requested, false otherwise.
      */
-
     private boolean isSpeakerphoneRequested() {
-        synchronized (mDeviceStateLock) {
-            AudioDeviceAttributes device = requestedCommunicationDevice();
-            return device != null
-                    && device.getType()
-                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
-        }
+        return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
     }
 
     /**
-     * Indicates if active route selection for communication is speakerphone.
+     * Indicates if preferred route selection for communication is speakerphone.
      * @return true if speakerphone is active, false otherwise.
      */
     /*package*/ boolean isSpeakerphoneOn() {
-        AudioDeviceAttributes device = getPreferredCommunicationDevice();
-        if (device == null) {
-            return false;
-        }
-        return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER;
+        return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
+    }
+
+    private boolean isSpeakerphoneActive() {
+        return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
     }
 
     /**
-     * Helper method on top of requestedCommunicationDevice() indicating if
+     * Helper method on top of isDeviceRequestedForCommunication() indicating if
      * Bluetooth SCO ON is currently requested or not.
      * @return true if Bluetooth SCO ON is requested, false otherwise.
      */
     /*package*/ boolean isBluetoothScoRequested() {
-        synchronized (mDeviceStateLock) {
-            AudioDeviceAttributes device = requestedCommunicationDevice();
-            return device != null
-                    && device.getType()
-                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
-        }
+        return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
+    }
+
+    /**
+     * Indicates if preferred route selection for communication is Bluetooth SCO.
+     * @return true if Bluetooth SCO is preferred , false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoOn() {
+        return isDeviceOnForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
+    }
+
+    /*package*/ boolean isBluetoothScoActive() {
+        return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO);
     }
 
     /*package*/ void setWiredDeviceConnectionState(int type,
@@ -593,18 +639,6 @@
         }
     }
 
-    /**
-     * Indicates if active route selection for communication is Bluetooth SCO.
-     * @return true if Bluetooth SCO is active , false otherwise.
-     */
-    /*package*/ boolean isBluetoothScoOn() {
-        AudioDeviceAttributes device = getPreferredCommunicationDevice();
-        if (device == null) {
-            return false;
-        }
-        return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType());
-    }
-
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
         synchronized (mDeviceStateLock) {
             return mDeviceInventory.startWatchingRoutes(observer);
@@ -748,8 +782,8 @@
 
     @GuardedBy("mDeviceStateLock")
     private void dispatchCommunicationDevice() {
-        AudioDeviceInfo device = getCommunicationDevice();
-        int portId = (device == null) ? 0 : device.getId();
+        int portId = (mActiveCommunicationDevice == null) ? 0
+                : mActiveCommunicationDevice.getId();
         if (portId == mCurCommunicationPortId) {
             return;
         }
@@ -1022,12 +1056,13 @@
             pw.println("  " + prefix + "pid: " + cl.getPid() + " device: "
                         + cl.getDevice() + " cb: " + cl.getBinder()); });
 
-        pw.println("\n" + prefix + "mPreferredCommunicationDevice: "
+        pw.println("\n" + prefix + "Computed Preferred communication device: "
+                +  preferredCommunicationDevice());
+        pw.println("\n" + prefix + "Applied Preferred communication device: "
                 +  mPreferredCommunicationDevice);
-
-        AudioDeviceInfo device = getCommunicationDevice();
-        pw.println(prefix + "Selected Communication Device: "
-                +  ((device == null) ? "None" : new AudioDeviceAttributes(device)));
+        pw.println(prefix + "Active communication device: "
+                +  ((mActiveCommunicationDevice == null) ? "None"
+                        : new AudioDeviceAttributes(mActiveCommunicationDevice)));
 
         pw.println(prefix + "mCommunicationStrategyId: "
                 +  mCommunicationStrategyId);
@@ -1128,6 +1163,7 @@
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
                             initCommunicationStrategyId();
+                            updateActiveCommunicationDevice();
                             mDeviceInventory.onRestoreDevices();
                             mBtHelper.onAudioServerDiedRestoreA2dp();
                             onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
@@ -1340,11 +1376,16 @@
                     final List<AudioDeviceAttributes> devices =
                             (List<AudioDeviceAttributes>) msg.obj;
                     setPreferredDevicesForStrategySync(strategy, devices);
-
+                    if (strategy == mCommunicationStrategyId) {
+                        onUpdatePhoneStrategyDevice(devices.isEmpty() ? null : devices.get(0));
+                    }
                 } break;
                 case MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
                     final int strategy = msg.arg1;
                     removePreferredDevicesForStrategySync(strategy);
+                    if (strategy == mCommunicationStrategyId) {
+                        onUpdatePhoneStrategyDevice(null);
+                    }
                 } break;
                 case MSG_CHECK_MUTE_MUSIC:
                     checkMessagesMuteMusic(0);
@@ -1672,14 +1713,14 @@
     }
 
     /**
-     * Determines which forced usage for communication should be sent to audio policy manager
+     * Determines which preferred device for phone strategy should be sent to audio policy manager
      * as a function of current SCO audio activation state and active communication route requests.
      * SCO audio state has the highest priority as it can result from external activation by
      * telephony service.
      * @return selected forced usage for communication.
      */
     @GuardedBy("mDeviceStateLock")
-    @Nullable private AudioDeviceAttributes getPreferredCommunicationDevice() {
+    @Nullable private AudioDeviceAttributes preferredCommunicationDevice() {
         boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
         if (btSCoOn) {
             // Use the SCO device known to BtHelper so that it matches exactly
@@ -1692,8 +1733,7 @@
             }
         }
         AudioDeviceAttributes device = requestedCommunicationDevice();
-        if (device == null
-                || AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType())) {
+        if (device == null || device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
             // Do not indicate BT SCO selection if SCO is requested but SCO is not ON
             return null;
         }
@@ -1707,30 +1747,49 @@
     // @GuardedBy("mSetModeLock")
     @GuardedBy("mDeviceStateLock")
     private void onUpdateCommunicationRoute(String eventSource) {
-        mPreferredCommunicationDevice = getPreferredCommunicationDevice();
+        AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice();
         if (AudioService.DEBUG_COMM_RTE) {
-            Log.v(TAG, "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
-                    + mPreferredCommunicationDevice + " eventSource: " + eventSource);
+            Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+                    + preferredCommunicationDevice + " eventSource: " + eventSource);
         }
         AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
-                "onUpdateCommunicationRoute, mPreferredCommunicationDevice: "
-                + mPreferredCommunicationDevice + " eventSource: " + eventSource)));
+                "onUpdateCommunicationRoute, preferredCommunicationDevice: "
+                + preferredCommunicationDevice + " eventSource: " + eventSource)));
 
-        if (mPreferredCommunicationDevice == null
-                || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
-                        mPreferredCommunicationDevice.getInternalType())) {
+        if (preferredCommunicationDevice == null
+                || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
             AudioSystem.setParameters("BT_SCO=off");
         } else {
             AudioSystem.setParameters("BT_SCO=on");
         }
-        if (mPreferredCommunicationDevice == null) {
+        if (preferredCommunicationDevice == null) {
             postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
         } else {
             postSetPreferredDevicesForStrategy(
-                    mCommunicationStrategyId, Arrays.asList(mPreferredCommunicationDevice));
+                    mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
         }
-        mAudioService.postUpdateRingerModeServiceInt();
-        dispatchCommunicationDevice();
+    }
+
+    private void onUpdatePhoneStrategyDevice(AudioDeviceAttributes device) {
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                boolean wasSpeakerphoneActive = isSpeakerphoneActive();
+                mPreferredCommunicationDevice = device;
+                updateActiveCommunicationDevice();
+                if (wasSpeakerphoneActive != isSpeakerphoneActive()) {
+                    try {
+                        mContext.sendBroadcastAsUser(
+                                new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+                                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                                                  UserHandle.ALL);
+                    } catch (Exception e) {
+                        Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
+                    }
+                }
+                mAudioService.postUpdateRingerModeServiceInt();
+                dispatchCommunicationDevice();
+            }
+        }
     }
 
     private CommunicationRouteClient removeCommunicationRouteClient(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 148c723..16a9626 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4192,7 +4192,7 @@
         final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                 || ringerMode == AudioManager.RINGER_MODE_SILENT;
         final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
-                && mDeviceBroker.isBluetoothScoOn();
+                && mDeviceBroker.isBluetoothScoActive();
         // Ask audio policy engine to force use Bluetooth SCO channel if needed
         final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                 + "/" + Binder.getCallingPid();
@@ -5600,7 +5600,7 @@
         switch (mPlatformType) {
         case AudioSystem.PLATFORM_VOICE:
             if (isInCommunication()) {
-                if (mDeviceBroker.isBluetoothScoOn()) {
+                if (mDeviceBroker.isBluetoothScoActive()) {
                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
@@ -5636,7 +5636,7 @@
             }
         default:
             if (isInCommunication()) {
-                if (mDeviceBroker.isBluetoothScoOn()) {
+                if (mDeviceBroker.isBluetoothScoActive()) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 63e7b4b..70f26ac 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -766,15 +766,18 @@
         public long[] getAuthenticatorIds(int callingUserId) {
             checkInternalPermission();
 
-            final List<Long> ids = new ArrayList<>();
+            final List<Long> authenticatorIds = new ArrayList<>();
             for (BiometricSensor sensor : mSensors) {
                 try {
-                    final long id = sensor.impl.getAuthenticatorId(callingUserId);
-                    if (Utils.isAtLeastStrength(sensor.getCurrentStrength(),
-                            Authenticators.BIOMETRIC_STRONG) && id != 0) {
-                        ids.add(id);
+                    final boolean hasEnrollments = sensor.impl.hasEnrolledTemplates(callingUserId,
+                            getContext().getOpPackageName());
+                    final long authenticatorId = sensor.impl.getAuthenticatorId(callingUserId);
+                    if (hasEnrollments && Utils.isAtLeastStrength(sensor.getCurrentStrength(),
+                            Authenticators.BIOMETRIC_STRONG)) {
+                        authenticatorIds.add(authenticatorId);
                     } else {
-                        Slog.d(TAG, "Sensor " + sensor + ", sensorId " + id
+                        Slog.d(TAG, "Sensor " + sensor + ", sensorId " + sensor.id
+                                + ", hasEnrollments: " + hasEnrollments
                                 + " cannot participate in Keystore operations");
                     }
                 } catch (RemoteException e) {
@@ -782,9 +785,9 @@
                 }
             }
 
-            long[] result = new long[ids.size()];
-            for (int i = 0; i < ids.size(); i++) {
-                result[i] = ids.get(i);
+            long[] result = new long[authenticatorIds.size()];
+            for (int i = 0; i < authenticatorIds.size(); i++) {
+                result[i] = authenticatorIds.get(i);
             }
             return result;
         }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b31fa70..d16a2de 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -201,6 +201,9 @@
     // Set to true when partial connectivity was detected.
     public boolean partialConnectivity;
 
+    // Delay between when the network is disconnected and when the native network is destroyed.
+    public int teardownDelayMs;
+
     // Captive portal info of the network from RFC8908, if any.
     // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
     public CaptivePortalData capportApiData;
@@ -589,13 +592,13 @@
     }
 
     /**
-     * Notify the NetworkAgent that the network is disconnected and destroyed.
+     * Notify the NetworkAgent that the native network has been destroyed.
      */
-    public void onNetworkDisconnected() {
+    public void onNetworkDestroyed() {
         try {
-            networkAgent.onNetworkDisconnected();
+            networkAgent.onNetworkDestroyed();
         } catch (RemoteException e) {
-            Log.e(TAG, "Error sending network disconnected event", e);
+            Log.e(TAG, "Error sending network destroyed event", e);
         }
     }
 
@@ -675,6 +678,12 @@
                 @QosCallbackException.ExceptionType final int exceptionType) {
             mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType);
         }
+
+        @Override
+        public void sendTeardownDelayMs(int teardownDelayMs) {
+            mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
+                    teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a070f27..820198c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -464,7 +464,7 @@
                 .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
-                .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+                .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
                 .build();
 
         loadAlwaysOnPackage();
@@ -528,7 +528,7 @@
     private void resetNetworkCapabilities() {
         mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
                 .setUids(null)
-                .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+                .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
                 .build();
     }
 
@@ -1259,7 +1259,7 @@
         capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId,
                 mConfig.allowedApplications, mConfig.disallowedApplications));
 
-        capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+        capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType(), mConfig.session));
 
         // Only apps targeting Q and above can explicitly declare themselves as metered.
         // These VPNs are assumed metered unless they state otherwise.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9d02576..393a4eb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2932,9 +2932,6 @@
 
         @Override // Binder call
         public int getRefreshRateSwitchingType() {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
-                    "Permission required read refresh rate switching type.");
             final long token = Binder.clearCallingIdentity();
             try {
                 return getRefreshRateSwitchingTypeInternal();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 56ad01b..cb8541e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -874,6 +874,10 @@
     private void cleanupHandlerThreadAfterStop() {
         setProximitySensorEnabled(false);
         mHandler.removeCallbacksAndMessages(null);
+        if (mUnfinishedBusiness) {
+            mCallbacks.releaseSuspendBlocker();
+            mUnfinishedBusiness = false;
+        }
         if (mPowerState != null) {
             mPowerState.stop();
             mPowerState = null;
@@ -1701,12 +1705,7 @@
         }
     }
 
-    private final Runnable mCleanListener = new Runnable() {
-        @Override
-        public void run() {
-            sendUpdatePowerState();
-        }
-    };
+    private final Runnable mCleanListener = this::sendUpdatePowerState;
 
     private void setProximitySensorEnabled(boolean enable) {
         if (enable) {
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 6201b94..5abc438 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -52,7 +52,8 @@
 
     // Entries added by LockSettingsService once a user's synthetic password is known. At this point
     // things are still keyed by userId.
-    @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockouts;
+    @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFingerprint;
+    @NonNull private final ArrayList<UserAuthInfo> mPendingResetLockoutsForFace;
 
     /**
      * Authentication info for a successful user unlock via Synthetic Password. This can be used to
@@ -151,7 +152,8 @@
         mContext = context;
         mSpManager = spManager;
         mHandler = handler;
-        mPendingResetLockouts = new ArrayList<>();
+        mPendingResetLockoutsForFingerprint = new ArrayList<>();
+        mPendingResetLockoutsForFace = new ArrayList<>();
     }
 
     public void systemReady(@Nullable FingerprintManager fingerprintManager,
@@ -173,17 +175,34 @@
      */
     void addPendingLockoutResetForUser(int userId, @NonNull byte[] gatekeeperPassword) {
         mHandler.post(() -> {
-            Slog.d(TAG, "addPendingLockoutResetForUser: " + userId);
-            mPendingResetLockouts.add(new UserAuthInfo(userId, gatekeeperPassword));
+            if (mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId)) {
+                Slog.d(TAG, "Face addPendingLockoutResetForUser: " + userId);
+                mPendingResetLockoutsForFace.add(new UserAuthInfo(userId, gatekeeperPassword));
+            }
+
+            if (mFingerprintManager != null
+                    && mFingerprintManager.hasEnrolledFingerprints(userId)) {
+                Slog.d(TAG, "Fingerprint addPendingLockoutResetForUser: " + userId);
+                mPendingResetLockoutsForFingerprint.add(new UserAuthInfo(userId,
+                        gatekeeperPassword));
+            }
         });
     }
 
     void processPendingLockoutResets() {
         mHandler.post(() -> {
-            Slog.d(TAG, "processPendingLockoutResets: " + mPendingResetLockouts.size());
-            processPendingLockoutsForFingerprint(new ArrayList<>(mPendingResetLockouts));
-            processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockouts));
-            mPendingResetLockouts.clear();
+            if (!mPendingResetLockoutsForFace.isEmpty()) {
+                Slog.d(TAG, "Processing pending resetLockout for face");
+                processPendingLockoutsForFace(new ArrayList<>(mPendingResetLockoutsForFace));
+                mPendingResetLockoutsForFace.clear();
+            }
+
+            if (!mPendingResetLockoutsForFingerprint.isEmpty()) {
+                Slog.d(TAG, "Processing pending resetLockout for fingerprint");
+                processPendingLockoutsForFingerprint(
+                        new ArrayList<>(mPendingResetLockoutsForFingerprint));
+                mPendingResetLockoutsForFingerprint.clear();
+            }
         });
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionConfig.java b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
similarity index 98%
rename from services/core/java/com/android/server/media/MediaSessionConfig.java
rename to services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
index a866b0d..9bb8e2e 100644
--- a/services/core/java/com/android/server/media/MediaSessionConfig.java
+++ b/services/core/java/com/android/server/media/MediaSessionDeviceConfig.java
@@ -23,7 +23,7 @@
 import java.io.PrintWriter;
 import java.util.Set;
 
-class MediaSessionConfig {
+class MediaSessionDeviceConfig {
     /**
      * Denotes the duration for which a media button receiver will be exempted from
      * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index aefdbca..50cfe1f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -238,7 +238,7 @@
                         mSession2TokenCallback);
                 break;
             case PHASE_ACTIVITY_MANAGER_READY:
-                MediaSessionConfig.initialize(mContext);
+                MediaSessionDeviceConfig.initialize(mContext);
                 break;
         }
     }
@@ -560,7 +560,7 @@
                         PowerExemptionManager.class);
                 powerExemptionManager.addToTemporaryAllowList(targetPackage,
                         PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason,
-                        MediaSessionConfig.getMediaSessionCallbackFgsAllowlistDurationMs());
+                        MediaSessionDeviceConfig.getMediaSessionCallbackFgsAllowlistDurationMs());
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1975,7 +1975,7 @@
                 }
                 mAudioPlayerStateMonitor.dump(mContext, pw, "");
             }
-            MediaSessionConfig.dump(pw, "");
+            MediaSessionDeviceConfig.dump(pw, "");
         }
 
         /**
@@ -2260,7 +2260,7 @@
                         mContext, keyEvent, callingPackageName,
                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver,
                         mHandler,
-                        MediaSessionConfig.getMediaButtonReceiverFgsAllowlistDurationMs());
+                        MediaSessionDeviceConfig.getMediaButtonReceiverFgsAllowlistDurationMs());
                 if (sent) {
                     String pkgName = mediaButtonReceiverHolder.getPackageName();
                     for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index ce49d88..5f0aa03 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -333,10 +333,7 @@
 
         if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) {
             if (isNewApp) {
-                final ShortcutPackage sp = mPackages.remove(packageName);
-                if (sp != null) {
-                    sp.removeShortcuts();
-                }
+                mPackages.remove(packageName);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e16c67f..9bc9d9f 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -278,7 +278,7 @@
             try {
                 return mContext.getPackageManager().getPermissionInfo(permissionName, 0);
             } catch (NameNotFoundException e) {
-                Slog.e(TAG, "Permission not found: " + permissionName);
+                Slog.w(TAG, "Permission not found: " + permissionName);
                 return null;
             }
         }
@@ -737,10 +737,9 @@
                 grantPermissionsToSystemPackage(pm, packageName, userId,
                         CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
-                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
+                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
                 grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
-                        ALWAYS_LOCATION_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS,
-                        ACTIVITY_RECOGNITION_PERMISSIONS);
+                        ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
         }
         if (locationExtraPackageNames != null) {
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 7f55723..268de3e 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -18,11 +18,13 @@
 import static android.view.KeyEvent.KEYCODE_POWER;
 
 import android.os.SystemClock;
+import android.util.Log;
 import android.util.SparseLongArray;
 import android.view.KeyEvent;
 
 import com.android.internal.util.ToBooleanFunction;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.function.Consumer;
 
@@ -89,8 +91,8 @@
 
         @Override
         public String toString() {
-            return "KeyCode1 = " + KeyEvent.keyCodeToString(mKeyCode1)
-                    + ", KeyCode2 = " +  KeyEvent.keyCodeToString(mKeyCode2);
+            return KeyEvent.keyCodeToString(mKeyCode1) + " + "
+                    + KeyEvent.keyCodeToString(mKeyCode2);
         }
     }
 
@@ -151,6 +153,7 @@
                     if (!rule.shouldInterceptKeys(mDownTimes)) {
                         return false;
                     }
+                    Log.v(TAG, "Performing combination rule : " + rule);
                     rule.execute();
                     mTriggeredRule = rule;
                     return true;
@@ -230,4 +233,11 @@
         }
         return false;
     }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "KeyCombination rules:");
+        forAllRules(mRules, (rule)-> {
+            pw.println(prefix + "  " + rule);
+        });
+    }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f185464..f931df8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -434,7 +434,6 @@
     // to hold wakelocks during dispatch and eliminating the critical path.
     volatile boolean mPowerKeyHandled;
     volatile boolean mBackKeyHandled;
-    volatile boolean mBeganFromNonInteractive;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
 
@@ -876,16 +875,6 @@
         if (!mPowerKeyHandled) {
             if (!interactive) {
                 wakeUpFromPowerKey(event.getDownTime());
-                if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
-                    mBeganFromNonInteractive = true;
-                } else {
-                    final int maxCount = getMaxMultiPressPowerCount();
-                    if (maxCount <= 1) {
-                        mPowerKeyHandled = true;
-                    } else {
-                        mBeganFromNonInteractive = true;
-                    }
-                }
             }
         } else {
             // handled by another power key policy.
@@ -895,7 +884,7 @@
         }
     }
 
-    private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
+    private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
         final boolean handled = canceled || mPowerKeyHandled;
 
         if (!handled) {
@@ -906,33 +895,36 @@
         } else {
             // handled by single key or another power key policy.
             mSingleKeyGestureDetector.reset();
-            finishPowerKeyPress();
         }
+
+        finishPowerKeyPress();
     }
 
     private void finishPowerKeyPress() {
-        mBeganFromNonInteractive = false;
         mPowerKeyHandled = false;
         if (mPowerKeyWakeLock.isHeld()) {
             mPowerKeyWakeLock.release();
         }
     }
 
-    private void powerPress(long eventTime, boolean interactive, int count) {
+    private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
         if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
             Slog.i(TAG, "Suppressed redundant power key press while "
                     + "already in the process of turning the screen on.");
             return;
         }
+
+        final boolean interactive = Display.isOnState(mDefaultDisplay.getState());
+
         Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
-                + " count=" + count + " beganFromNonInteractive=" + mBeganFromNonInteractive +
-                " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
+                + " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
+                + " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
 
         if (count == 2) {
             powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
         } else if (count == 3) {
             powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
-        } else if (interactive && !mBeganFromNonInteractive) {
+        } else if (interactive && !beganFromNonInteractive) {
             switch (mShortPressOnPowerBehavior) {
                 case SHORT_PRESS_POWER_NOTHING:
                     break;
@@ -1906,12 +1898,19 @@
 
         @Override
         void onPress(long downTime) {
-            powerPress(downTime, true, 1 /*count*/);
+            powerPress(downTime, 1 /*count*/,
+                    mSingleKeyGestureDetector.beganFromNonInteractive());
             finishPowerKeyPress();
         }
 
         @Override
         void onLongPress(long eventTime) {
+            if (mSingleKeyGestureDetector.beganFromNonInteractive()
+                    && !mSupportLongPressPowerWhenNonInteractive) {
+                Slog.v(TAG, "Not support long press power when device is not interactive.");
+                return;
+            }
+
             powerLongPress(eventTime);
         }
 
@@ -1923,7 +1922,7 @@
 
         @Override
         void onMultiPress(long downTime, int count) {
-            powerPress(downTime, true, count);
+            powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
             finishPowerKeyPress();
         }
     }
@@ -3562,7 +3561,7 @@
                 if (down) {
                     interceptPowerKeyDown(event, interactiveAndOn);
                 } else {
-                    interceptPowerKeyUp(event, interactiveAndOn, canceled);
+                    interceptPowerKeyUp(event, canceled);
                 }
                 break;
             }
@@ -3752,7 +3751,7 @@
             }
         }
 
-        mSingleKeyGestureDetector.interceptKey(event);
+        mSingleKeyGestureDetector.interceptKey(event, interactive);
     }
 
     // The camera gesture will be detected by GestureLauncherService.
@@ -5230,6 +5229,8 @@
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
 
         mGlobalKeyManager.dump(prefix, pw);
+        mKeyCombinationManager.dump(prefix, pw);
+        mSingleKeyGestureDetector.dump(prefix, pw);
 
         if (mWakeGestureListener != null) {
             mWakeGestureListener.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index cae2093..3f4d920 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -25,6 +25,7 @@
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -37,7 +38,7 @@
 
 public final class SingleKeyGestureDetector {
     private static final String TAG = "SingleKeyGesture";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = PhoneWindowManager.DEBUG_INPUT;
 
     private static final int MSG_KEY_LONG_PRESS = 0;
     private static final int MSG_KEY_VERY_LONG_PRESS = 1;
@@ -47,6 +48,7 @@
     private final long mVeryLongPressTimeout;
 
     private volatile int mKeyPressCounter;
+    private boolean mBeganFromNonInteractive = false;
 
     private final ArrayList<SingleKeyRule> mRules = new ArrayList();
     private SingleKeyRule mActiveRule = null;
@@ -57,7 +59,6 @@
     private final Handler mHandler;
     private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
 
-
     /** Supported gesture flags */
     public static final int KEY_LONGPRESS = 1 << 1;
     public static final int KEY_VERYLONGPRESS = 1 << 2;
@@ -143,10 +144,10 @@
 
         @Override
         public String toString() {
-            return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode)
-                    + ", long press : " + supportLongPress()
-                    + ", very Long press : " + supportVeryLongPress()
-                    + ", max multi press count : " + getMaxMultiPressCount();
+            return "KeyCode=" + KeyEvent.keyCodeToString(mKeyCode)
+                    + ", LongPress=" + supportLongPress()
+                    + ", VeryLongPress=" + supportVeryLongPress()
+                    + ", MaxMultiPressCount=" + getMaxMultiPressCount();
         }
     }
 
@@ -161,8 +162,12 @@
         mRules.add(rule);
     }
 
-    void interceptKey(KeyEvent event) {
+    void interceptKey(KeyEvent event, boolean interactive) {
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            // Store the non interactive state when first down.
+            if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
+                mBeganFromNonInteractive = !interactive;
+            }
             interceptKeyDown(event);
         } else {
             interceptKeyUp(event);
@@ -268,6 +273,7 @@
                     Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
                 }
                 mActiveRule.onPress(downTime);
+                reset();
                 return true;
             }
 
@@ -316,6 +322,17 @@
         return false;
     }
 
+    boolean beganFromNonInteractive() {
+        return mBeganFromNonInteractive;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "SingleKey rules:");
+        for (SingleKeyRule rule : mRules) {
+            pw.println(prefix + "  " + rule);
+        }
+    }
+
     private class KeyHandler extends Handler {
         KeyHandler() {
             super(Looper.getMainLooper());
@@ -354,7 +371,7 @@
                     } else {
                         mActiveRule.onMultiPress(eventTime, mKeyPressCounter);
                     }
-                    mKeyPressCounter = 0;
+                    reset();
                     break;
             }
         }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index a4459d0..4adcfb6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -615,7 +615,7 @@
                 if (session == null || session.isStagedSessionFailed()) {
                     iter.remove();
                     deleteRollback(rollback,
-                            "Session " + session.getSessionId() + " not existed or failed");
+                            "Session " + rollback.getStagedSessionId() + " not existed or failed");
                     continue;
                 }
 
diff --git a/services/core/java/com/android/server/stats/StatsHelper.java b/services/core/java/com/android/server/stats/StatsHelper.java
new file mode 100644
index 0000000..9b9f6b50
--- /dev/null
+++ b/services/core/java/com/android/server/stats/StatsHelper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.stats;
+
+import static android.app.StatsManager.ACTION_STATSD_STARTED;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+/**
+ * Provides helper methods for the Statsd APEX
+ *
+ * @hide
+ **/
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public final class StatsHelper {
+    private StatsHelper() {}
+
+    /**
+     * Send statsd ready broadcast
+     *
+     **/
+    public static void sendStatsdReadyBroadcast(@NonNull final Context context) {
+        context.sendBroadcastAsUser(
+                new Intent(ACTION_STATSD_STARTED).addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
+                UserHandle.SYSTEM, android.Manifest.permission.DUMP);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b947c88..16692e1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -514,6 +514,18 @@
         }
     }
 
+    void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
+        if (mAccessibilityTracing.isEnabled()) {
+            mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
+                    "windowState=" + windowState + "; shown=" + shown);
+        }
+        final int displayId = windowState.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.onImeSurfaceShownChanged(shown);
+        }
+    }
+
     private static void populateTransformationMatrix(WindowState windowState,
             Matrix outMatrix) {
         windowState.getTransformationMatrix(sTempFloats, outMatrix);
@@ -766,6 +778,15 @@
             }
         }
 
+        void onImeSurfaceShownChanged(boolean shown) {
+            if (mAccessibilityTracing.isEnabled()) {
+                mAccessibilityTracing.logState(
+                        LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+            }
+            mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
+                    shown ? 1 : 0, 0).sendToTarget();
+        }
+
         MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
             if (mAccessibilityTracing.isEnabled()) {
                 mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
@@ -1337,6 +1358,7 @@
             public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
             public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
             public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+            public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
 
             MyHandler(Looper looper) {
                 super(looper);
@@ -1380,6 +1402,11 @@
                             }
                         }
                     } break;
+
+                    case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: {
+                        final boolean shown = message.arg1 == 1;
+                        mCallbacks.onImeWindowVisibilityChanged(shown);
+                    } break;
                 }
             }
         }
@@ -2123,5 +2150,4 @@
             }
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 1262dee..5a8af45 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -17,7 +17,9 @@
 package com.android.server.wm;
 
 import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
+import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
+import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
 import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
 
@@ -34,6 +36,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -49,15 +54,22 @@
 import android.service.displayhash.DisplayHashParams;
 import android.service.displayhash.DisplayHasherService;
 import android.service.displayhash.IDisplayHasherService;
+import android.util.AttributeSet;
 import android.util.Size;
 import android.util.Slog;
+import android.util.Xml;
 import android.view.MagnificationSpec;
 import android.view.SurfaceControl;
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
@@ -102,6 +114,41 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final RectF mTmpRectF = new RectF();
 
+    /**
+     * Lock used when retrieving xml metadata. Lock when retrieving the xml data the first time
+     * since it will be cached after that. Check if {@link #mParsedXml} is set to determine if the
+     * metadata needs to retrieved.
+     */
+    private final Object mParseXmlLock = new Object();
+
+    /**
+     * Flag whether the xml metadata has been retrieved and parsed. Once this is set to true,
+     * there's no need to request metadata again.
+     */
+    @GuardedBy("mParseXmlLock")
+    private boolean mParsedXml;
+
+    /**
+     * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
+     * than once per throttleTime
+     */
+    private int mThrottleDurationMillis = 0;
+
+    /**
+     * The last time an app requested to generate a display hash in System time.
+     */
+    private long mLastRequestTimeMs;
+
+    /**
+     * The last uid that requested to generate a hash.
+     */
+    private int mLastRequestUid;
+
+    /**
+     * Only used for testing. Throttling should always be enabled unless running tests
+     */
+    private boolean mDisplayHashThrottlingEnabled = true;
+
     private interface Command {
         void run(IDisplayHasherService service) throws RemoteException;
     }
@@ -131,6 +178,10 @@
         return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
     }
 
+    void setDisplayHashThrottlingEnabled(boolean enable) {
+        mDisplayHashThrottlingEnabled = enable;
+    }
+
     private void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
             String hashAlgorithm, RemoteCallback callback) {
         connectAndRun(
@@ -138,8 +189,36 @@
                         callback));
     }
 
+    private boolean allowedToGenerateHash(int uid) {
+        if (!mDisplayHashThrottlingEnabled) {
+            // Always allow to generate the hash. This is used to allow tests to run without
+            // waiting on the designated threshold.
+            return true;
+        }
+
+        long currentTime = System.currentTimeMillis();
+        if (mLastRequestUid != uid) {
+            mLastRequestUid = uid;
+            mLastRequestTimeMs = currentTime;
+            return true;
+        }
+
+        int throttleDurationMs = getThrottleDurationMillis();
+        if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+            return false;
+        }
+
+        mLastRequestTimeMs = currentTime;
+        return true;
+    }
+
     void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
-            Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback) {
+            Rect boundsInWindow, String hashAlgorithm, int uid, RemoteCallback callback) {
+        if (!allowedToGenerateHash(uid)) {
+            sendDisplayHashError(callback, DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS);
+            return;
+        }
+
         final Map<String, DisplayHashParams> displayHashAlgorithmsMap = getDisplayHashAlgorithms();
         DisplayHashParams displayHashParams = displayHashAlgorithmsMap.get(hashAlgorithm);
         if (displayHashParams == null) {
@@ -277,6 +356,64 @@
         }
     }
 
+    private int getThrottleDurationMillis() {
+        if (!parseXmlProperties()) {
+            return 0;
+        }
+        return mThrottleDurationMillis;
+    }
+
+    private boolean parseXmlProperties() {
+        // We have a separate lock for the xml parsing since it doesn't need to make the
+        // request through the service connection. Instead, we have a lock to ensure we can
+        // properly cache the xml metadata so we don't need to call into the  ExtServices
+        // process for each request.
+        synchronized (mParseXmlLock) {
+            if (mParsedXml) {
+                return true;
+            }
+
+            final ServiceInfo serviceInfo = getServiceInfo();
+            if (serviceInfo == null) return false;
+
+            final PackageManager pm = mContext.getPackageManager();
+
+            XmlResourceParser parser;
+            parser = serviceInfo.loadXmlMetaData(pm, SERVICE_META_DATA);
+            if (parser == null) {
+                return false;
+            }
+
+            Resources res;
+            try {
+                res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while (true) {
+                try {
+                    if (!((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                            && type != XmlPullParser.START_TAG)) {
+                        break;
+                    }
+                } catch (XmlPullParserException | IOException e) {
+                    return false;
+                }
+            }
+
+            TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHasherService);
+            mThrottleDurationMillis = sa.getInt(
+                    R.styleable.DisplayHasherService_throttleDurationMillis, 0);
+            sa.recycle();
+            mParsedXml = true;
+            return true;
+        }
+    }
+
     /**
      * Run a command, starting the service connection if necessary.
      */
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6046cc6..9ff701c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1539,20 +1539,6 @@
             }
         }
 
-        @Override
-        public boolean canUseRotationResolver() {
-            if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
-
-            switch (mCurrentAppOrientation) {
-                case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
-                case ActivityInfo.SCREEN_ORIENTATION_USER:
-                case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
-                case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
-                case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
-                    return true;
-            }
-            return false;
-        }
 
         @Override
         public void onProposedRotationChanged(int rotation) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 8148f15..ab515d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -113,7 +113,7 @@
          *
          * @param magnificationRegion the current magnification region
          */
-        public void onMagnificationRegionChanged(Region magnificationRegion);
+        void onMagnificationRegionChanged(Region magnificationRegion);
 
         /**
          * Called when an application requests a rectangle on the screen to allow
@@ -124,20 +124,27 @@
          * @param right The rectangle right.
          * @param bottom The rectangle bottom.
          */
-        public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
+        void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
 
         /**
          * Notifies that the rotation changed.
          *
          * @param rotation The current rotation.
          */
-        public void onRotationChanged(int rotation);
+        void onRotationChanged(int rotation);
 
         /**
          * Notifies that the context of the user changed. For example, an application
          * was started.
          */
-        public void onUserContextChanged();
+        void onUserContextChanged();
+
+        /**
+         * Notifies that the IME window visibility changed.
+         * @param shown {@code true} means the IME window shows on the screen. Otherwise it's
+         *                           hidden.
+         */
+        void onImeWindowVisibilityChanged(boolean shown);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d494b75..a670b39 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8626,6 +8626,14 @@
         return mDisplayHashController.verifyDisplayHash(displayHash);
     }
 
+    @Override
+    public void setDisplayHashThrottlingEnabled(boolean enable) {
+        if (!checkCallingPermission(READ_FRAME_BUFFER, "setDisplayHashThrottle()")) {
+            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+        }
+        mDisplayHashController.setDisplayHashThrottlingEnabled(enable);
+    }
+
     void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
             String hashAlgorithm, RemoteCallback callback) {
         final SurfaceControl displaySurfaceControl;
@@ -8639,6 +8647,13 @@
                 return;
             }
 
+            if (win.mActivityRecord == null || !win.mActivityRecord.isState(
+                    Task.ActivityState.RESUMED)) {
+                mDisplayHashController.sendDisplayHashError(callback,
+                        DISPLAY_HASH_ERROR_MISSING_WINDOW);
+                return;
+            }
+
             DisplayContent displayContent = win.getDisplayContent();
             if (displayContent == null) {
                 Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
@@ -8669,8 +8684,8 @@
                         .setUid(uid)
                         .setSourceCrop(boundsInDisplay);
 
-        mDisplayHashController.generateDisplayHash(args, boundsInWindow,
-                hashAlgorithm, callback);
+        mDisplayHashController.generateDisplayHash(args, boundsInWindow, hashAlgorithm, uid,
+                callback);
     }
 
     boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 49e704f..b971cab 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -275,19 +275,6 @@
         }
     }
 
-    /**
-     * Returns true if the current status of the phone is suitable for using rotation resolver
-     * service.
-     *
-     * To reduce the power consumption of rotation resolver service, rotation query should run less
-     * frequently than other low power orientation sensors. This method is used to check whether
-     * the current status of the phone is necessary to request a suggested screen rotation from the
-     * rotation resolver service. Note that it always returns {@code false} in the base class. It
-     * should be overridden in the derived classes.
-     */
-    public boolean canUseRotationResolver() {
-        return false;
-    }
 
     /**
      * Returns true if the rotation resolver feature is enabled by setting. It means {@link
@@ -1155,7 +1142,7 @@
                 }
             }
 
-            if (isRotationResolverEnabled() && canUseRotationResolver()) {
+            if (isRotationResolverEnabled()) {
                 if (mRotationResolverService == null) {
                     mRotationResolverService = LocalServices.getService(
                             RotationResolverInternal.class);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index eb83152..92c9522 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3462,6 +3462,9 @@
         if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
             mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
         }
+        if (mIsImWindow && mWmService.mAccessibilityController != null) {
+            mWmService.mAccessibilityController.onImeSurfaceShownChanged(this, shown);
+        }
     }
 
     private void logExclusionRestrictions(int side) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 29aedce..1208ecc 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -30,6 +30,8 @@
 import android.os.ServiceManager;
 import android.os.UpdateEngine;
 import android.os.UpdateEngineCallback;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.DeviceConfig;
 import android.util.Log;
 
@@ -301,10 +303,18 @@
 
         new Thread(() -> {
             try {
-                String reportPath = mIProfcollect.report();
+                String reportUuid = mIProfcollect.report();
+
                 if (!uploadReport) {
                     return;
                 }
+
+                final int profileId = getBBProfileId();
+                mIProfcollect.copy_report_to_bb(profileId, reportUuid);
+                String reportPath =
+                        "/data/user/" + profileId
+                        + "/com.google.android.apps.internal.betterbug/cache/"
+                        + reportUuid + ".zip";
                 Intent uploadIntent =
                         new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
                         .setPackage("com.google.android.apps.internal.betterbug")
@@ -316,9 +326,27 @@
                 if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
                     context.sendBroadcast(uploadIntent);
                 }
+                mIProfcollect.delete_report(reportUuid);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, e.getMessage());
             }
         }).start();
     }
+
+    /**
+     * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system
+     * user ID.
+     *
+     * @return BetterBug's profile ID.
+     */
+    private int getBBProfileId() {
+        UserManager userManager = UserManager.get(getContext());
+        int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false);
+        for (int p : profiles) {
+            if (userManager.getUserInfo(p).isManagedProfile()) {
+                return p;
+            }
+        }
+        return UserHandle.USER_SYSTEM;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 29691fb..502f64a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -16,7 +16,7 @@
 
 package com.android.server.accessibility.magnification;
 
-import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -96,8 +96,8 @@
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
     private final MagnificationAnimationCallback mAnimationCallback = mock(
             MagnificationAnimationCallback.class);
-    private final MagnificationRequestObserver mRequestObserver = mock(
-            MagnificationRequestObserver.class);
+    private final MagnificationInfoChangedCallback mRequestObserver = mock(
+            MagnificationInfoChangedCallback.class);
     final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
 
     ValueAnimator mMockValueAnimator;
@@ -1145,6 +1145,15 @@
         verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false));
     }
 
+    @Test
+    public void testImeWindowIsShown_serviceNotified() {
+        register(DISPLAY_0);
+        MagnificationCallbacks callbacks = getMagnificationCallbacks(DISPLAY_0);
+        callbacks.onImeWindowVisibilityChanged(true);
+        mMessageCapturingHandler.sendAllMessages();
+        verify(mRequestObserver).onImeWindowVisibilityChanged(eq(true));
+    }
+
     private void setScaleToMagnifying() {
         register(DISPLAY_0);
         float scale = 2.0f;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 28a6ff7..f881f04 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -53,7 +53,7 @@
 
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.EventStreamTransformation;
-import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver;
+import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
 import com.android.server.wm.WindowManagerInternal;
@@ -126,7 +126,7 @@
     @Mock
     MagnificationGestureHandler.Callback mMockCallback;
     @Mock
-    MagnificationRequestObserver mMagnificationRequestObserver;
+    MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
     @Mock
     WindowMagnificationPromptController mWindowMagnificationPromptController;
 
@@ -151,7 +151,7 @@
         when(mockController.getAnimationDuration()).thenReturn(1000L);
         when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
         mFullScreenMagnificationController = new FullScreenMagnificationController(mockController,
-                new Object(), mMagnificationRequestObserver) {
+                new Object(), mMagnificationInfoChangedCallback) {
             @Override
             public boolean magnificationRegionContains(int displayId, float x, float y) {
                 return true;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cf23197..3dff36e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -486,6 +486,47 @@
                 eq(MODE_WINDOW));
     }
 
+    @Test
+    public void imeWindowStateShown_windowMagnifying_logWindowMode() {
+        mMagnificationController.onWindowMagnificationActivationState(true);
+
+        mMagnificationController.onImeWindowVisibilityChanged(true);
+
+        verify(mMagnificationController).logMagnificationModeWithIme(
+                eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+    }
+
+    @Test
+    public void imeWindowStateShown_fullScreenMagnifying_logFullScreenMode() {
+        mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+        mMagnificationController.onImeWindowVisibilityChanged(true);
+
+        verify(mMagnificationController).logMagnificationModeWithIme(
+                eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
+    }
+
+    @Test
+    public void imeWindowStateShown_noMagnifying_noLogAnyMode() {
+        mMagnificationController.onImeWindowVisibilityChanged(true);
+
+        verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+    }
+
+    @Test
+    public void imeWindowStateHidden_windowMagnifying_noLogAnyMode() {
+        mMagnificationController.onFullScreenMagnificationActivationState(true);
+
+        verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+    }
+
+    @Test
+    public void imeWindowStateHidden_fullScreenMagnifying_noLogAnyMode() {
+        mMagnificationController.onWindowMagnificationActivationState(true);
+
+        verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+    }
+
     private void setMagnificationEnabled(int mode) throws RemoteException {
 
         setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index ef3e320..88b0651 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -850,6 +850,12 @@
         }
 
         @Override
+        public void getStorageInfo(String packageName, String databaseName, int userId,
+                IAppSearchResultCallback callback) throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
         public void persistToDisk(int userId) throws RemoteException {
 
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index f5d0ca7..584bcf4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -58,7 +58,6 @@
     private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
     private int mFinalizedRotation;
     private boolean mRotationResolverEnabled;
-    private boolean mCanUseRotationResolver;
     private SensorEvent mFakeSensorEvent;
     private Sensor mFakeSensor;
 
@@ -66,7 +65,6 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mRotationResolverEnabled = true;
-        mCanUseRotationResolver = true;
 
         mFakeRotationResolverInternal = new TestableRotationResolver();
         doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
@@ -89,16 +87,6 @@
     }
 
     @Test
-    public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
-        mCanUseRotationResolver = false;
-
-        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
-
-        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
-
-    }
-
-    @Test
     public void testOnSensorChanged_normalCase() {
         mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
 
@@ -139,11 +127,6 @@
         }
 
         @Override
-        public boolean canUseRotationResolver() {
-            return mCanUseRotationResolver;
-        }
-
-        @Override
         public boolean isRotationResolverEnabled() {
             return mRotationResolverEnabled;
         }
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 3025a95..222c692 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -26,6 +26,7 @@
 import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.app.Instrumentation;
@@ -62,6 +63,10 @@
     private long mLongPressTime;
     private long mVeryLongPressTime;
 
+    // Allow press from non interactive mode.
+    private boolean mAllowNonInteractiveForPress = true;
+    private boolean mAllowNonInteractiveForLongPress = true;
+
     @Before
     public void setUp() {
         mDetector = new SingleKeyGestureDetector(mContext);
@@ -81,11 +86,17 @@
             }
             @Override
             public void onPress(long downTime) {
+                if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
+                    return;
+                }
                 mShortPressed.countDown();
             }
 
             @Override
             void onLongPress(long downTime) {
+                if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForLongPress) {
+                    return;
+                }
                 mLongPressed.countDown();
             }
 
@@ -96,6 +107,9 @@
 
             @Override
             void onMultiPress(long downTime, int count) {
+                if (mDetector.beganFromNonInteractive() && !mAllowNonInteractiveForPress) {
+                    return;
+                }
                 mMultiPressed.countDown();
                 assertEquals(mMaxMultiPressPowerCount, count);
             }
@@ -103,9 +117,13 @@
     }
 
     private void pressKey(long eventTime, int keyCode, long pressTime) {
+        pressKey(eventTime, keyCode, pressTime, true /* interactive */);
+    }
+
+    private void pressKey(long eventTime, int keyCode, long pressTime, boolean interactive) {
         final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
                 keyCode, 0 /* repeat */, 0 /* metaState */);
-        mDetector.interceptKey(keyDown);
+        mDetector.interceptKey(keyDown, interactive);
 
         // keep press down.
         try {
@@ -118,7 +136,7 @@
         final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP,
                 keyCode, 0 /* repeat */, 0 /* metaState */);
 
-        mDetector.interceptKey(keyUp);
+        mDetector.interceptKey(keyUp, interactive);
     }
 
     @Test
@@ -149,4 +167,18 @@
         pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
         assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
     }
+
+    @Test
+    public void testNonInteractive() throws InterruptedException {
+        long eventTime = SystemClock.uptimeMillis();
+        // Disallow short press behavior from non interactive.
+        mAllowNonInteractiveForPress = false;
+        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
+        assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+
+        // Allow long press behavior from non interactive.
+        eventTime = SystemClock.uptimeMillis();
+        pressKey(eventTime, KEYCODE_POWER, mLongPressTime, false /* interactive */);
+        assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
+    }
 }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 6315b24..b384e50 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -391,6 +391,7 @@
      * event to the framework.
      * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
      * exchange if it is supported by the device.
+     * @hide
      */
     public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
             @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) {
@@ -399,14 +400,45 @@
     }
 
     /**
+     * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either
+     * presence or OPTIONS for capability exchange.
+     *
+     * Will only be requested by the framework if capability exchange is configured
+     * as capable during a
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
+     * exchange if it is supported by the device.
+     */
+    public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+            @NonNull CapabilityExchangeEventListener listener) {
+        // Base Implementation, override to implement functionality
+        return new RcsCapabilityExchangeImplBase();
+    }
+
+    /**
      * Remove the given CapabilityExchangeImplBase instance.
      * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed.
+     * @hide
      */
     public void removeCapabilityExchangeImpl(
             @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
         // Override to implement the process of removing RcsCapabilityExchangeImplBase instance.
     }
 
+    /**
+     * Remove the given CapabilityExchangeImplBase instance.
+     * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+     */
+    public void destroyCapabilityExchangeImpl(
+            @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
+        // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
+    }
+
     /**{@inheritDoc}*/
     @Override
     public void onFeatureRemoved() {
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 25b9446..a117adc 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -356,12 +356,13 @@
         void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
     }
 
-    private final Executor mBinderExecutor;
+    private Executor mBinderExecutor;
 
     /**
      * Create a new RcsCapabilityExchangeImplBase instance.
      *
      * @param executor The executor that remote calls from the framework will be called on.
+     * @hide
      */
     public RcsCapabilityExchangeImplBase(@NonNull Executor executor) {
         if (executor == null) {
@@ -371,6 +372,12 @@
     }
 
     /**
+     * Create a new RcsCapabilityExchangeImplBase instance.
+     */
+    public RcsCapabilityExchangeImplBase() {
+    }
+
+    /**
      * The user capabilities of one or multiple contacts have been requested by the framework.
      * <p>
      * The implementer must follow up this call with an
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index e6a4501..ed0a98d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -101,10 +101,10 @@
         }
     } else {
         assertLayersStart {
-            this.coversAtLeast(startingBounds)
+            this.visibleRegion().coversAtLeast(startingBounds)
         }
         assertLayersEnd {
-            this.coversAtLeast(endingBounds)
+            this.visibleRegion().coversAtLeast(endingBounds)
         }
     }
 }
@@ -152,10 +152,10 @@
     val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
 
     assertLayersStart {
-        this.coversExactly(startingPos, NAV_BAR_LAYER_NAME)
+        this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
     }
     assertLayersEnd {
-        this.coversExactly(endingPos, NAV_BAR_LAYER_NAME)
+        this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
     }
 }
 
@@ -168,10 +168,10 @@
     val endingPos = WindowUtils.getStatusBarPosition(endRotation)
 
     assertLayersStart {
-        this.coversExactly(startingPos, STATUS_BAR_WINDOW_NAME)
+        this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(startingPos)
     }
     assertLayersEnd {
-        this.coversExactly(endingPos, STATUS_BAR_WINDOW_NAME)
+        this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(endingPos)
     }
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index e118363..a524466 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
+import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -87,13 +88,13 @@
         testSpec.statusBarLayerIsAlwaysVisible()
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     open fun statusBarLayerRotatesScales() {
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 619a05e..bd7c185 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -34,7 +34,7 @@
         wmHelper: WindowManagerStateHelper?
     ) {
         // do nothing (the app is focused automatically)
-        waitAndAssertIMEShown(device, wmHelper)
+        waitIMEShown(device, wmHelper)
     }
 
     override fun open() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d8091a9..83fddae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -51,17 +51,17 @@
                 "was left in an unknown state (e.g. in split screen)"
         }
         editText.click()
-        waitAndAssertIMEShown(device, wmHelper)
+        waitIMEShown(device, wmHelper)
     }
 
-    protected fun waitAndAssertIMEShown(
+    protected fun waitIMEShown(
         device: UiDevice,
         wmHelper: WindowManagerStateHelper? = null
     ) {
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+            wmHelper.waitImeWindowShown()
         }
     }
 
@@ -78,7 +78,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+            wmHelper.waitImeWindowGone()
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index d39044ab..7ba9db1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -20,6 +20,7 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -134,7 +135,7 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index f037f1d..35ad597 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -24,7 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,20 +60,6 @@
 
     @Presubmit
     @Test
-    override fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        super.navBarLayerRotatesAndScales()
-    }
-
-    @FlakyTest(bugId = 140855415)
-    @Test
-    fun navBarLayerRotatesAndScales_flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        super.navBarLayerRotatesAndScales()
-    }
-
-    @Presubmit
-    @Test
     fun screenshotLayerBecomesInvisible() {
         testSpec.assertLayers {
             this.isVisible(testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index f037f08..2989035 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -139,7 +139,7 @@
     @Test
     open fun appLayerRotates_StartingPos() {
         testSpec.assertLayersStart {
-            this.coversExactly(startingPos, testApp.getPackage())
+            this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
         }
     }
 
@@ -147,7 +147,7 @@
     @Test
     open fun appLayerRotates_EndingPos() {
         testSpec.assertLayersEnd {
-            this.coversExactly(endingPos, testApp.getPackage())
+            this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
index b7a42ec..fee65f0 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -16,6 +16,9 @@
 
 package android.net;
 
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
+
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 
 import static org.junit.Assert.assertEquals;
@@ -33,23 +36,33 @@
 
     @Test
     public void testParceling() {
-        VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
-        assertParcelSane(v, 1 /* fieldCount */);
+        VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+        assertParcelSane(v, 2 /* fieldCount */);
     }
 
     @Test
     public void testEqualsAndHashCode() {
-        VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
-        VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
-        VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
-        VpnTransportInfo v4 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY);
-        VpnTransportInfo v5 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM);
+        String session1 = "12345";
+        String session2 = "6789";
+        VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+        VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1);
+        VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+        VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1);
+        VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1);
+        VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2);
 
-        assertNotEquals(v1, v2);
-        assertNotEquals(v3, v4);
-        assertNotEquals(v4, v5);
+        VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+        VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
 
-        assertEquals(v1, v3);
-        assertEquals(v1.hashCode(), v3.hashCode());
+        assertNotEquals(v11, v12);
+        assertNotEquals(v13, v14);
+        assertNotEquals(v14, v15);
+        assertNotEquals(v14, v21);
+
+        assertEquals(v11, v13);
+        assertEquals(v31, v32);
+        assertEquals(v11.hashCode(), v13.hashCode());
+        assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions());
+        assertEquals(session1, v15.makeCopy(REDACT_NONE).sessionId);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
index b62bdbc..5006d53 100644
--- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -101,7 +101,7 @@
 
         // Check valid customization generates expected array.
         val validRes = arrayOf("0,3", "1,0", "4,4")
-        val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
+        val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0)
 
         val mockContext = getMockedContextWithStringArrayRes(
                 R.array.config_networkSupportedKeepaliveCount,
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 54d4938..8254f6b3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -717,6 +717,9 @@
         private int mProbesSucceeded;
         private String mNmValidationRedirectUrl = null;
         private boolean mNmProvNotificationRequested = false;
+        private Runnable mCreatedCallback;
+        private Runnable mUnwantedCallback;
+        private Runnable mDisconnectedCallback;
 
         private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
         // Contains the redirectUrl from networkStatus(). Before reading, wait for
@@ -771,6 +774,24 @@
                     mRedirectUrl = redirectUrl;
                     mNetworkStatusReceived.open();
                 }
+
+                @Override
+                public void onNetworkCreated() {
+                    super.onNetworkCreated();
+                    if (mCreatedCallback != null) mCreatedCallback.run();
+                }
+
+                @Override
+                public void onNetworkUnwanted() {
+                    super.onNetworkUnwanted();
+                    if (mUnwantedCallback != null) mUnwantedCallback.run();
+                }
+
+                @Override
+                public void onNetworkDestroyed() {
+                    super.onNetworkDestroyed();
+                    if (mDisconnectedCallback != null) mDisconnectedCallback.run();
+                }
             };
 
             assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId);
@@ -972,6 +993,18 @@
             p.timestampMillis = DATA_STALL_TIMESTAMP;
             mNmCallbacks.notifyDataStallSuspected(p);
         }
+
+        public void setCreatedCallback(Runnable r) {
+            mCreatedCallback = r;
+        }
+
+        public void setUnwantedCallback(Runnable r) {
+            mUnwantedCallback = r;
+        }
+
+        public void setDisconnectedCallback(Runnable r) {
+            mDisconnectedCallback = r;
+        }
     }
 
     /**
@@ -1182,10 +1215,12 @@
             if (mAgentRegistered) throw new IllegalStateException("already registered");
             updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
             mConfig = new VpnConfig();
+            mConfig.session = "MySession12345";
             setUids(uids);
             if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
             mInterface = VPN_IFNAME;
-            mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+            mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(),
+                    mConfig.session));
             mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
                     mNetworkCapabilities);
             mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -2800,6 +2835,94 @@
     }
 
     @Test
+    public void testNetworkAgentCallbacks() throws Exception {
+        // Keeps track of the order of events that happen in this test.
+        final LinkedBlockingQueue<String> eventOrder = new LinkedBlockingQueue<>();
+
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI).build();
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        final AtomicReference<Network> wifiNetwork = new AtomicReference<>();
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+        // Expectations for state when various callbacks fire. These expectations run on the handler
+        // thread and not on the test thread because they need to prevent the handler thread from
+        // advancing while they examine state.
+
+        // 1. When onCreated fires, netd has been told to create the network.
+        mWiFiNetworkAgent.setCreatedCallback(() -> {
+            eventOrder.offer("onNetworkCreated");
+            wifiNetwork.set(mWiFiNetworkAgent.getNetwork());
+            assertNotNull(wifiNetwork.get());
+            try {
+                verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(),
+                        INetd.PERMISSION_NONE);
+            } catch (RemoteException impossible) {
+                fail();
+            }
+        });
+
+        // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just
+        //    check that it is fired at some point after disconnect.
+        mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted"));
+
+        // 3. While the teardown timer is running, connectivity APIs report the network is gone, but
+        //    netd has not yet been told to destroy it.
+        final Runnable duringTeardown = () -> {
+            eventOrder.offer("timePasses");
+            assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+            try {
+                verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId());
+            } catch (RemoteException impossible) {
+                fail();
+            }
+        };
+
+        // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone,
+        // and netd has been told to destroy it.
+        mWiFiNetworkAgent.setDisconnectedCallback(() -> {
+            eventOrder.offer("onNetworkDisconnected");
+            assertNull(mCm.getLinkProperties(wifiNetwork.get()));
+            try {
+                verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId());
+            } catch (RemoteException impossible) {
+                fail();
+            }
+        });
+
+        // Connect a network, and file a request for it after it has come up, to ensure the nascent
+        // timer is cleared and the test does not have to wait for it. Filing the request after the
+        // network has come up is necessary because ConnectivityService does not appear to clear the
+        // nascent timer if the first request satisfied by the network was filed before the network
+        // connected.
+        // TODO: fix this bug, file the request before connecting, and remove the waitForIdle.
+        mWiFiNetworkAgent.connectWithoutInternet();
+        waitForIdle();
+        mCm.requestNetwork(request, callback);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        // Set teardown delay and make sure CS has processed it.
+        mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMs(300);
+        waitForIdle();
+
+        // Post the duringTeardown lambda to the handler so it fires while teardown is in progress.
+        // The delay must be long enough it will run after the unregisterNetworkCallback has torn
+        // down the network and started the teardown timer, and short enough that the lambda is
+        // scheduled to run before the teardown timer.
+        final Handler h = new Handler(mCsHandlerThread.getLooper());
+        h.postDelayed(duringTeardown, 150);
+
+        // Disconnect the network and check that events happened in the right order.
+        mCm.unregisterNetworkCallback(callback);
+        assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        mCm.unregisterNetworkCallback(callback);
+    }
+
+    @Test
     public void testExplicitlySelected() throws Exception {
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index a447cef..24c60b7 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -42,6 +42,10 @@
 using android::idmap2::policy::kPolicyStringToFlag;
 
 namespace aapt {
+namespace {
+constexpr const char* kPublicGroupTag = "public-group";
+constexpr const char* kStagingPublicGroupTag = "staging-public-group";
+}  // namespace
 
 constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
 
@@ -102,6 +106,7 @@
 
   ResourceId id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
+  bool staged_api = false;
   bool allow_new = false;
   Maybe<OverlayableItem> overlayable_item;
 
@@ -122,6 +127,7 @@
   if (res->visibility_level != Visibility::Level::kUndefined) {
     Visibility visibility;
     visibility.level = res->visibility_level;
+    visibility.staged_api = res->staged_api;
     visibility.source = res->source;
     visibility.comment = res->comment;
     res_builder.SetVisibility(visibility);
@@ -525,6 +531,7 @@
       {"plurals", std::mem_fn(&ResourceParser::ParsePlural)},
       {"public", std::mem_fn(&ResourceParser::ParsePublic)},
       {"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
+      {"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
       {"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
       {"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
                           std::placeholders::_2, std::placeholders::_3)},
@@ -653,7 +660,8 @@
     const auto bag_iter = elToBagMap.find(resource_type);
     if (bag_iter != elToBagMap.end()) {
       // Ensure we have a name (unless this is a <public-group> or <overlayable>).
-      if (resource_type != "public-group" && resource_type != "overlayable") {
+      if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
+          resource_type != "overlayable") {
         if (!maybe_name) {
           diag_->Error(DiagMessage(out_resource->source)
                        << "<" << parser->element_name() << "> missing 'name' attribute");
@@ -890,54 +898,45 @@
   return true;
 }
 
-bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
-  if (options_.visibility) {
-    diag_->Error(DiagMessage(out_resource->source)
-                 << "<public-group> tag not allowed with --visibility flag");
-    return false;
-  }
-
+template <typename Func>
+bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
+                           const char* tag_name, IDiagnostics* diag, Func&& func) {
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
-    diag_->Warn(DiagMessage(out_resource->source)
-                << "ignoring configuration '" << out_resource->config
-                << "' for <public-group> tag");
+    diag->Warn(DiagMessage(out_resource->source)
+               << "ignoring configuration '" << out_resource->config << "' for <" << tag_name
+               << "> tag");
   }
 
   Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
   if (!maybe_type) {
-    diag_->Error(DiagMessage(out_resource->source)
-                 << "<public-group> must have a 'type' attribute");
+    diag->Error(DiagMessage(out_resource->source)
+                << "<" << tag_name << "> must have a 'type' attribute");
     return false;
   }
 
   const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
   if (!parsed_type) {
-    diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
-                                                   << maybe_type.value()
-                                                   << "' in <public-group>");
+    diag->Error(DiagMessage(out_resource->source)
+                << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
     return false;
   }
 
-  Maybe<StringPiece> maybe_id_str =
-      xml::FindNonEmptyAttribute(parser, "first-id");
+  Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
   if (!maybe_id_str) {
-    diag_->Error(DiagMessage(out_resource->source)
-                 << "<public-group> must have a 'first-id' attribute");
+    diag->Error(DiagMessage(out_resource->source)
+                << "<" << tag_name << "> must have a 'first-id' attribute");
     return false;
   }
 
-  Maybe<ResourceId> maybe_id =
-      ResourceUtils::ParseResourceId(maybe_id_str.value());
+  Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
   if (!maybe_id) {
-    diag_->Error(DiagMessage(out_resource->source) << "invalid resource ID '"
-                                                   << maybe_id_str.value()
-                                                   << "' in <public-group>");
+    diag->Error(DiagMessage(out_resource->source)
+                << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
     return false;
   }
 
-  ResourceId next_id = maybe_id.value();
-
   std::string comment;
+  ResourceId next_id = maybe_id.value();
   bool error = false;
   const size_t depth = parser->depth();
   while (xml::XmlPullParser::NextChildNode(parser, depth)) {
@@ -949,53 +948,72 @@
       continue;
     }
 
-    const Source item_source = source_.WithLine(parser->line_number());
+    const Source item_source = out_resource->source.WithLine(parser->line_number());
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "public") {
-      Maybe<StringPiece> maybe_name =
-          xml::FindNonEmptyAttribute(parser, "name");
+      auto maybe_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!maybe_name) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<public> must have a 'name' attribute");
+        diag->Error(DiagMessage(item_source) << "<public> must have a 'name' attribute");
         error = true;
         continue;
       }
 
       if (xml::FindNonEmptyAttribute(parser, "id")) {
-        diag_->Error(DiagMessage(item_source)
-                     << "'id' is ignored within <public-group>");
+        diag->Error(DiagMessage(item_source) << "'id' is ignored within <" << tag_name << ">");
         error = true;
         continue;
       }
 
       if (xml::FindNonEmptyAttribute(parser, "type")) {
-        diag_->Error(DiagMessage(item_source)
-                     << "'type' is ignored within <public-group>");
+        diag->Error(DiagMessage(item_source) << "'type' is ignored within <" << tag_name << ">");
         error = true;
         continue;
       }
 
-      ParsedResource child_resource;
-      child_resource.name.type = *parsed_type;
-      child_resource.name.entry = maybe_name.value().to_string();
-      child_resource.id = next_id;
-      // NOLINTNEXTLINE(bugprone-use-after-move) move+reset comment
-      child_resource.comment = std::move(comment);
-      child_resource.source = item_source;
-      child_resource.visibility_level = Visibility::Level::kPublic;
-      out_resource->child_resources.push_back(std::move(child_resource));
+      ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
+          .name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
+          .source = item_source,
+          .id = next_id,
+          .comment = std::move(comment),
+      });
 
-      next_id.id += 1;
+      // Execute group specific code.
+      func(entry_res, next_id);
 
+      next_id.id++;
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
-      diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+      diag->Error(DiagMessage(item_source) << ":" << element_name << ">");
       error = true;
     }
   }
   return !error;
 }
 
+bool ResourceParser::ParseStagingPublicGroup(xml::XmlPullParser* parser,
+                                             ParsedResource* out_resource) {
+  return ParseGroupImpl(parser, out_resource, kStagingPublicGroupTag, diag_,
+                        [](ParsedResource& parsed_entry, ResourceId id) {
+                          parsed_entry.id = id;
+                          parsed_entry.staged_api = true;
+                          parsed_entry.visibility_level = Visibility::Level::kPublic;
+                        });
+}
+
+bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+  if (options_.visibility) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag");
+    return false;
+  }
+
+  return ParseGroupImpl(parser, out_resource, kPublicGroupTag, diag_,
+                        [](ParsedResource& parsed_entry, ResourceId id) {
+                          parsed_entry.id = id;
+                          parsed_entry.visibility_level = Visibility::Level::kPublic;
+                        });
+}
+
 bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
                                      ParsedResource* out_resource) {
   Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 9d3ecc8..af0db8c 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -99,6 +99,7 @@
 
   bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+  bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 53bfa0b..4a509be 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -840,6 +840,31 @@
   EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010041)));
 }
 
+TEST_F(ResourceParserTest, StagingPublicGroup) {
+  std::string input = R"(
+      <staging-public-group type="attr" first-id="0x01ff0049">
+        <public name="foo" />
+        <public name="bar" />
+      </staging-public-group>)";
+  ASSERT_TRUE(TestParse(input));
+
+  Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+  ASSERT_TRUE(result);
+
+  ASSERT_TRUE(result.value().entry->id);
+  EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff0049)));
+  EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+  EXPECT_TRUE(result.value().entry->visibility.staged_api);
+
+  result = table_.FindResource(test::ParseNameOrDie("attr/bar"));
+  ASSERT_TRUE(result);
+
+  ASSERT_TRUE(result.value().entry->id);
+  EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01ff004a)));
+  EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+  EXPECT_TRUE(result.value().entry->visibility.staged_api);
+}
+
 TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
   std::string input = R"(
       <!-- private -->
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index cff9872..27f7bdd 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -47,11 +47,6 @@
 }
 
 template <typename T>
-bool less_than_type_and_id(const T& lhs, const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
-  return lhs.id != rhs.second ? lhs.id < rhs.second : lhs.type < rhs.first;
-}
-
-template <typename T>
 bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
   return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
 }
@@ -80,12 +75,6 @@
   return lhs.name.compare(0, lhs.name.size(), rhs.first.data(), rhs.first.size()) < 0;
 }
 
-template <typename T, typename U>
-bool less_than_struct_with_name_and_id_pointer(const T* lhs,
-                                               const std::pair<std::string_view, Maybe<U>>& rhs) {
-  return less_than_struct_with_name_and_id(*lhs, rhs);
-}
-
 template <typename T, typename Func, typename Elements>
 T* FindElementsRunAction(const android::StringPiece& name, Elements& entries, Func action) {
   const auto iter =
@@ -307,51 +296,115 @@
   return CollisionResult::kConflict;
 }
 
+template <typename T, typename Comparer>
+struct SortedVectorInserter : public Comparer {
+  std::pair<bool, typename std::vector<T>::iterator> LowerBound(std::vector<T>& el,
+                                                                const T& value) {
+    auto it = std::lower_bound(el.begin(), el.end(), value, [&](auto& lhs, auto& rhs) {
+      return Comparer::operator()(lhs, rhs);
+    });
+    bool found =
+        it != el.end() && !Comparer::operator()(*it, value) && !Comparer::operator()(value, *it);
+    return std::make_pair(found, it);
+  }
+
+  T* Insert(std::vector<T>& el, T&& value) {
+    auto [found, it] = LowerBound(el, value);
+    if (found) {
+      return &*it;
+    }
+    return &*el.insert(it, std::move(value));
+  }
+};
+
+struct PackageViewComparer {
+  bool operator()(const ResourceTablePackageView& lhs, const ResourceTablePackageView& rhs) {
+    return less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>(
+        lhs, std::make_pair(rhs.name, rhs.id));
+  }
+};
+
+struct TypeViewComparer {
+  bool operator()(const ResourceTableTypeView& lhs, const ResourceTableTypeView& rhs) {
+    return lhs.id != rhs.id ? lhs.id < rhs.id : lhs.type < rhs.type;
+  }
+};
+
+struct EntryViewComparer {
+  bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) {
+    return less_than_struct_with_name_and_id<ResourceEntry, ResourceId>(
+        *lhs, std::make_pair(rhs->name, rhs->id));
+  }
+};
+
 ResourceTableView ResourceTable::GetPartitionedView() const {
   ResourceTableView view;
+  SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
+  SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
+  SortedVectorInserter<const ResourceEntry*, EntryViewComparer> entry_inserter;
+
   for (const auto& package : packages) {
     for (const auto& type : package->types) {
       for (const auto& entry : type->entries) {
-        std::pair<std::string_view, Maybe<uint8_t>> package_key(package->name, {});
-        std::pair<std::string_view, Maybe<ResourceId>> entry_key(entry->name, {});
-        std::pair<ResourceType, Maybe<uint8_t>> type_key(type->type, {});
-        if (entry->id) {
-          // If the entry has a defined id, use the id to determine insertion position.
-          package_key.second = entry->id.value().package_id();
-          type_key.second = entry->id.value().type_id();
-          entry_key.second = entry->id.value();
-        }
+        ResourceTablePackageView new_package{
+            package->name, entry->id ? entry->id.value().package_id() : Maybe<uint8_t>{}};
+        auto view_package = package_inserter.Insert(view.packages, std::move(new_package));
 
-        auto package_it =
-            std::lower_bound(view.packages.begin(), view.packages.end(), package_key,
-                             less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>);
-        if (package_it == view.packages.end() || package_it->name != package_key.first ||
-            package_it->id != package_key.second) {
-          ResourceTablePackageView new_package{std::string(package_key.first), package_key.second};
-          package_it = view.packages.insert(package_it, new_package);
-        }
-
-        auto type_it = std::lower_bound(package_it->types.begin(), package_it->types.end(),
-                                        type_key, less_than_type_and_id<ResourceTableTypeView>);
-        if (type_it == package_it->types.end() || type_key.first != type_it->type ||
-            type_it->id != type_key.second) {
-          ResourceTableTypeView new_type{type_key.first, type_key.second};
-          type_it = package_it->types.insert(type_it, new_type);
-        }
+        ResourceTableTypeView new_type{type->type,
+                                       entry->id ? entry->id.value().type_id() : Maybe<uint8_t>{}};
+        auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
 
         if (entry->visibility.level == Visibility::Level::kPublic) {
           // Only mark the type visibility level as public, it doesn't care about being private.
-          type_it->visibility_level = Visibility::Level::kPublic;
+          view_type->visibility_level = Visibility::Level::kPublic;
         }
 
-        auto entry_it =
-            std::lower_bound(type_it->entries.begin(), type_it->entries.end(), entry_key,
-                             less_than_struct_with_name_and_id_pointer<ResourceEntry, ResourceId>);
-        type_it->entries.insert(entry_it, entry.get());
+        entry_inserter.Insert(view_type->entries, entry.get());
       }
     }
   }
 
+  // The android runtime does not support querying resources when the there are multiple type ids
+  // for the same resource type within the same package. For this reason, if there are types with
+  // multiple type ids, each type needs to exist in its own package in order to be queried by name.
+  std::vector<ResourceTablePackageView> new_packages;
+  for (auto& package : view.packages) {
+    // If a new package was already created for a different type within this package, then
+    // we can reuse those packages for other types that need to be extracted from this package.
+    // `start_index` is the index of the first newly created package that can be reused.
+    const size_t start_index = new_packages.size();
+    std::map<ResourceType, size_t> type_new_package_index;
+    for (auto type_it = package.types.begin(); type_it != package.types.end();) {
+      auto& type = *type_it;
+      auto type_index_iter = type_new_package_index.find(type.type);
+      if (type_index_iter == type_new_package_index.end()) {
+        // First occurrence of the resource type in this package. Keep it in this package.
+        type_new_package_index.insert(type_index_iter, std::make_pair(type.type, start_index));
+        ++type_it;
+        continue;
+      }
+
+      // The resource type has already been seen for this package, so this type must be extracted to
+      // a new separate package.
+      const size_t index = type_index_iter->second;
+      if (new_packages.size() == index) {
+        new_packages.emplace_back(ResourceTablePackageView{package.name, package.id});
+        type_new_package_index[type.type] = index + 1;
+      }
+
+      // Move the type into a new package
+      auto& other_package = new_packages[index];
+      type_inserter.Insert(other_package.types, std::move(type));
+      type_it = package.types.erase(type_it);
+    }
+  }
+
+  for (auto& new_package : new_packages) {
+    // Insert newly created packages after their original packages
+    auto [_, it] = package_inserter.LowerBound(view.packages, new_package);
+    view.packages.insert(++it, std::move(new_package));
+  }
+
   return view;
 }
 
@@ -424,6 +477,10 @@
       // This symbol definition takes precedence, replace.
       entry->visibility = res.visibility.value();
     }
+
+    if (res.visibility->staged_api) {
+      entry->visibility.staged_api = entry->visibility.staged_api;
+    }
   }
 
   if (res.overlayable.has_value()) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 49392a5..080ecc2 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -51,6 +51,11 @@
   Level level = Level::kUndefined;
   Source source;
   std::string comment;
+
+  // Indicates that the resource id may change across builds and that the public R.java identifier
+  // for this resource should not be final. This is set to `true` for resources in `staging-group`
+  // tags.
+  bool staged_api = false;
 };
 
 // Represents <add-resource> in an overlay.
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b1e1a77..4247ec5 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -132,6 +132,11 @@
 
   // The comment associated with the <public> tag.
   string comment = 3;
+
+  // Indicates that the resource id may change across builds and that the public R.java identifier
+  // for this resource should not be final. This is set to `true` for resources in `staging-group`
+  // tags.
+  bool staged_api = 4;
 }
 
 // Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 27cbe88..dfdac6b 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -25,6 +25,7 @@
 using testing::Eq;
 using testing::HasSubstr;
 using testing::Ne;
+using testing::NotNull;
 
 namespace aapt {
 
@@ -400,4 +401,127 @@
   EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
 }
 
+TEST_F(LinkTest, StagedAndroidApi) {
+  StdErrDiagnostics diag;
+  const std::string android_values =
+      R"(<resources>
+          <public type="attr" name="finalized_res" id="0x01010001"/>
+
+          <!-- S staged attributes (support staged resources in the same type id) -->
+          <staging-public-group type="attr" first-id="0x01010050">
+            <public name="staged_s_res" />
+          </staging-public-group>
+
+          <!-- SV2 staged attributes (support staged resources in a separate type id) -->
+          <staging-public-group type="attr" first-id="0x01ff0049">
+            <public name="staged_s2_res" />
+          </staging-public-group>
+
+          <!-- T staged attributes (support staged resources in multiple separate type ids) -->
+          <staging-public-group type="attr" first-id="0x01fe0063">
+            <public name="staged_t_res" />
+          </staging-public-group>
+
+          <staging-public-group type="string" first-id="0x01fd0072">
+            <public name="staged_t_string" />
+          </staging-public-group>
+
+          <attr name="finalized_res" />
+          <attr name="staged_s_res" />
+          <attr name="staged_s2_res" />
+          <attr name="staged_t_res" />
+          <string name="staged_t_string">Hello</string>
+         </resources>)";
+
+  const std::string app_values =
+      R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
+           <attr name="bar" />
+           <declare-styleable name="ClientStyleable">
+             <attr name="android:finalized_res" />
+             <attr name="android:staged_s_res" />
+             <attr name="bar" />
+           </declare-styleable>
+         </resources>)";
+
+  const std::string android_res = GetTestPath("android-res");
+  ASSERT_TRUE(
+      CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
+
+  const std::string android_apk = GetTestPath("android.apk");
+  const std::string android_java = GetTestPath("android_java");
+  // clang-format off
+  auto android_manifest = ManifestBuilder(this)
+      .SetPackageName("android")
+      .Build();
+
+  auto android_link_args = LinkCommandBuilder(this)
+      .SetManifestFile(android_manifest)
+      .AddParameter("--private-symbols", "com.android.internal")
+      .AddParameter("--java", android_java)
+      .AddCompiledResDir(android_res, &diag)
+      .Build(android_apk);
+  // clang-format on
+  ASSERT_TRUE(Link(android_link_args, &diag));
+
+  const std::string android_r_java = android_java + "/android/R.java";
+  std::string android_r_contents;
+  ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
+  EXPECT_THAT(android_r_contents, HasSubstr(" public static final int finalized_res=0x01010001;"));
+  EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s_res=0x01010050;"));
+  EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_s2_res=0x01ff0049;"));
+  EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_res=0x01fe0063;"));
+  EXPECT_THAT(android_r_contents, HasSubstr(" public static int staged_t_string=0x01fd0072;"));
+
+  // Build an app that uses the framework attribute in a declare-styleable
+  const std::string client_res = GetTestPath("app-res");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
+
+  const std::string app_apk = GetTestPath("app.apk");
+  const std::string app_java = GetTestPath("app_java");
+  // clang-format off
+  auto app_manifest = ManifestBuilder(this)
+      .SetPackageName("com.example.app")
+      .Build();
+
+  auto app_link_args = LinkCommandBuilder(this)
+      .SetManifestFile(app_manifest)
+      .AddParameter("--java", app_java)
+      .AddParameter("-I", android_apk)
+      .AddCompiledResDir(client_res, &diag)
+      .Build(app_apk);
+  // clang-format on
+  ASSERT_TRUE(Link(app_link_args, &diag));
+
+  const std::string client_r_java = app_java + "/com/example/app/R.java";
+  std::string client_r_contents;
+  ASSERT_TRUE(android::base::ReadFileToString(client_r_java, &client_r_contents));
+  EXPECT_THAT(client_r_contents, HasSubstr(" 0x01010001, android.R.attr.staged_s_res, 0x7f010000"));
+
+  // Test that the resource ids of staged and non-staged resource can be retrieved
+  android::AssetManager2 am;
+  auto android_asset = android::ApkAssets::Load(android_apk);
+  ASSERT_THAT(android_asset, NotNull());
+  ASSERT_TRUE(am.SetApkAssets({android_asset.get()}));
+
+  auto result = am.GetResourceId("android:attr/finalized_res");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_THAT(*result, Eq(0x01010001));
+
+  result = am.GetResourceId("android:attr/staged_s_res");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_THAT(*result, Eq(0x01010050));
+
+  result = am.GetResourceId("android:attr/staged_s2_res");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_THAT(*result, Eq(0x01ff0049));
+
+  result = am.GetResourceId("android:attr/staged_t_res");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_THAT(*result, Eq(0x01fe0063));
+
+  result = am.GetResourceId("android:string/staged_t_string");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_THAT(*result, Eq(0x01fd0072));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 07db73d..9a50b26 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -77,6 +77,27 @@
   NextIdFinder<uint16_t, ResourceName> next_entry_id_;
 };
 
+struct ResourceTypeKey {
+  ResourceType type;
+  uint8_t id;
+
+  bool operator<(const ResourceTypeKey& other) const {
+    return (type != other.type) ? type < other.type : id < other.id;
+  }
+
+  bool operator==(const ResourceTypeKey& other) const {
+    return type == other.type && id == other.id;
+  }
+
+  bool operator!=(const ResourceTypeKey& other) const {
+    return !(*this == other);
+  }
+};
+
+::std::ostream& operator<<(::std::ostream& out, const ResourceTypeKey& type) {
+  return out << type.type;
+}
+
 struct IdAssignerContext {
   IdAssignerContext(std::string package_name, uint8_t package_id)
       : package_name_(std::move(package_name)), package_id_(package_id) {
@@ -85,7 +106,8 @@
   // Attempts to reserve the resource id for the specified resource name.
   // Returns whether the id was reserved successfully.
   // Reserving identifiers must be completed before `NextId` is called for the first time.
-  bool ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag);
+  bool ReserveId(const ResourceName& name, ResourceId id, const Visibility& visibility,
+                 IDiagnostics* diag);
 
   // Retrieves the next available resource id that has not been reserved.
   std::optional<ResourceId> NextId(const ResourceName& name, IDiagnostics* diag);
@@ -93,8 +115,10 @@
  private:
   std::string package_name_;
   uint8_t package_id_;
-  std::map<ResourceType, TypeGroup> types_;
-  NextIdFinder<uint8_t, ResourceType> type_id_finder_ = NextIdFinder<uint8_t, ResourceType>(1);
+  std::map<ResourceTypeKey, TypeGroup> types_;
+  std::map<ResourceType, uint8_t> non_staged_type_ids_;
+  NextIdFinder<uint8_t, ResourceTypeKey> type_id_finder_ =
+      NextIdFinder<uint8_t, ResourceTypeKey>(1);
 };
 
 }  // namespace
@@ -106,7 +130,8 @@
       for (auto& entry : type->entries) {
         const ResourceName name(package->name, type->type, entry->name);
         if (entry->id) {
-          if (!assigned_ids.ReserveId(name, entry->id.value(), context->GetDiagnostics())) {
+          if (!assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
+                                      context->GetDiagnostics())) {
             return false;
           }
         }
@@ -116,7 +141,8 @@
           const auto iter = assigned_id_map_->find(name);
           if (iter != assigned_id_map_->end()) {
             const ResourceId assigned_id = iter->second;
-            if (!assigned_ids.ReserveId(name, assigned_id, context->GetDiagnostics())) {
+            if (!assigned_ids.ReserveId(name, assigned_id, entry->visibility,
+                                        context->GetDiagnostics())) {
               return false;
             }
             entry->id = assigned_id;
@@ -132,7 +158,8 @@
     for (const auto& stable_id_entry : *assigned_id_map_) {
       const ResourceName& pre_assigned_name = stable_id_entry.first;
       const ResourceId& pre_assigned_id = stable_id_entry.second;
-      if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, context->GetDiagnostics())) {
+      if (!assigned_ids.ReserveId(pre_assigned_name, pre_assigned_id, {},
+                                  context->GetDiagnostics())) {
         return false;
       }
     }
@@ -165,7 +192,7 @@
   auto assign_result = pre_assigned_ids_.emplace(id, key);
   if (!assign_result.second && assign_result.first->second != key) {
     std::stringstream error;
-    error << "ID " << id << " is already assigned to " << assign_result.first->second;
+    error << "ID is already assigned to " << assign_result.first->second;
     return unexpected(error.str());
   }
   return id;
@@ -210,7 +237,7 @@
   if (type_id_ != id.type_id()) {
     // Currently there cannot be multiple type ids for a single type.
     std::stringstream error;
-    error << "type '" << name.type << "' already has ID " << id.type_id();
+    error << "type '" << name.type << "' already has ID " << std::hex << (int)id.type_id();
     return unexpected(error.str());
   }
 
@@ -234,24 +261,38 @@
   return ResourceId(package_id_, type_id_, entry_id.value());
 }
 
-bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id, IDiagnostics* diag) {
+bool IdAssignerContext::ReserveId(const ResourceName& name, ResourceId id,
+                                  const Visibility& visibility, IDiagnostics* diag) {
   if (package_id_ != id.package_id()) {
     diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
-                              << " because package already has ID " << id.package_id());
+                              << " because package already has ID " << std::hex
+                              << (int)id.package_id());
     return false;
   }
 
-  auto type = types_.find(name.type);
+  auto key = ResourceTypeKey{name.type, id.type_id()};
+  auto type = types_.find(key);
   if (type == types_.end()) {
     // The type has not been assigned an id yet. Ensure that the specified id is not being used by
     // another type.
-    auto assign_result = type_id_finder_.ReserveId(name.type, id.type_id());
+    auto assign_result = type_id_finder_.ReserveId(key, id.type_id());
     if (!assign_result.has_value()) {
       diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
                                 << " because type " << assign_result.error());
       return false;
     }
-    type = types_.emplace(name.type, TypeGroup(package_id_, id.type_id())).first;
+    type = types_.emplace(key, TypeGroup(package_id_, id.type_id())).first;
+  }
+
+  if (!visibility.staged_api) {
+    // Ensure that non-staged resources can only exist in one type ID.
+    auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+    if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
+      diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
+                                << " because type already has ID " << std::hex
+                                << (int)id.type_id());
+      return false;
+    }
   }
 
   auto assign_result = type->second.ReserveId(name, id);
@@ -268,11 +309,19 @@
   // The package name is not known during the compile stage.
   // Resources without a package name are considered a part of the app being linked.
   CHECK(name.package.empty() || name.package == package_name_);
-  auto type = types_.find(name.type);
-  if (type == types_.end()) {
+
+  // Find the type id for non-staged resources of this type.
+  auto non_staged_type = non_staged_type_ids_.find(name.type);
+  if (non_staged_type == non_staged_type_ids_.end()) {
     auto next_type_id = type_id_finder_.NextId();
     CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
-    type = types_.emplace(name.type, TypeGroup(package_id_, next_type_id.value())).first;
+    non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+  }
+
+  ResourceTypeKey key{name.type, non_staged_type->second};
+  auto type = types_.find(key);
+  if (type == types_.end()) {
+    type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
   }
 
   auto assign_result = type->second.NextId();
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 0065a22..6637766 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -98,6 +98,37 @@
   ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
 }
 
+TEST_F(IdAssignerTests, FailWhenNonUniqueTypeIdsAssigned) {
+  auto table = test::ResourceTableBuilder()
+                   .AddSimple("android:string/foo", ResourceId(0x01040000))
+                   .AddSimple("android:attr/bar", ResourceId(0x01040006))
+                   .Build();
+  IdAssigner assigner;
+  ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIds) {
+  auto table = test::ResourceTableBuilder()
+                   .AddSimple("android:attr/foo", ResourceId(0x01050000))
+                   .AddSimple("android:attr/bar", ResourceId(0x01040006))
+                   .Build();
+  IdAssigner assigner;
+  ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId) {
+  auto table = test::ResourceTableBuilder()
+                   .AddSimple("android:attr/foo", ResourceId(0x01050000))
+                   .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
+                   .Add(NewResourceBuilder("android:attr/staged_baz")
+                            .SetId(0x01ff0000)
+                            .SetVisibility({.staged_api = true})
+                            .Build())
+                   .Build();
+  IdAssigner assigner;
+  ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
 TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
   auto table = test::ResourceTableBuilder()
                    .AddSimple("android:attr/foo")
@@ -154,52 +185,24 @@
 }
 
 ::testing::AssertionResult VerifyIds(ResourceTable* table) {
-  std::set<uint8_t> package_ids;
-  auto table_view = table->GetPartitionedView();
-  for (auto& package : table_view.packages) {
-    if (!package.id) {
-      return ::testing::AssertionFailure() << "package " << package.name << " has no ID";
-    }
-
-    if (!package_ids.insert(package.id.value()).second) {
-      return ::testing::AssertionFailure() << "package " << package.name << " has non-unique ID "
-                                           << std::hex << (int)package.id.value() << std::dec;
-    }
-  }
-
-  for (auto& package : table_view.packages) {
-    std::set<uint8_t> type_ids;
-    for (auto& type : package.types) {
-      if (!type.id) {
-        return ::testing::AssertionFailure()
-               << "type " << type.type << " of package " << package.name << " has no ID";
-      }
-
-      if (!type_ids.insert(type.id.value()).second) {
-        return ::testing::AssertionFailure()
-               << "type " << type.type << " of package " << package.name << " has non-unique ID "
-               << std::hex << (int)type.id.value() << std::dec;
-      }
-    }
-
-    for (auto& type : package.types) {
-      std::set<ResourceId> entry_ids;
-      for (auto& entry : type.entries) {
+  std::set<ResourceId> seen_ids;
+  for (auto& package : table->packages) {
+    for (auto& type : package->types) {
+      for (auto& entry : type->entries) {
         if (!entry->id) {
           return ::testing::AssertionFailure()
-                 << "entry " << entry->name << " of type " << type.type << " of package "
-                 << package.name << " has no ID";
+                 << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+                 << " has no ID";
         }
-
-        if (!entry_ids.insert(entry->id.value()).second) {
+        if (!seen_ids.insert(entry->id.value()).second) {
           return ::testing::AssertionFailure()
-                 << "entry " << entry->name << " of type " << type.type << " of package "
-                 << package.name << " has non-unique ID " << std::hex << entry->id.value()
-                 << std::dec;
+                 << "resource " << ResourceNameRef(package->name, type->type, entry->name)
+                 << " has a non-unique ID" << std::hex << entry->id.value() << std::dec;
         }
       }
     }
   }
+
   return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
 }
 
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 5fea897..17d11a6 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -546,8 +546,12 @@
       const uint16_t entry_id = entry->id.value().entry_id();
 
       // Populate the config masks for this entry.
+      uint32_t& entry_config_masks = config_masks[entry_id];
       if (entry->visibility.level == Visibility::Level::kPublic) {
-        config_masks[entry_id] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+        entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+      }
+      if (entry->visibility.staged_api) {
+        entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_STAGED_API);
       }
 
       const size_t config_count = entry->values.size();
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index bfb92da..498d5a2 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -456,6 +456,7 @@
           DeserializeSourceFromPb(pb_visibility.source(), src_pool, &entry->visibility.source);
         }
         entry->visibility.comment = pb_visibility.comment();
+        entry->visibility.staged_api = pb_visibility.staged_api();
 
         const Visibility::Level level = DeserializeVisibilityFromPb(pb_visibility.level());
         entry->visibility.level = level;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 9842e25..f13f82d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -378,6 +378,7 @@
 
         // Write the Visibility struct.
         pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
+        pb_visibility->set_staged_api(entry->visibility.staged_api);
         pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
         if (source_pool != nullptr) {
           SerializeSourceToPb(entry->visibility.source, source_pool.get(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index ad5ed4d..591ba149 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -50,6 +50,53 @@
   return (result) ? result.value().entry : nullptr;
 }
 
+TEST(ProtoSerializeTest, SerializeVisibility) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .Add(NewResourceBuilder("com.app.a:bool/foo")
+                   .SetVisibility({Visibility::Level::kUndefined})
+                   .Build())
+          .Add(NewResourceBuilder("com.app.a:bool/bar")
+                   .SetVisibility({Visibility::Level::kPrivate})
+                   .Build())
+          .Add(NewResourceBuilder("com.app.a:bool/baz")
+                   .SetVisibility({Visibility::Level::kPublic})
+                   .Build())
+          .Add(NewResourceBuilder("com.app.a:bool/fiz")
+                   .SetVisibility({.level = Visibility::Level::kPublic, .staged_api = true})
+                   .Build())
+          .Build();
+
+  ResourceTable new_table;
+  pb::ResourceTable pb_table;
+  MockFileCollection files;
+  std::string error;
+  SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  EXPECT_THAT(error, IsEmpty());
+
+  auto search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+  ASSERT_TRUE(search_result);
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+  ASSERT_TRUE(search_result);
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPrivate));
+  EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+  ASSERT_TRUE(search_result);
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+  EXPECT_FALSE(search_result.value().entry->visibility.staged_api);
+
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+  ASSERT_TRUE(search_result);
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
+  EXPECT_TRUE(search_result.value().entry->visibility.staged_api);
+}
+
 TEST(ProtoSerializeTest, SerializeSinglePackage) {
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   std::unique_ptr<ResourceTable> table =
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 995495a..d3648c8 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -59,8 +59,9 @@
 template <typename T>
 class PrimitiveMember : public ClassMember {
  public:
-  PrimitiveMember(const android::StringPiece& name, const T& val)
-      : name_(name.to_string()), val_(val) {}
+  PrimitiveMember(const android::StringPiece& name, const T& val, bool staged_api = false)
+      : name_(name.to_string()), val_(val), staged_api_(staged_api) {
+  }
 
   bool empty() const override {
     return false;
@@ -77,7 +78,7 @@
     ClassMember::Print(final, printer, strip_api_annotations);
 
     printer->Print("public static ");
-    if (final) {
+    if (final && !staged_api_) {
       printer->Print("final ");
     }
     printer->Print("int ").Print(name_).Print("=").Print(to_string(val_)).Print(";");
@@ -88,14 +89,16 @@
 
   std::string name_;
   T val_;
+  bool staged_api_;
 };
 
 // Specialization for strings so they get the right type and are quoted with "".
 template <>
 class PrimitiveMember<std::string> : public ClassMember {
  public:
-  PrimitiveMember(const android::StringPiece& name, const std::string& val)
-      : name_(name.to_string()), val_(val) {}
+  PrimitiveMember(const android::StringPiece& name, const std::string& val, bool staged_api = false)
+      : name_(name.to_string()), val_(val) {
+  }
 
   bool empty() const override {
     return false;
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 039448e..e1e2e01 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -460,7 +460,8 @@
 
   const std::string field_name = TransformToFieldName(name.entry);
   if (out_class_def != nullptr) {
-    auto resource_member = util::make_unique<ResourceMember>(field_name, real_id);
+    auto resource_member =
+        util::make_unique<ResourceMember>(field_name, real_id, entry.visibility.staged_api);
 
     // Build the comments and annotations for this entry.
     AnnotationProcessor* processor = resource_member->GetCommentBuilder();
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index de72334..d385267 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -199,7 +199,8 @@
 
   if (sr.entry->id) {
     symbol->id = sr.entry->id.value();
-    symbol->is_dynamic = (sr.entry->id.value().package_id() == 0);
+    symbol->is_dynamic =
+        (sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
   }
 
   if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
@@ -374,7 +375,8 @@
 
   if (s) {
     s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
-    s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
+    s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package) ||
+                    (type_spec_flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
     return s;
   }
   return {};
@@ -421,7 +423,8 @@
 
   if (s) {
     s->is_public = (*flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
-    s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
+    s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package) ||
+                    (*flags & android::ResTable_typeSpec::SPEC_STAGED_API) != 0;
     return s;
   }
   return {};