Merge cherrypicks of [15831545, 15831526, 15831634, 15831684, 15831293, 15831546, 15831847, 15831808, 15831638, 15831639, 15831558, 15831793, 15831794, 15831795, 15831796, 15831797, 15831798, 15831799, 15831800, 15831801, 15831802, 15831803, 15831804, 15831640, 15831849, 15831810, 15831811, 15831850, 15831641, 15831805, 15831812, 15831642, 15831813, 15831851, 15831852, 15831887, 15831888, 15831559, 15831814, 15831815, 15831853, 15831854, 15831855, 15831315] into sc-release

Change-Id: I6b2fbc1d2e4c5f02209d5d3e86b453369df2132f
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 0d6a079..e6cdcc0 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -31,6 +31,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -86,6 +87,12 @@
         if (TextUtils.isEmpty(type)) {
             throw new IllegalArgumentException("the type must not be empty: " + type);
         }
+        if (name.length() > 200) {
+            throw new IllegalArgumentException("account name is longer than 200 characters");
+        }
+        if (type.length() > 200) {
+            throw new IllegalArgumentException("account type is longer than 200 characters");
+        }
         this.name = name;
         this.type = type;
         this.accessId = accessId;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0c64c86..de0fef0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -464,11 +464,7 @@
 
         @Override
         public int hashCode() {
-            return hashCode(authority, userId);
-        }
-
-        public static int hashCode(final String auth, final int userIdent) {
-            return ((auth != null) ? auth.hashCode() : 0) ^ userIdent;
+            return ((authority != null) ? authority.hashCode() : 0) ^ userId;
         }
     }
 
@@ -490,7 +486,7 @@
     // Note we never removes items from this map but that's okay because there are only so many
     // users and so many authorities.
     @GuardedBy("mGetProviderKeys")
-    final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>();
+    final ArrayMap<ProviderKey, ProviderKey> mGetProviderKeys = new ArrayMap<>();
 
     final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
         = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
@@ -7015,11 +7011,11 @@
     }
 
     private ProviderKey getGetProviderKey(String auth, int userId) {
-        final int key = ProviderKey.hashCode(auth, userId);
+        final ProviderKey key = new ProviderKey(auth, userId);
         synchronized (mGetProviderKeys) {
             ProviderKey lock = mGetProviderKeys.get(key);
             if (lock == null) {
-                lock = new ProviderKey(auth, userId);
+                lock = key;
                 mGetProviderKeys.put(key, lock);
             }
             return lock;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index bbb550f..5a5fac4 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1329,7 +1329,10 @@
             if (alias == null) {
                 return getName();
             }
-            return alias;
+            return alias
+                    .replace('\t', ' ')
+                    .replace('\n', ' ')
+                    .replace('\r', ' ');
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 86ed50b..72ee00f 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -102,6 +102,13 @@
         mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
     };
 
+    /** @hide */
+    public int getPlayerIId() {
+        synchronized (mLock) {
+            return mPlayerIId;
+        }
+    }
+
     /**
      * Call from derived class when instantiation / initialization is successful
      */
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index c1a0a9a..b4cafd8 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -93,9 +93,9 @@
             final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
             setTitle(Html.fromHtml(getString(
                     R.string.confirmation_title,
-                    getCallingAppName(),
-                    profileName,
-                    selectedDevice.getDisplayName()), 0));
+                    Html.escapeHtml(getCallingAppName()),
+                    Html.escapeHtml(selectedDevice.getDisplayName())), 0));
+
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
             getService().mSelectedDevice = selectedDevice;
@@ -108,8 +108,8 @@
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setVisibility(View.GONE);
             setTitle(Html.fromHtml(getString(R.string.chooser_title,
-                    profileName,
-                    getCallingAppName()), 0));
+                    Html.escapeHtml(profileName),
+                    Html.escapeHtml(getCallingAppName())), 0));
             mDeviceListView = findViewById(R.id.device_list);
             mDevicesAdapter = new DevicesAdapter();
             mDeviceListView.setAdapter(mDevicesAdapter);
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 89786ee..a617850 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -139,7 +139,7 @@
                                 + " with ducking", e);
                     }
                     player.start();
-                    if (DEBUG) { Log.d(mTag, "player.start"); }
+                    if (DEBUG) { Log.d(mTag, "player.start piid:" + player.getPlayerIId()); }
                 } catch (Exception e) {
                     if (player != null) {
                         player.release();
@@ -155,7 +155,13 @@
                     mPlayer = player;
                 }
                 if (mp != null) {
-                    if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
+                    if (DEBUG) {
+                        Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId());
+                    }
+                    mp.pause();
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException ie) { }
                     mp.release();
                 }
                 this.notify();
@@ -244,6 +250,10 @@
                         try {
                             mp.stop();
                         } catch (Exception e) { }
+                        if (DEBUG) {
+                            Log.i(mTag, "About to release MediaPlayer piid:"
+                                    + mp.getPlayerIId() + " due to notif cancelled");
+                        }
                         mp.release();
                         synchronized(mQueueAudioFocusLock) {
                             if (mAudioManagerWithAudioFocus != null) {
@@ -284,7 +294,7 @@
     public void onCompletion(MediaPlayer mp) {
         synchronized(mQueueAudioFocusLock) {
             if (mAudioManagerWithAudioFocus != null) {
-                if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus");
+                if (DEBUG) Log.d(mTag, "onCompletion() abandoning AudioFocus");
                 mAudioManagerWithAudioFocus.abandonAudioFocus(null);
                 mAudioManagerWithAudioFocus = null;
             } else {
@@ -310,6 +320,10 @@
             }
         }
         if (mp != null) {
+            if (DEBUG) {
+                Log.i("NotificationPlayer", "About to release MediaPlayer piid:"
+                        + mp.getPlayerIId() + " due to onCompletion");
+            }
             mp.release();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 541ee2c..4a75810 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -51,6 +51,7 @@
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -95,6 +96,7 @@
     private final UiEventLogger mUiEventLogger;
     private final InstanceIdSequence mInstanceIdSequence;
     private final CustomTileStatePersister mCustomTileStatePersister;
+    private final FeatureFlags mFeatureFlags;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
     private AutoTileManager mAutoTiles;
@@ -122,7 +124,8 @@
             UiEventLogger uiEventLogger,
             UserTracker userTracker,
             SecureSettings secureSettings,
-            CustomTileStatePersister customTileStatePersister
+            CustomTileStatePersister customTileStatePersister,
+            FeatureFlags featureFlags
     ) {
         mIconController = iconController;
         mContext = context;
@@ -144,6 +147,7 @@
         mUserTracker = userTracker;
         mSecureSettings = secureSettings;
         mCustomTileStatePersister = customTileStatePersister;
+        mFeatureFlags = featureFlags;
 
         mainHandler.post(() -> {
             // This is technically a hack to avoid circular dependency of
@@ -265,7 +269,7 @@
         if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
             newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
         }
-        final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
+        final List<String> tileSpecs = loadTileSpecs(mContext, newValue, mFeatureFlags);
         int currentUser = mUserTracker.getUserId();
         if (currentUser != mCurrentUser) {
             mUserContext = mUserTracker.getUserContext();
@@ -334,7 +338,7 @@
         if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
             // If we didn't manage to create any tiles, set it to empty (default)
             Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
-            changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
+            changeTiles(currentSpecs, loadTileSpecs(mContext, "", mFeatureFlags));
         } else {
             for (int i = 0; i < mCallbacks.size(); i++) {
                 mCallbacks.get(i).onTilesChanged();
@@ -389,7 +393,7 @@
 
     private void changeTileSpecs(Predicate<List<String>> changeFunction) {
         final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
-        final List<String> tileSpecs = loadTileSpecs(mContext, setting);
+        final List<String> tileSpecs = loadTileSpecs(mContext, setting, mFeatureFlags);
         if (changeFunction.test(tileSpecs)) {
             saveTilesToSettings(tileSpecs);
         }
@@ -478,7 +482,8 @@
         throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
     }
 
-    protected static List<String> loadTileSpecs(Context context, String tileList) {
+    protected static List<String> loadTileSpecs(
+            Context context, String tileList, FeatureFlags featureFlags) {
         final Resources res = context.getResources();
 
         if (TextUtils.isEmpty(tileList)) {
@@ -511,6 +516,21 @@
                 }
             }
         }
+        if (featureFlags.isProviderModelSettingEnabled()) {
+            if (!tiles.contains("internet")) {
+                if (tiles.contains("wifi")) {
+                    // Replace the WiFi with Internet, and remove the Cell
+                    tiles.set(tiles.indexOf("wifi"), "internet");
+                    tiles.remove("cell");
+                } else if (tiles.contains("cell")) {
+                    // Replace the Cell with Internet
+                    tiles.set(tiles.indexOf("cell"), "internet");
+                }
+            } else {
+                tiles.remove("wifi");
+                tiles.remove("cell");
+            }
+        }
         return tiles;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 3c2f35b..f2832b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -41,6 +41,7 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.leak.GarbageMonitor;
 
 import java.util.ArrayList;
@@ -62,6 +63,7 @@
     private final Executor mBgExecutor;
     private final Context mContext;
     private final UserTracker mUserTracker;
+    private final FeatureFlags mFeatureFlags;
     private TileStateListener mListener;
 
     private boolean mFinished;
@@ -71,12 +73,14 @@
             Context context,
             UserTracker userTracker,
             @Main Executor mainExecutor,
-            @Background Executor bgExecutor
+            @Background Executor bgExecutor,
+            FeatureFlags featureFlags
     ) {
         mContext = context;
         mMainExecutor = mainExecutor;
         mBgExecutor = bgExecutor;
         mUserTracker = userTracker;
+        mFeatureFlags = featureFlags;
     }
 
     public void setListener(TileStateListener listener) {
@@ -117,6 +121,10 @@
         }
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
+        if (mFeatureFlags.isProviderModelSettingEnabled()) {
+            possibleTiles.remove("cell");
+            possibleTiles.remove("wifi");
+        }
 
         for (String spec : possibleTiles) {
             // Only add current and stock tiles that can be created from QSFactoryImpl.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 86c90c7..9eb95c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import android.annotation.ColorInt;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -28,15 +27,12 @@
 import com.android.systemui.statusbar.notification.stack.ViewState;
 
 public class FooterView extends StackScrollerDecorView {
-    private final int mClearAllTopPadding;
     private FooterViewButton mDismissButton;
     private FooterViewButton mManageButton;
     private boolean mShowHistory;
 
     public FooterView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mClearAllTopPadding = context.getResources().getDimensionPixelSize(
-                R.dimen.clear_all_padding_top);
     }
 
     @Override
@@ -55,11 +51,6 @@
         mManageButton = findViewById(R.id.manage_text);
     }
 
-    public void setTextColor(@ColorInt int color) {
-        mManageButton.setTextColor(color);
-        mDismissButton.setTextColor(color);
-    }
-
     public void setManageButtonClickListener(OnClickListener listener) {
         mManageButton.setOnClickListener(listener);
     }
@@ -95,21 +86,25 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        int textColor = getResources().getColor(R.color.notif_pill_text);
-        Resources.Theme theme = getContext().getTheme();
-        mDismissButton.setBackground(
-                getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
-        mDismissButton.setTextColor(textColor);
-        mManageButton.setBackground(
-                getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
-        mManageButton = findViewById(R.id.manage_text);
+        updateColors();
         mDismissButton.setText(R.string.clear_all_notifications_text);
-        mManageButton.setTextColor(textColor);
         mDismissButton.setContentDescription(
                 mContext.getString(R.string.accessibility_clear_all));
         showHistory(mShowHistory);
     }
 
+    /**
+     * Update the text and background colors for the current color palette and night mode setting.
+     */
+    public void updateColors() {
+        Resources.Theme theme = mContext.getTheme();
+        int textColor = getResources().getColor(R.color.notif_pill_text, theme);
+        mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        mDismissButton.setTextColor(textColor);
+        mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        mManageButton.setTextColor(textColor);
+    }
+
     @Override
     public ExpandableViewState createExpandableViewState() {
         return new FooterViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 289c32f..0660daa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4231,7 +4231,7 @@
         final @ColorInt int textColor =
                 Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
         mSectionsManager.setHeaderForegroundColor(textColor);
-        mFooterView.setTextColor(textColor);
+        mFooterView.updateColors();
         mEmptyShadeView.setTextColor(textColor);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 3ee3e55..7f89b26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -139,7 +139,7 @@
                 () -> mock(AutoTileManager.class), mock(DumpManager.class),
                 mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
                 mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
-                mock(SecureSettings.class), mock(CustomTileStatePersister.class));
+                mock(SecureSettings.class), mock(CustomTileStatePersister.class), mFeatureFlags);
         qs.setHost(host);
 
         qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 9e97f80..4cbad5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -63,6 +63,7 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -124,6 +125,8 @@
     private SecureSettings mSecureSettings;
     @Mock
     private CustomTileStatePersister mCustomTileStatePersister;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
     private Handler mHandler;
     private TestableLooper mLooper;
@@ -137,9 +140,9 @@
         mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
                 mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
                 mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
-                mSecureSettings, mCustomTileStatePersister);
+                mSecureSettings, mCustomTileStatePersister, mFeatureFlags);
         setUpTileFactory();
-
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
         when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
                 .thenReturn("");
     }
@@ -169,13 +172,13 @@
 
     @Test
     public void testLoadTileSpecs_emptySetting() {
-        List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
+        List<String> tiles = QSTileHost.loadTileSpecs(mContext, "", mFeatureFlags);
         assertFalse(tiles.isEmpty());
     }
 
     @Test
     public void testLoadTileSpecs_nullSetting() {
-        List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
+        List<String> tiles = QSTileHost.loadTileSpecs(mContext, null, mFeatureFlags);
         assertFalse(tiles.isEmpty());
     }
 
@@ -189,6 +192,55 @@
     }
 
     @Test
+    public void testRemoveWifiAndCellularWithoutInternet() {
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
+
+        assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
+    public void testRemoveWifiAndCellularWithInternet() {
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
+
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
+    public void testRemoveWifiWithoutInternet() {
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
+
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
+    public void testRemoveCellWithInternet() {
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
+
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
+    public void testNoWifiNoCellularNoInternet() {
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
+
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+    }
+
+    @Test
     public void testSpecWithInvalidDoesNotUseDefault() {
         mContext.getOrCreateTestableResources()
                 .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
@@ -321,7 +373,7 @@
 
     @Test
     public void testLoadTileSpec_repeated() {
-        List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
+        List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mFeatureFlags);
 
         assertEquals(2, specs.size());
         assertEquals("spec1", specs.get(0));
@@ -332,7 +384,7 @@
     public void testLoadTileSpec_repeatedInDefault() {
         mContext.getOrCreateTestableResources()
                 .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
-        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
+        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default", mFeatureFlags);
 
         // Remove spurious tiles, like dbg:mem
         specs.removeIf(spec -> !"spec1".equals(spec));
@@ -343,7 +395,7 @@
     public void testLoadTileSpec_repeatedDefaultAndSetting() {
         mContext.getOrCreateTestableResources()
                 .addOverride(R.string.quick_settings_tiles_default, "spec1");
-        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
+        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mFeatureFlags);
 
         // Remove spurious tiles, like dbg:mem
         specs.removeIf(spec -> !"spec1".equals(spec));
@@ -371,11 +423,12 @@
                 Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
                 BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
                 UiEventLogger uiEventLogger, UserTracker userTracker,
-                SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
+                SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
+                FeatureFlags featureFlags) {
             super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
                     tunerService, autoTiles, dumpManager, broadcastDispatcher,
                     Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
-                    customTileStatePersister);
+                    customTileStatePersister, featureFlags);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 4efcc5c..c5b6709 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -58,6 +58,7 @@
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -108,6 +109,8 @@
     private PackageManager mPackageManager;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private FeatureFlags mFeatureFlags;
     @Captor
     private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
 
@@ -133,12 +136,12 @@
                     }
                 }
         ).when(mQSTileHost).createTile(anyString());
-
+        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
         FakeSystemClock clock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(clock);
         mBgExecutor = new FakeExecutor(clock);
         mTileQueryHelper = new TileQueryHelper(
-                mContext, mUserTracker, mMainExecutor, mBgExecutor);
+                mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags);
         mTileQueryHelper.setListener(mListener);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 2b18404..01fa222 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -98,6 +99,8 @@
     private UserTracker mUserTracker;
     @Mock
     private SecureSettings  mSecureSettings;
+    @Mock
+    private FeatureFlags mFeatureFlags;
 
     @Before
     public void setUp() throws Exception {
@@ -119,7 +122,8 @@
                 mUiEventLogger,
                 mUserTracker,
                 mSecureSettings,
-                mock(CustomTileStatePersister.class));
+                mock(CustomTileStatePersister.class),
+                mFeatureFlags);
         mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
                 mUserTracker);
     }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a6a8cf01..400b084 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1833,6 +1833,11 @@
                                 + ", skipping since the account already exists");
                         return false;
                     }
+                    if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
+                        Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+                                + ", skipping since more than 50 accounts on device exist");
+                        return false;
+                    }
                     long accountId = accounts.accountsDb.insertCeAccount(account, password);
                     if (accountId < 0) {
                         Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index dc95533..3e52f5e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2582,14 +2582,12 @@
         }
 
         if (mCurToken != null) {
-            try {
-                if (DEBUG) {
-                    Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
-                            + mCurTokenDisplayId);
-                }
-                mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId);
-            } catch (RemoteException e) {
+            if (DEBUG) {
+                Slog.v(TAG, "Removing window token: " + mCurToken + " for display: "
+                        + mCurTokenDisplayId);
             }
+            mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */,
+                    false /* animateExit */, mCurTokenDisplayId);
             // Set IME window status as invisible when unbind current method.
             mImeWindowVis = 0;
             mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3019439..5643873 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -215,7 +215,7 @@
     private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
             new DefaultCrossProfileIntentFilter.Builder(
                     DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
-                    /* flags= */0,
+                    /* flags= */ ONLY_IF_NO_MATCH_FOUND,
                     /* letsPersonalDataIntoProfile= */ false)
                     .addAction(ACTION_RECOGNIZE_SPEECH)
                     .addCategory(Intent.CATEGORY_DEFAULT)
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index cda4806..94e551a 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -480,9 +480,10 @@
             r.append("DUP:");
             r.append(permissionInfo.name);
         }
-        if (permission.isRuntime() && (ownerChanged || wasNonRuntime)) {
-            // If this is a runtime permission and the owner has changed, or this wasn't a runtime
-            // permission, then permission state should be cleaned up
+        if ((permission.isInternal() && ownerChanged)
+                || (permission.isRuntime() && (ownerChanged || wasNonRuntime))) {
+            // If this is an internal/runtime permission and the owner has changed, or this wasn't a
+            // runtime permission, then permission state should be cleaned up.
             permission.mDefinitionChanged = true;
         }
         if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1133faa..7b12709 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1643,7 +1643,8 @@
             isRolePermission = permission.isRole();
         }
         final boolean mayRevokeRolePermission = isRolePermission
-                && mayManageRolePermission(callingUid);
+                // Allow ourselves to revoke role permissions due to definition changes.
+                && (callingUid == Process.myUid() || mayManageRolePermission(callingUid));
 
         final boolean isRuntimePermission;
         synchronized (mLock) {
@@ -2321,11 +2322,13 @@
 
         for (int permNum = 0; permNum < numPermissions; permNum++) {
             final String permName = permissionsToRevoke.get(permNum);
+            final boolean isInternalPermission;
             synchronized (mLock) {
                 final Permission bp = mRegistry.getPermission(permName);
-                if (bp == null || !bp.isRuntime()) {
+                if (bp == null || !(bp.isInternal() || bp.isRuntime())) {
                     continue;
                 }
+                isInternalPermission = bp.isInternal();
             }
             mPackageManagerInt.forEachPackage(pkg -> {
                 final String packageName = pkg.getPackageName();
@@ -2345,12 +2348,18 @@
                     if (permissionState == PackageManager.PERMISSION_GRANTED
                             && (flags & flagMask) == 0) {
                         final int uid = UserHandle.getUid(userId, appId);
-                        EventLog.writeEvent(0x534e4554, "154505240", uid,
-                                "Revoking permission " + permName + " from package "
-                                        + packageName + " due to definition change");
-                        EventLog.writeEvent(0x534e4554, "168319670", uid,
-                                "Revoking permission " + permName + " from package "
-                                        + packageName + " due to definition change");
+                        if (isInternalPermission) {
+                            EventLog.writeEvent(0x534e4554, "195338390", uid,
+                                    "Revoking permission " + permName + " from package "
+                                            + packageName + " due to definition change");
+                        } else {
+                            EventLog.writeEvent(0x534e4554, "154505240", uid,
+                                    "Revoking permission " + permName + " from package "
+                                            + packageName + " due to definition change");
+                            EventLog.writeEvent(0x534e4554, "168319670", uid,
+                                    "Revoking permission " + permName + " from package "
+                                            + packageName + " due to definition change");
+                        }
                         Slog.e(TAG, "Revoking permission " + permName + " from package "
                                 + packageName + " due to definition change");
                         try {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 36a854e..2894708 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2304,10 +2304,9 @@
         public void requestChannelBrowsable(Uri channelUri, int userId)
                 throws RemoteException {
             final String callingPackageName = getCallingPackageName();
+            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, "requestChannelBrowsable");
             final long identity = Binder.clearCallingIdentity();
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                userId, "requestChannelBrowsable");
             try {
                 Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED);
                 List<ResolveInfo> list = getContext().getPackageManager()
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 308df2f..c2cfe0b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -656,6 +656,12 @@
     boolean mLastImeShown;
 
     /**
+     * When set to true, the IME insets will be frozen until the next app becomes IME input target.
+     * @see InsetsPolicy#adjustVisibilityForIme
+     */
+    boolean mImeInsetsFrozenUntilStartInput;
+
+    /**
      * A flag to determine if this AR is in the process of closing or entering PIP. This is needed
      * to help AR know that the app is in the process of closing but hasn't yet started closing on
      * the WM side.
@@ -1363,6 +1369,7 @@
         }
         if (newTask != null && isState(RESUMED)) {
             newTask.setResumedActivity(this, "onParentChanged");
+            mImeInsetsFrozenUntilStartInput = false;
         }
 
         if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -4759,6 +4766,7 @@
                     && imeInputTarget.getWindow().mActivityRecord == this
                     && mDisplayContent.mInputMethodWindow != null
                     && mDisplayContent.mInputMethodWindow.isVisible();
+            mImeInsetsFrozenUntilStartInput = true;
         }
 
         final DisplayContent displayContent = getDisplayContent();
@@ -5877,6 +5885,14 @@
             // closing activity having to wait until idle timeout to be stopped or destroyed if the
             // next activity won't report idle (e.g. repeated view animation).
             mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
+
+            // If the activity is visible, but no windows are eligible to start input, unfreeze
+            // to avoid permanently frozen IME insets.
+            if (mImeInsetsFrozenUntilStartInput && getWindow(
+                    win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags))
+                    == null) {
+                mImeInsetsFrozenUntilStartInput = false;
+            }
         }
     }
 
@@ -7792,6 +7808,13 @@
         }
     }
 
+    @Override
+    void onResize() {
+        // Reset freezing IME insets flag when the activity resized.
+        mImeInsetsFrozenUntilStartInput = false;
+        super.onResize();
+    }
+
     /** Returns true if the configuration is compatible with this activity. */
     boolean isConfigurationCompatible(Configuration config) {
         final int orientation = getRequestedOrientation();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index dbc1116..9335846 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1165,10 +1165,10 @@
         }
     }
 
-    WindowToken removeWindowToken(IBinder binder) {
+    WindowToken removeWindowToken(IBinder binder, boolean animateExit) {
         final WindowToken token = mTokenMap.remove(binder);
         if (token != null && token.asActivityRecord() == null) {
-            token.setExiting();
+            token.setExiting(animateExit);
         }
         return token;
     }
@@ -1252,7 +1252,7 @@
     }
 
     void removeAppToken(IBinder binder) {
-        final WindowToken token = removeWindowToken(binder);
+        final WindowToken token = removeWindowToken(binder, true /* animateExit */);
         if (token == null) {
             Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder);
             return;
@@ -3971,6 +3971,9 @@
     void updateImeInputAndControlTarget(WindowState target) {
         if (mImeInputTarget != target) {
             ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
+            if (target != null && target.mActivityRecord != null) {
+                target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+            }
             setImeInputTarget(target);
             updateImeControlTarget();
         }
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f2f1926..a8e1c1c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -211,7 +211,7 @@
     InsetsState getInsetsForWindow(WindowState target) {
         final InsetsState originalState = mStateController.getInsetsForWindow(target);
         final InsetsState state = adjustVisibilityForTransientTypes(originalState);
-        return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
+        return adjustVisibilityForIme(target, state, state == originalState);
     }
 
     /**
@@ -241,16 +241,37 @@
         return state;
     }
 
-    // Navigation bar insets is always visible to IME.
-    private static InsetsState adjustVisibilityForIme(InsetsState originalState,
+    private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
             boolean copyState) {
-        final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
-        if (originalNavSource != null && !originalNavSource.isVisible()) {
-            final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
-            final InsetsSource navSource = new InsetsSource(originalNavSource);
-            navSource.setVisible(true);
-            state.addSource(navSource);
-            return state;
+        if (w.mIsImWindow) {
+            // Navigation bar insets is always visible to IME.
+            final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
+            if (originalNavSource != null && !originalNavSource.isVisible()) {
+                final InsetsState state = copyState ? new InsetsState(originalState)
+                        : originalState;
+                final InsetsSource navSource = new InsetsSource(originalNavSource);
+                navSource.setVisible(true);
+                state.addSource(navSource);
+                return state;
+            }
+        } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
+            // During switching tasks with gestural navigation, if the IME is attached to
+            // one app window on that time, even the next app window is behind the IME window,
+            // conceptually the window should not receive the IME insets if the next window is
+            // not eligible IME requester and ready to show IME on top of it.
+            final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
+            final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
+
+            if (shouldImeAttachedToApp && originalImeSource != null) {
+                final boolean imeVisibility =
+                        w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME);
+                final InsetsState state = copyState ? new InsetsState(originalState)
+                        : originalState;
+                final InsetsSource imeSource = new InsetsSource(originalImeSource);
+                imeSource.setVisible(imeVisibility);
+                state.addSource(imeSource);
+                return state;
+            }
         }
         return originalState;
     }
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 194f48f..b54e8b7 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -66,8 +66,8 @@
     }
 
     @Override
-    void setExiting() {
-        super.setExiting();
+    void setExiting(boolean animateExit) {
+        super.setExiting(animateExit);
         mDisplayContent.mWallpaperController.removeWallpaperToken(this);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 47087cf..4fac05c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -445,8 +445,21 @@
      * @param removeWindows Whether to also remove the windows associated with the token.
      * @param displayId The display to remove the token from.
      */
+    public final void removeWindowToken(android.os.IBinder token, boolean removeWindows,
+            int displayId) {
+        removeWindowToken(token, removeWindows, true /* animateExit */, displayId);
+    }
+
+    /**
+     * Removes a window token.
+     *
+     * @param token The toke to remove.
+     * @param removeWindows Whether to also remove the windows associated with the token.
+     * @param animateExit Whether to play the windows exit animation after the token removal.
+     * @param displayId The display to remove the token from.
+     */
     public abstract void removeWindowToken(android.os.IBinder token, boolean removeWindows,
-            int displayId);
+            boolean animateExit, int displayId);
 
     /**
      * Registers a listener to be notified about app transition events.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1ec9187..9caef70 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2816,6 +2816,31 @@
 
     }
 
+    void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
+            int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+
+            if (dc == null) {
+                ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+                        + " for non-exiting displayId=%d", binder, displayId);
+                return;
+            }
+            final WindowToken token = dc.removeWindowToken(binder, animateExit);
+            if (token == null) {
+                ProtoLog.w(WM_ERROR,
+                        "removeWindowToken: Attempted to remove non-existing token: %s",
+                        binder);
+                return;
+            }
+
+            if (removeWindows) {
+                token.removeAllWindowsIfPossible();
+            }
+            dc.getInputMonitor().updateInputWindowsLw(true /* force */);
+        }
+    }
+
     @Override
     public void removeWindowToken(IBinder binder, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
@@ -2823,23 +2848,7 @@
         }
         final long origId = Binder.clearCallingIdentity();
         try {
-            synchronized (mGlobalLock) {
-                final DisplayContent dc = mRoot.getDisplayContent(displayId);
-
-                if (dc == null) {
-                    ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
-                            + " for non-exiting displayId=%d", binder, displayId);
-                    return;
-                }
-                final WindowToken token = dc.removeWindowToken(binder);
-                if (token == null) {
-                    ProtoLog.w(WM_ERROR,
-                            "removeWindowToken: Attempted to remove non-existing token: %s",
-                            binder);
-                    return;
-                }
-                dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
-            }
+            removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
@@ -7536,28 +7545,10 @@
         }
 
         @Override
-        public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) {
-            synchronized (mGlobalLock) {
-                if (removeWindows) {
-                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                    if (dc == null) {
-                        ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
-                                + " for non-exiting displayId=%d", binder, displayId);
-                        return;
-                    }
-
-                    final WindowToken token = dc.removeWindowToken(binder);
-                    if (token == null) {
-                        ProtoLog.w(WM_ERROR,
-                                "removeWindowToken: Attempted to remove non-existing token: %s",
-                                binder);
-                        return;
-                    }
-
-                    token.removeAllWindowsIfPossible();
-                }
-                WindowManagerService.this.removeWindowToken(binder, displayId);
-            }
+        public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
+                int displayId) {
+            WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit,
+                    displayId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c3fc995..5e042ef 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2180,11 +2180,18 @@
         }
     }
 
-    boolean onSetAppExiting() {
+    boolean onSetAppExiting(boolean animateExit) {
         final DisplayContent displayContent = getDisplayContent();
         boolean changed = false;
 
-        if (isVisibleNow()) {
+        if (!animateExit) {
+            // Hide the window permanently if no window exist animation is performed, so we can
+            // avoid the window surface becoming visible again unexpectedly during the next
+            // relayout.
+            mPermanentlyHidden = true;
+            hide(false /* doAnimation */, false /* requestAnim */);
+        }
+        if (isVisibleNow() && animateExit) {
             mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
             if (mWmService.mAccessibilityController != null) {
                 mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
@@ -2197,7 +2204,7 @@
 
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowState c = mChildren.get(i);
-            changed |= c.onSetAppExiting();
+            changed |= c.onSetAppExiting(animateExit);
         }
 
         return changed;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fbfa400..3cbc67c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -24,6 +24,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -232,7 +233,7 @@
         }
     }
 
-    void setExiting() {
+    void setExiting(boolean animateExit) {
         if (isEmpty()) {
             super.removeImmediately();
             return;
@@ -247,11 +248,12 @@
 
         final int count = mChildren.size();
         boolean changed = false;
-        final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN);
+        final boolean delayed = isAnimating(TRANSITION | PARENTS)
+                || (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit);
 
         for (int i = 0; i < count; i++) {
             final WindowState win = mChildren.get(i);
-            changed |= win.onSetAppExiting();
+            changed |= win.onSetAppExiting(animateExit);
         }
 
         final ActivityRecord app = asActivityRecord();
@@ -353,7 +355,7 @@
     @Override
     void removeImmediately() {
         if (mDisplayContent != null) {
-            mDisplayContent.removeWindowToken(token);
+            mDisplayContent.removeWindowToken(token, true /* animateExit */);
         }
         // Needs to occur after the token is removed from the display above to avoid attempt at
         // duplicate removal of this window container from it's parent.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 293e862a..6f04f17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -39,6 +39,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -2816,6 +2817,73 @@
         assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
     }
 
+    @Test
+    public void testImeInsetsFrozenFlag_resetWhenReparented() {
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app");
+        final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+        final Task newTask = new TaskBuilder(mSupervisor).build();
+        makeWindowVisible(app, imeWindow);
+        mDisplayContent.mInputMethodWindow = imeWindow;
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+
+        // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        app.mActivityRecord.commitVisibility(false, false);
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Expect IME insets frozen state will reset when the activity is reparent to the new task.
+        activity.setState(RESUMED, "test");
+        activity.reparent(newTask, 0 /* top */, "test");
+        assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+    }
+
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testImeInsetsFrozenFlag_resetWhenResized() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        makeWindowVisibleAndDrawn(app, mImeWindow);
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+
+        // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        app.mActivityRecord.commitVisibility(false, false);
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Expect IME insets frozen state will reset when the activity is reparent to the new task.
+        app.mActivityRecord.onResize();
+        assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+    }
+
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        makeWindowVisibleAndDrawn(app, mImeWindow);
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+
+        // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        app.mActivityRecord.commitVisibility(false, false);
+        app.mActivityRecord.onWindowsGone();
+
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Expect IME insets frozen state will reset when the activity has no IME focusable window.
+        app.mActivityRecord.forAllWindowsUnchecked(w -> {
+            w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+            return true;
+        }, true);
+
+        app.mActivityRecord.commitVisibility(true, false);
+        app.mActivityRecord.onWindowsVisible();
+
+        assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+    }
+
     private void assertHasStartingWindow(ActivityRecord atoken) {
         assertNotNull(atoken.mStartingSurface);
         assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a8e1753..8e7ba4bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -258,6 +258,7 @@
         final ActivityManagerInternal amInternal = mAmService.mInternal;
         spyOn(amInternal);
         doNothing().when(amInternal).trimApplications();
+        doNothing().when(amInternal).scheduleAppGcs();
         doNothing().when(amInternal).updateCpuStats();
         doNothing().when(amInternal).updateOomAdj();
         doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 92b670e..d88ac25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -891,6 +891,40 @@
         assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
     }
 
+    @Test
+    public void testAdjustImeInsetsVisibilityWhenSwitchingApps() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+        final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+        spyOn(imeWindow);
+        doReturn(true).when(imeWindow).isVisible();
+        mDisplayContent.mInputMethodWindow = imeWindow;
+
+        final InsetsStateController controller = mDisplayContent.getInsetsStateController();
+        controller.getImeSourceProvider().setWindow(imeWindow, null, null);
+
+        // Simulate app requests IME with updating all windows Insets State when IME is above app.
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+        assertTrue(mDisplayContent.shouldImeAttachedToApp());
+        controller.getImeSourceProvider().scheduleShowImePostLayout(app);
+        controller.getImeSourceProvider().getSource().setVisible(true);
+        controller.updateAboveInsetsState(imeWindow, false);
+
+        // Expect all app windows behind IME can receive IME insets visible.
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
+
+        // Simulate app plays closing transition to app2.
+        app.mActivityRecord.commitVisibility(false, false);
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Verify the IME insets is visible on app, but not for app2 during app task switching.
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
+    }
+
     @UseTestDisplay(addWindows = { W_ACTIVITY })
     @Test
     public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index d048f1842..589f913 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -24,6 +24,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -44,6 +45,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 import java.util.function.BiFunction;
 
@@ -126,7 +128,7 @@
         final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1");
         final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2");
 
-        mDisplayContent.removeWindowToken(token.token);
+        mDisplayContent.removeWindowToken(token.token, true /* animateExit */);
         // Verify that the token is no longer mapped on the display
         assertNull(mDisplayContent.getWindowToken(token.token));
         // Verify that the token is still attached to its parent
@@ -261,4 +263,29 @@
         assertNotNull(app.getFrozenInsetsState());
         assertNull(mDisplayContent.mInputMethodWindow.getFrozenInsetsState());
     }
+
+    @Test
+    public void testRemoveWindowToken_noAnimateExitWhenSet() {
+        final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
+        final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win");
+        makeWindowVisible(win);
+        assertTrue(win.isOnScreen());
+        spyOn(win);
+        spyOn(win.mWinAnimator);
+        spyOn(win.mToken);
+
+        // Invoking removeWindowToken with setting no window exit animation and not remove window
+        // immediately. verify the window will hide without applying exit animation.
+        mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */,
+                mDisplayContent.mDisplayId);
+        verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */);
+        verify(win).hide(false /* doAnimation */, false /* requestAnim */);
+        assertFalse(win.isOnScreen());
+        verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+        assertTrue(win.mToken.hasChild());
+
+        // Even though the window is being removed afterwards, it won't apply exit animation.
+        win.removeIfPossible();
+        verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false);
+    }
 }