Merge "Create DefaultAppsIconProvider" into main
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 5b9d30e..b703892 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -227,7 +227,7 @@
     get() =
         when (type) {
             ShortcutCategoryType.SYSTEM -> R.string.shortcut_helper_category_system
-            ShortcutCategoryType.MULTI_TASKING -> R.string.shortcut_helper_category_system
+            ShortcutCategoryType.MULTI_TASKING -> R.string.shortcut_helper_category_multitasking
             ShortcutCategoryType.IME -> R.string.shortcut_helper_category_input
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index 646db40..d0e3ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -34,7 +34,6 @@
 import androidx.lifecycle.lifecycleScope
 import com.android.compose.theme.PlatformTheme
 import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper
-import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
 import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
 import com.android.systemui.res.R
 import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -81,10 +80,7 @@
         requireViewById<ComposeView>(R.id.shortcut_helper_compose_container).apply {
             setContent {
                 PlatformTheme {
-                    val shortcutsUiState by
-                        viewModel.shortcutsUiState.collectAsStateWithLifecycle(
-                            initialValue = ShortcutsUiState.Inactive
-                        )
+                    val shortcutsUiState by viewModel.shortcutsUiState.collectAsStateWithLifecycle()
                     ShortcutHelper(
                         shortcutsUiState = shortcutsUiState,
                         onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 3759b0c..e602cad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -23,13 +23,17 @@
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
 
 class ShortcutHelperViewModel
 @Inject
 constructor(
+    @Background private val backgroundScope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val stateInteractor: ShortcutHelperStateInteractor,
     categoriesInteractor: ShortcutHelperCategoriesInteractor,
@@ -42,16 +46,22 @@
             .flowOn(backgroundDispatcher)
 
     val shortcutsUiState =
-        categoriesInteractor.shortcutCategories.map {
-            if (it.isEmpty()) {
-                ShortcutsUiState.Inactive
-            } else {
-                ShortcutsUiState.Active(
-                    shortcutCategories = it,
-                    defaultSelectedCategory = it.first().type,
-                )
+        categoriesInteractor.shortcutCategories
+            .map {
+                if (it.isEmpty()) {
+                    ShortcutsUiState.Inactive
+                } else {
+                    ShortcutsUiState.Active(
+                        shortcutCategories = it,
+                        defaultSelectedCategory = it.first().type,
+                    )
+                }
             }
-        }
+            .stateIn(
+                scope = backgroundScope,
+                started = SharingStarted.Lazily,
+                initialValue = ShortcutsUiState.Inactive
+            )
 
     fun onViewClosed() {
         stateInteractor.onViewClosed()
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index 160ae86..fe54044 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -19,6 +19,7 @@
 import android.database.ContentObserver
 import android.net.Uri
 import android.provider.Settings.SettingNotFoundException
+import androidx.annotation.WorkerThread
 import com.android.app.tracing.TraceUtils.trace
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -59,10 +60,13 @@
     fun getUriFor(name: String): Uri
 
     /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
+     * Registers listener for a given content observer <b>while blocking the current thread</b>.
      * Implicitly calls [getUriFor] on the passed in name.
+     *
+     * This should not be called from the main thread, use [registerContentObserver] or
+     * [registerContentObserverAsync] instead.
      */
+    @WorkerThread
     fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
         registerContentObserverSync(getUriFor(name), settingsObserver)
     }
@@ -90,7 +94,13 @@
             registerContentObserverSync(getUriFor(name), settingsObserver)
         }
 
-    /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
+    /**
+     * Registers listener for a given content observer <b>while blocking the current thread</b>.
+     *
+     * This should not be called from the main thread, use [registerContentObserver] or
+     * [registerContentObserverAsync] instead.
+     */
+    @WorkerThread
     fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
         registerContentObserverSync(uri, false, settingsObserver)
 
@@ -157,7 +167,13 @@
             registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
         }
 
-    /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
+    /**
+     * Registers listener for a given content observer <b>while blocking the current thread</b>.
+     *
+     * This should not be called from the main thread, use [registerContentObserver] or
+     * [registerContentObserverAsync] instead.
+     */
+    @WorkerThread
     fun registerContentObserverSync(
         uri: Uri,
         notifyForDescendants: Boolean,
@@ -200,7 +216,13 @@
             registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
         }
 
-    /** See [ContentResolver.unregisterContentObserver]. */
+    /**
+     * Unregisters the given content observer <b>while blocking the current thread</b>.
+     *
+     * This should not be called from the main thread, use [unregisterContentObserver] or
+     * [unregisterContentObserverAsync] instead.
+     */
+    @WorkerThread
     fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
         trace({ "SP#unregisterObserver" }) {
             getContentResolver().unregisterContentObserver(settingsObserver)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 55c803a..a1021f6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -100,6 +100,7 @@
 val Kosmos.shortcutHelperViewModel by
     Kosmos.Fixture {
         ShortcutHelperViewModel(
+            applicationCoroutineScope,
             testDispatcher,
             shortcutHelperStateInteractor,
             shortcutHelperCategoriesInteractor
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fbb6ccf..b0d734d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -70,7 +70,6 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -80,7 +79,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.hardware.input.InputManager;
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManagerInternal;
@@ -195,7 +193,6 @@
 import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -349,8 +346,6 @@
     @GuardedBy("ImfLock.class")
     private UserDataRepository mUserDataRepository;
 
-    @MultiUserUnawareField
-    final SettingsObserver mSettingsObserver;
     final WindowManagerInternal mWindowManagerInternal;
     private final ActivityManagerInternal mActivityManagerInternal;
     final PackageManagerInternal mPackageManagerInternal;
@@ -570,82 +565,52 @@
     @NonNull
     private final ImeTrackerService mImeTrackerService;
 
-    class SettingsObserver extends ContentObserver {
-
-        /**
-         * <em>This constructor must be called within the lock.</em>
-         */
-        SettingsObserver(Handler handler) {
-            super(handler);
+    @GuardedBy("ImfLock.class")
+    private void onSecureSettingsChangedLocked(@NonNull String key, @UserIdInt int userId) {
+        if (!mConcurrentMultiUserModeEnabled && userId != mCurrentUserId) {
+            return;
         }
-
-        void registerContentObserverForAllUsers() {
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
-                    Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.ALL);
-            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
-                    Settings.Secure.ENABLED_INPUT_METHODS), false, this, UserHandle.ALL);
-            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
-                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, UserHandle.ALL);
-            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
-                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, UserHandle.ALL);
-            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, UserHandle.ALL);
-            resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
-                    STYLUS_HANDWRITING_ENABLED), false, this, UserHandle.ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, int flags,
-                @UserIdInt int userId) {
-            uris.forEach(uri -> onChangeInternal(uri, userId));
-        }
-
-        private void onChangeInternal(@NonNull Uri uri, @UserIdInt int userId) {
-            final Uri showImeUri = Settings.Secure.getUriFor(
-                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
-            final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
-            final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor(
-                    STYLUS_HANDWRITING_ENABLED);
-            synchronized (ImfLock.class) {
-                if (!mConcurrentMultiUserModeEnabled && mCurrentUserId != userId) {
-                    return;
+        switch (key) {
+            case Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD: {
+                mMenuController.updateKeyboardFromSettingsLocked();
+                break;
+            }
+            case Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE: {
+                final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, userId);
+                mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
+                        accessibilitySoftKeyboardSetting);
+                final var userData = getUserData(userId);
+                if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
+                    hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
+                            0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, userId);
+                } else if (isShowRequestedForCurrentWindow(userId)) {
+                    showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
+                            InputMethodManager.SHOW_IMPLICIT,
+                            SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);
                 }
-
-                if (showImeUri.equals(uri)) {
-                    mMenuController.updateKeyboardFromSettingsLocked();
-                } else if (accessibilityRequestingNoImeUri.equals(uri)) {
-                    final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
-                            mContext.getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, userId);
-                    mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
-                            accessibilitySoftKeyboardSetting);
-                    final var userData = getUserData(userId);
-                    if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
-                        hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
-                                0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE,
-                                userId);
-                    } else if (isShowRequestedForCurrentWindow(userId)) {
-                        showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
-                                InputMethodManager.SHOW_IMPLICIT,
-                                SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);
-                    }
-                } else if (stylusHandwritingEnabledUri.equals(uri)) {
-                    InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches();
-                    InputMethodManager
-                            .invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches();
-                } else {
-                    boolean enabledChanged = false;
-                    String newEnabled = InputMethodSettingsRepository.get(userId)
-                            .getEnabledInputMethodsStr();
-                    final var userData = getUserData(userId);
-                    if (!userData.mLastEnabledInputMethodsStr.equals(newEnabled)) {
-                        userData.mLastEnabledInputMethodsStr = newEnabled;
-                        enabledChanged = true;
-                    }
-                    updateInputMethodsFromSettingsLocked(enabledChanged, userId);
+                break;
+            }
+            case STYLUS_HANDWRITING_ENABLED: {
+                InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches();
+                InputMethodManager
+                        .invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches();
+                break;
+            }
+            case Settings.Secure.DEFAULT_INPUT_METHOD:
+            case Settings.Secure.ENABLED_INPUT_METHODS:
+            case Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE: {
+                boolean enabledChanged = false;
+                String newEnabled = InputMethodSettingsRepository.get(userId)
+                        .getEnabledInputMethodsStr();
+                final var userData = getUserData(userId);
+                if (!userData.mLastEnabledInputMethodsStr.equals(newEnabled)) {
+                    userData.mLastEnabledInputMethodsStr = newEnabled;
+                    enabledChanged = true;
                 }
+                updateInputMethodsFromSettingsLocked(enabledChanged, userId);
+                break;
             }
         }
     }
@@ -1118,8 +1083,6 @@
             }
             SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
             mImeTrackerService = new ImeTrackerService(mHandler);
-            // Note: SettingsObserver doesn't register observers in its constructor.
-            mSettingsObserver = new SettingsObserver(mHandler);
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
             mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
@@ -1389,7 +1352,19 @@
                 }, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
 
                 mMyPackageMonitor.register(mContext, UserHandle.ALL, mIoHandler);
-                mSettingsObserver.registerContentObserverForAllUsers();
+                SecureSettingsChangeCallback.register(mHandler, mContext.getContentResolver(),
+                        new String[] {
+                                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                                Settings.Secure.DEFAULT_INPUT_METHOD,
+                                Settings.Secure.ENABLED_INPUT_METHODS,
+                                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                                Settings.Secure.STYLUS_HANDWRITING_ENABLED,
+                        }, (key, flags, userId) -> {
+                            synchronized (ImfLock.class) {
+                                onSecureSettingsChangedLocked(key, userId);
+                            }
+                        });
 
                 final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
                 broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
diff --git a/services/core/java/com/android/server/inputmethod/SecureSettingsChangeCallback.java b/services/core/java/com/android/server/inputmethod/SecureSettingsChangeCallback.java
new file mode 100644
index 0000000..328d7c6
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/SecureSettingsChangeCallback.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import java.util.Collection;
+
+/**
+ * A wrapper interface to monitor the given set of {@link Settings.Secure}.
+ */
+@FunctionalInterface
+interface SecureSettingsChangeCallback {
+    /**
+     * Called back when the value associated with {@code key} is updated.
+     *
+     * @param key a key defined in {@link Settings.Secure}
+     * @param flags flags defined in {@link ContentResolver.NotifyFlags}
+     * @param userId the user ID with which the value is associated
+     */
+    void onChange(@NonNull String key, @ContentResolver.NotifyFlags int flags,
+            @UserIdInt int userId);
+
+    /**
+     * Registers {@link SecureSettingsChangeCallback} to the given set of {@link Settings.Secure}.
+     *
+     * @param handler  {@link Handler} to be used to call back {@link #onChange(String, int, int)}
+     * @param resolver {@link ContentResolver} with which {@link Settings.Secure} will be retrieved
+     * @param keys     A set of {@link Settings.Secure} to be monitored
+     * @param callback {@link SecureSettingsChangeCallback} to be called back
+     */
+    @NonNull
+    static void register(@NonNull Handler handler, @NonNull ContentResolver resolver,
+            @NonNull String[] keys, @NonNull SecureSettingsChangeCallback callback) {
+        final ArrayMap<Uri, String> uriMapper = new ArrayMap<>();
+        for (String key : keys) {
+            uriMapper.put(Settings.Secure.getUriFor(key), key);
+        }
+        final ContentObserver observer = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, int flags,
+                    @UserIdInt int userId) {
+                uris.forEach(uri -> {
+                    final String key = uriMapper.get(uri);
+                    if (key != null) {
+                        callback.onChange(key, flags, userId);
+                    }
+                });
+            }
+        };
+        for (Uri uri : uriMapper.keySet()) {
+            resolver.registerContentObserverAsUser(uri, false /* notifyForDescendants */, observer,
+                    UserHandle.ALL);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index edd118d..1d02f1c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -179,7 +179,7 @@
 
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
-    protected final WindowList<E> mChildren = new WindowList<E>();
+    protected final ArrayList<E> mChildren = new ArrayList<E>();
 
     // The specified orientation for this window container.
     // Shouldn't be accessed directly since subclasses can override getOverrideOrientation.
@@ -855,7 +855,7 @@
             mSurfaceFreezer.unfreeze(getSyncTransaction());
         }
         while (!mChildren.isEmpty()) {
-            final E child = mChildren.peekLast();
+            final E child = mChildren.getLast();
             child.removeImmediately();
             // Need to do this after calling remove on the child because the child might try to
             // remove/detach itself from its parent which will cause an exception if we remove
@@ -979,7 +979,7 @@
 
         switch (position) {
             case POSITION_TOP:
-                if (mChildren.peekLast() != child) {
+                if (getTopChild() != child) {
                     mChildren.remove(child);
                     mChildren.add(child);
                     onChildPositionChanged(child);
@@ -990,7 +990,7 @@
                 }
                 break;
             case POSITION_BOTTOM:
-                if (mChildren.peekFirst() != child) {
+                if (getBottomChild() != child) {
                     mChildren.remove(child);
                     mChildren.addFirst(child);
                     onChildPositionChanged(child);
@@ -1445,7 +1445,13 @@
 
     /** Returns the top child container. */
     E getTopChild() {
-        return mChildren.peekLast();
+        final int n = mChildren.size();
+        return n == 0 ? null : mChildren.get(n - 1);
+    }
+
+    E getBottomChild() {
+        final int n = mChildren.size();
+        return n == 0 ? null : mChildren.get(0);
     }
 
     /**
@@ -2550,7 +2556,7 @@
         }
 
         if (mParent != null && mParent == other.mParent) {
-            final WindowList<WindowContainer> list = mParent.mChildren;
+            final ArrayList<WindowContainer> list = mParent.mChildren;
             return list.indexOf(this) > list.indexOf(other) ? 1 : -1;
         }
 
@@ -2587,7 +2593,7 @@
 
             // The position of the first non-common ancestor in the common ancestor list determines
             // which is greater the which.
-            final WindowList<WindowContainer> list = commonAncestor.mChildren;
+            final ArrayList<WindowContainer> list = commonAncestor.mChildren;
             return list.indexOf(thisParentChain.peekLast()) > list.indexOf(otherParentChain.peekLast())
                     ? 1 : -1;
         } finally {
diff --git a/services/core/java/com/android/server/wm/WindowList.java b/services/core/java/com/android/server/wm/WindowList.java
deleted file mode 100644
index 1e888f5..0000000
--- a/services/core/java/com/android/server/wm/WindowList.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2017 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.wm;
-
-import java.util.ArrayList;
-
-/**
- * An {@link ArrayList} with extended functionality to be used as the children data structure in
- * {@link WindowContainer}.
- */
-class WindowList<E> extends ArrayList<E> {
-
-    public void addFirst(E e) {
-        add(0, e);
-    }
-
-    E peekLast() {
-        return size() > 0 ? get(size() - 1) : null;
-    }
-
-    E peekFirst() {
-        return size() > 0 ? get(0) : null;
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index deb7098..e900488 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1596,7 +1596,7 @@
             case OP_TYPE_REORDER_TO_BOTTOM_OF_TASK: {
                 final Task task = taskFragment.getTask();
                 if (task != null) {
-                    if (task.mChildren.peekFirst() != taskFragment) {
+                    if (task.getBottomChild() != taskFragment) {
                         task.mChildren.remove(taskFragment);
                         task.mChildren.add(0, taskFragment);
                         if (!taskFragment.hasChild()) {
@@ -1612,7 +1612,7 @@
             case OP_TYPE_REORDER_TO_TOP_OF_TASK: {
                 final Task task = taskFragment.getTask();
                 if (task != null) {
-                    if (task.mChildren.peekLast() != taskFragment) {
+                    if (task.getTopChild() != taskFragment) {
                         task.mChildren.remove(taskFragment);
                         task.mChildren.add(taskFragment);
                         if (!taskFragment.hasChild()) {
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 ef3df6c..2e9726f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2482,10 +2482,10 @@
         assertTrue(activity.mChildren.contains(win4));
 
         // The starting window should be on-top of all other windows.
-        assertEquals(startingWin, activity.mChildren.peekLast());
+        assertEquals(startingWin, activity.getTopChild());
 
         // The base application window should be below all other windows.
-        assertEquals(baseWin, activity.mChildren.peekFirst());
+        assertEquals(baseWin, activity.getBottomChild());
         activity.removeImmediately();
     }