Merge "Color correction improvement (1/n)"
diff --git a/core/java/android/content/pm/ParceledListSlice.aidl b/core/java/android/content/pm/ParceledListSlice.aidl
index c02cc6a..5031fba 100644
--- a/core/java/android/content/pm/ParceledListSlice.aidl
+++ b/core/java/android/content/pm/ParceledListSlice.aidl
@@ -16,4 +16,4 @@
 
 package android.content.pm;
 
-parcelable ParceledListSlice;
+parcelable ParceledListSlice<T>;
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index 443bbd8..e81e3f7 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -70,22 +70,6 @@
 
     /**
      * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
-     * ByteBuffer} created by the {@link ByteBufferFactory}.  The output is suitable to be used as
-     * the on-disk format for fs-verity to use.
-     *
-     * @return VerityResult containing a buffer with the generated Merkle tree stored at the
-     *         front, the tree size, and the calculated root hash.
-     */
-    @NonNull
-    public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
-            @NonNull ByteBufferFactory bufferFactory)
-            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
-                false /* skipSigningBlock */);
-    }
-
-    /**
-     * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
      * ByteBuffer} created by the {@link ByteBufferFactory}.  The Merkle tree does not cover Signing
      * Block specificed in {@code signatureInfo}.  The output is suitable to be used as the on-disk
      * format for fs-verity to use (with elide and patch extensions).
@@ -97,21 +81,16 @@
     public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
-                true /* skipSigningBlock */);
+        return generateVerityTreeInternal(apk, bufferFactory, signatureInfo);
     }
 
     @NonNull
     private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
-            @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
-            boolean skipSigningBlock)
+            @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        long dataSize = apk.length();
-        if (skipSigningBlock) {
-            long signingBlockSize =
-                    signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
-            dataSize -= signingBlockSize;
-        }
+        long signingBlockSize =
+                signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+        long dataSize = apk.length() - signingBlockSize;
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
         int merkleTreeSize = levelOffset[levelOffset.length - 1];
 
@@ -120,10 +99,8 @@
                 + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
         ByteBuffer tree = slice(output, 0, merkleTreeSize);
-        // Only use default salt in legacy case.
-        byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
-        byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
-                tree, skipSigningBlock);
+        byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT,
+                levelOffset, tree);
         return new VerityResult(output, merkleTreeSize, apkRootHash);
     }
 
@@ -173,8 +150,7 @@
             throws IOException, SignatureNotFoundException, SecurityException, DigestException,
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
-            VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
-                    true /* skipSigningBlock */);
+            VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo);
             ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
                     result.verityData.limit());
             generateApkVerityFooter(apk, signatureInfo, footer);
@@ -351,17 +327,12 @@
     @NonNull
     private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
-            @NonNull int[] levelOffset, @NonNull ByteBuffer output, boolean skipSigningBlock)
+            @NonNull int[] levelOffset, @NonNull ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
         // 1. Digest the apk to generate the leaf level hashes.
-        if (skipSigningBlock) {
-            assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-            generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
-                        levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
-        } else {
-            generateFsVerityDigestAtLeafLevel(apk, slice(output,
-                        levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
-        }
+        assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+        generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
+                    levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
 
         // 2. Digest the lower level hashes bottom up.
         for (int level = levelOffset.length - 3; level >= 0; level--) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c699cdc..d79abc2 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1170,16 +1170,6 @@
     /**
      * {@hide}
      */
-    @UnsupportedAppUsage
-    protected void setNeedsMenuKey(int value) {
-        final WindowManager.LayoutParams attrs = getAttributes();
-        attrs.needsMenuKey = value;
-        dispatchWindowAttributesChanged(attrs);
-    }
-
-    /**
-     * {@hide}
-     */
     protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) {
         if (mCallback != null) {
             mCallback.onWindowAttributesChanged(attrs);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 62a824d..9d5f98e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -29,7 +29,6 @@
 import static android.view.WindowLayoutParamsProto.HEIGHT;
 import static android.view.WindowLayoutParamsProto.HORIZONTAL_MARGIN;
 import static android.view.WindowLayoutParamsProto.INPUT_FEATURE_FLAGS;
-import static android.view.WindowLayoutParamsProto.NEEDS_MENU_KEY;
 import static android.view.WindowLayoutParamsProto.PREFERRED_REFRESH_RATE;
 import static android.view.WindowLayoutParamsProto.PRIVATE_FLAGS;
 import static android.view.WindowLayoutParamsProto.ROTATION_ANIMATION;
@@ -1948,48 +1947,6 @@
         public int privateFlags;
 
         /**
-         * Value for {@link #needsMenuKey} for a window that has not explicitly specified if it
-         * needs {@link #NEEDS_MENU_SET_TRUE} or doesn't need {@link #NEEDS_MENU_SET_FALSE} a menu
-         * key. For this case, we should look at windows behind it to determine the appropriate
-         * value.
-         *
-         * @hide
-         */
-        public static final int NEEDS_MENU_UNSET = 0;
-
-        /**
-         * Value for {@link #needsMenuKey} for a window that has explicitly specified it needs a
-         * menu key.
-         *
-         * @hide
-         */
-        @UnsupportedAppUsage
-        public static final int NEEDS_MENU_SET_TRUE = 1;
-
-        /**
-         * Value for {@link #needsMenuKey} for a window that has explicitly specified it doesn't
-         * needs a menu key.
-         *
-         * @hide
-         */
-        @UnsupportedAppUsage
-        public static final int NEEDS_MENU_SET_FALSE = 2;
-
-        /**
-         * State variable for a window belonging to an activity that responds to
-         * {@link KeyEvent#KEYCODE_MENU} and therefore needs a Menu key. For devices where Menu is a
-         * physical button this variable is ignored, but on devices where the Menu key is drawn in
-         * software it may be hidden unless this variable is set to {@link #NEEDS_MENU_SET_TRUE}.
-         *
-         *  (Note that Action Bars, when available, are the preferred way to offer additional
-         * functions otherwise accessed via an options menu.)
-         *
-         * {@hide}
-         */
-        @UnsupportedAppUsage
-        public int needsMenuKey = NEEDS_MENU_UNSET;
-
-        /**
          * Given a particular set of window manager flags, determine whether
          * such a window may be a target for an input method when it has
          * focus.  In particular, this checks the
@@ -2791,7 +2748,6 @@
             out.writeInt(surfaceInsets.bottom);
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
-            out.writeInt(needsMenuKey);
             out.writeLong(accessibilityIdOfAnchor);
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
             out.writeInt(mColorMode);
@@ -2849,7 +2805,6 @@
             surfaceInsets.bottom = in.readInt();
             hasManualSurfaceInsets = in.readInt() != 0;
             preservePreviousSurfaceInsets = in.readInt() != 0;
-            needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readLong();
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mColorMode = in.readInt();
@@ -2891,8 +2846,6 @@
         /** {@hide} */
         public static final int PREFERRED_REFRESH_RATE_CHANGED = 1 << 21;
         /** {@hide} */
-        public static final int NEEDS_MENU_KEY_CHANGED = 1 << 22;
-        /** {@hide} */
         public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23;
         /** {@hide} */
         public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
@@ -3066,11 +3019,6 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
-            if (needsMenuKey != o.needsMenuKey) {
-                needsMenuKey = o.needsMenuKey;
-                changes |= NEEDS_MENU_KEY_CHANGED;
-            }
-
             if (accessibilityIdOfAnchor != o.accessibilityIdOfAnchor) {
                 accessibilityIdOfAnchor = o.accessibilityIdOfAnchor;
                 changes |= ACCESSIBILITY_ANCHOR_CHANGED;
@@ -3224,9 +3172,6 @@
                     sb.append(" (!preservePreviousSurfaceInsets)");
                 }
             }
-            if (needsMenuKey == NEEDS_MENU_SET_TRUE) {
-                sb.append(" needsMenuKey");
-            }
             if (mColorMode != COLOR_MODE_DEFAULT) {
                 sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
             }
@@ -3288,7 +3233,6 @@
             proto.write(HAS_SYSTEM_UI_LISTENERS, hasSystemUiListeners);
             proto.write(INPUT_FEATURE_FLAGS, inputFeatures);
             proto.write(USER_ACTIVITY_TIMEOUT, userActivityTimeout);
-            proto.write(NEEDS_MENU_KEY, needsMenuKey);
             proto.write(COLOR_MODE, mColorMode);
             proto.write(FLAGS, flags);
             proto.write(PRIVATE_FLAGS, privateFlags);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 764d4a59..9aa56f0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -22,7 +22,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
@@ -2423,19 +2422,8 @@
 
         final Context context = getContext();
         final int targetSdk = context.getApplicationInfo().targetSdkVersion;
-        final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
-        final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
         final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
         final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
-        final boolean targetHcNeedsOptions = context.getResources().getBoolean(
-                R.bool.target_honeycomb_needs_options_menu);
-        final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
-
-        if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
-            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
-        } else {
-            setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
-        }
 
         if (!mForcedStatusBarColor) {
             mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index 93a9fe2..e7c2827 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -56,13 +56,6 @@
     optional uint32 input_feature_flags = 19;
     optional int64 user_activity_timeout = 20;
 
-    enum NeedsMenuState {
-        NEEDS_MENU_UNSET = 0;
-        NEEDS_MENU_SET_TRUE = 1;
-        NEEDS_MENU_SET_FALSE = 2;
-    }
-    optional NeedsMenuState needs_menu_key = 22;
-
     optional DisplayProto.ColorMode color_mode = 23;
     optional uint32 flags = 24;
     optional uint32 private_flags = 26;
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 00f45c1..b339717 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -15,7 +15,6 @@
 -->
 
 <resources>
-    <bool name="target_honeycomb_needs_options_menu">false</bool>
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="kg_share_status_area">false</bool>
     <bool name="kg_sim_puk_account_full_screen">false</bool>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index b49fe49..29f9f6c 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -23,7 +23,6 @@
     <bool name="preferences_prefer_dual_pane">false</bool>
     <bool name="show_ongoing_ime_switcher">true</bool>
     <bool name="action_bar_expanded_action_views_exclusive">true</bool>
-    <bool name="target_honeycomb_needs_options_menu">true</bool>
     <!-- Whether or not to use the drawable/lockscreen_notselected and
          drawable/lockscreen_selected instead of the generic dots when displaying
          the LockPatternView.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 90343e0..29b9281 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1660,7 +1660,6 @@
   <java-symbol type="bool" name="config_perDisplayFocusEnabled" />
   <java-symbol type="bool" name="config_showNavigationBar" />
   <java-symbol type="bool" name="config_supportAutoRotation" />
-  <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
   <java-symbol type="dimen" name="docked_stack_divider_thickness" />
   <java-symbol type="dimen" name="docked_stack_divider_insets" />
   <java-symbol type="dimen" name="docked_stack_minimize_thickness" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 440b885..eff353c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -79,12 +79,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1976550065": {
-      "message": "commitVisibility: %s: visible=%b visibleRequested=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-1963461591": {
       "message": "Removing %s from %s",
       "level": "VERBOSE",
@@ -283,6 +277,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1521427940": {
+      "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1515151503": {
       "message": ">>> OPEN TRANSACTION removeReplacedWindows",
       "level": "INFO",
@@ -697,12 +697,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "-633961578": {
-      "message": "applyAnimation: transition animation is disabled or skipped. container=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-622997754": {
       "message": "postWindowRemoveCleanupLocked: %s",
       "level": "VERBOSE",
@@ -1477,12 +1471,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "841702299": {
-      "message": "Changing app %s visible=%b performLayout=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "845234215": {
       "message": "App is requesting an orientation, return %d for display id=%d",
       "level": "VERBOSE",
@@ -1993,6 +1981,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "1967975839": {
+      "message": "Changing app %s visible=%b performLayout=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1b93c6e..ade292f 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -40,6 +40,8 @@
     <string name="wifi_security_short_sae" translatable="false">WPA3</string>
     <!-- Do not translate.  Concise terminology for wifi with WPA2/WPA3 transition security -->
     <string name="wifi_security_short_psk_sae" translatable="false">WPA2/WPA3</string>
+    <!-- Do not translate.  Concise terminology for Wi-Fi with None/OWE transition mode security -->
+    <string name="wifi_security_short_none_owe" translatable="false">None/OWE</string>
     <!-- Do not translate.  Concise terminology for wifi with OWE security -->
     <string name="wifi_security_short_owe" translatable="false">OWE</string>
     <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
@@ -70,6 +72,8 @@
     <string name="wifi_security_sae" translatable="false">WPA3-Personal</string>
     <!-- Do not translate.  Terminology for wifi with WPA2/WPA3 Transition mode security -->
     <string name="wifi_security_psk_sae" translatable="false">WPA2/WPA3-Personal</string>
+    <!-- Do not translate.  Terminology for Wi-Fi with None/OWE transition mode security -->
+    <string name="wifi_security_none_owe" translatable="false">None/Enhanced Open</string>
     <!-- Do not translate.  Terminology for wifi with OWE security -->
     <string name="wifi_security_owe" translatable="false">Enhanced Open</string>
     <!-- Do not translate.  Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 328bfb2..49e214b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1004,6 +1004,10 @@
             return concise ? context.getString(R.string.wifi_security_short_psk_sae) :
                     context.getString(R.string.wifi_security_psk_sae);
         }
+        if (mIsOweTransitionMode) {
+            return concise ? context.getString(R.string.wifi_security_short_none_owe) :
+                    context.getString(R.string.wifi_security_none_owe);
+        }
 
         switch(security) {
             case SECURITY_EAP:
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 325366e..2a70506 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -1683,6 +1683,18 @@
         assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isFalse();
     }
 
+    @Test
+    public void testGetSecurityString_oweTransitionMode_shouldReturnCorrectly() {
+        when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+        when(mMockWifiManager.isEnhancedOpenSupported()).thenReturn(true);
+        AccessPoint oweTransitionModeAp = getOweTransitionModeAp();
+
+        assertThat(oweTransitionModeAp.getSecurityString(true /* concise */))
+                .isEqualTo(mContext.getString(R.string.wifi_security_short_none_owe));
+        assertThat(oweTransitionModeAp.getSecurityString(false /* concise */))
+                .isEqualTo(mContext.getString(R.string.wifi_security_none_owe));
+    }
+
     private AccessPoint getPskSaeTransitionModeAp() {
         ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
                 TEST_BSSID, DEFAULT_RSSI);
@@ -1692,4 +1704,13 @@
                 .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult)))
                 .build();
     }
+
+    private AccessPoint getOweTransitionModeAp() {
+        ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID),
+                TEST_BSSID, DEFAULT_RSSI);
+        scanResult.capabilities = "[OWE_TRANSITION]";
+        return new TestAccessPointBuilder(mContext)
+                .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult)))
+                .build();
+    }
 }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b28a112..f608642 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -273,14 +273,6 @@
         public WindowManager.LayoutParams getAttrs();
 
         /**
-         * Return whether this window needs the menu key shown.  Must be called
-         * with window lock held, because it may need to traverse down through
-         * window list to determine the result.
-         * @param bottom The bottom-most window to consider when determining this.
-         */
-        public boolean getNeedsMenuLw(WindowState bottom);
-
-        /**
          * Retrieve the current system UI visibility flags associated with
          * this window.
          */
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 092a844..4e39dae 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -547,7 +547,7 @@
     private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
             new WindowState.UpdateReportedVisibilityResults();
 
-    private boolean mUseTransferredAnimation;
+    boolean mUseTransferredAnimation;
 
     /**
      * @see #currentLaunchCanTurnScreenOn()
@@ -2190,7 +2190,7 @@
     }
 
     /**
-     * Sets if this AWT is in the process of closing or entering PIP.
+     * Sets if this {@link ActivityRecord} is in the process of closing or entering PIP.
      * {@link #mWillCloseOrEnterPip}}
      */
     void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
@@ -2198,7 +2198,7 @@
     }
 
     /**
-     * Returns whether this AWT is considered closing. Conditions are either
+     * Returns whether this {@link ActivityRecord} is considered closing. Conditions are either
      * 1. Is this app animating and was requested to be hidden
      * 2. App is delayed closing since it might enter PIP.
      */
@@ -3000,7 +3000,7 @@
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);
 
-        boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+        commitVisibility(false /* visible */, true /* performLayout */);
 
         getDisplayContent().mOpeningApps.remove(this);
         getDisplayContent().mChangingApps.remove(this);
@@ -3008,6 +3008,8 @@
         mWmService.mTaskSnapshotController.onAppRemoved(this);
         mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
         waitingToShow = false;
+
+        boolean delayed = isAnimating(TRANSITION | CHILDREN);
         if (getDisplayContent().mClosingApps.contains(this)) {
             delayed = true;
         } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
@@ -3152,16 +3154,6 @@
         updateLetterboxSurface(child);
     }
 
-    private boolean waitingForReplacement() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState candidate = mChildren.get(i);
-            if (candidate.waitingForReplacement()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void onWindowReplacementTimeout() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             (mChildren.get(i)).onWindowReplacementTimeout();
@@ -3417,7 +3409,7 @@
         // before the non-exiting app tokens. So, we skip the exiting app tokens here.
         // TODO: Investigate if we need to continue to do this or if we can just process them
         // in-order.
-        if (mIsExiting && !waitingForReplacement()) {
+        if (mIsExiting && !forAllWindowsUnchecked(WindowState::waitingForReplacement, true)) {
             return false;
         }
         return forAllWindowsUnchecked(callback, traverseTopToBottom);
@@ -3862,6 +3854,17 @@
         }
     }
 
+    /**
+     * Set visibility on this {@link ActivityRecord}
+     *
+     * <p class="note"><strong>Note: </strong>This function might not update the visibility of
+     * this {@link ActivityRecord} immediately. In case we are preparing an app transition, we
+     * delay changing the visibility of this {@link ActivityRecord} until we execute that
+     * transition.</p>
+     *
+     * @param visible {@code true} if the {@link ActivityRecord} should become visible, otherwise
+     *                this should become invisible.
+     */
     void setVisibility(boolean visible) {
         if (getParent() == null) {
             Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
@@ -3998,153 +4001,169 @@
             return;
         }
 
-        commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+        commitVisibility(visible, true /* performLayout */);
         updateReportedVisibilityLocked();
     }
 
-    boolean commitVisibility(WindowManager.LayoutParams lp,
-            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
+    @Override
+    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+            boolean isVoiceInteraction) {
+        if (mUseTransferredAnimation) {
+            return false;
+        }
+        return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
+    }
 
-        boolean delayed = false;
-        // Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
+    /**
+     * Update visibility to this {@link ActivityRecord}.
+     *
+     * <p class="note"><strong>Note: </strong> Unlike {@link #setVisibility}, this immediately
+     * updates the visibility without starting an app transition. Since this function may start
+     * animation on {@link WindowState} depending on app transition animation status, an app
+     * transition animation must be started before calling this function if necessary.</p>
+     *
+     * @param visible {@code true} if this {@link ActivityRecord} should become visible, otherwise
+     *                this should become invisible.
+     * @param performLayout if {@code true}, perform surface placement after committing visibility.
+     */
+    void commitVisibility(boolean visible, boolean performLayout) {
+        // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
         // been set by the app now.
         mVisibleSetFromTransferredStartingWindow = false;
-
-        // Allow for state changes and animation to be applied if:
-        // * token is transitioning visibility state
-        // * or the token was marked as hidden and is exiting before we had a chance to play the
-        // transition animation
-        // * or this is an opening app and windows are being replaced
-        // * or the token is the opening app and visible while opening task behind existing one.
-        final DisplayContent displayContent = getDisplayContent();
-        boolean visibilityChanged = false;
-        if (isVisible() != visible || (!isVisible() && mIsExiting)
-                || (visible && waitingForReplacement())
-                || (visible && displayContent.mOpeningApps.contains(this)
-                && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
-            final AccessibilityController accessibilityController =
-                    mWmService.mAccessibilityController;
-            boolean changed = false;
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "Changing app %s visible=%b performLayout=%b", this, isVisible(),
-                    performLayout);
-
-            boolean runningAppAnimation = false;
-
-            if (transit != WindowManager.TRANSIT_UNSET) {
-                if (mUseTransferredAnimation) {
-                    runningAppAnimation = isAnimating();
-                } else if (applyAnimation(lp, transit, visible, isVoiceInteraction)) {
-                    runningAppAnimation = true;
-                }
-                delayed = runningAppAnimation;
-                final WindowState window = findMainWindow();
-                if (window != null && accessibilityController != null) {
-                    accessibilityController.onAppWindowTransitionLocked(window, transit);
-                }
-                changed = true;
-            }
-
-            final int windowsCount = mChildren.size();
-            for (int i = 0; i < windowsCount; i++) {
-                final WindowState win = mChildren.get(i);
-                changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
-            }
-
-            setVisible(visible);
-            mVisibleRequested = visible;
-            visibilityChanged = true;
-            if (!visible) {
-                stopFreezingScreen(true, true);
-            } else {
-                // If we are being set visible, and the starting window is not yet displayed,
-                // then make sure it doesn't get displayed.
-                if (startingWindow != null && !startingWindow.isDrawnLw()) {
-                    startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
-                    startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
-                }
-
-                // We are becoming visible, so better freeze the screen with the windows that are
-                // getting visible so we also wait for them.
-                forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
-            }
-
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                    "commitVisibility: %s: visible=%b visibleRequested=%b", this,
-                    isVisible(), mVisibleRequested);
-
-            if (changed) {
-                displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
-                if (performLayout) {
-                    mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
-                            false /*updateInputWindows*/);
-                    mWmService.mWindowPlacerLocked.performSurfacePlacement();
-                }
-                displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
-            }
+        if (visible == isVisible()) {
+            return;
         }
+
+        final int windowsCount = mChildren.size();
+        for (int i = 0; i < windowsCount; i++) {
+            mChildren.get(i).onAppVisibilityChanged(visible, isAnimating(PARENTS));
+        }
+        setVisible(visible);
+        mVisibleRequested = visible;
+        if (!visible) {
+            stopFreezingScreen(true, true);
+        } else {
+            // If we are being set visible, and the starting window is not yet displayed,
+            // then make sure it doesn't get displayed.
+            if (startingWindow != null && !startingWindow.isDrawnLw()) {
+                startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+                startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
+            }
+            // We are becoming visible, so better freeze the screen with the windows that are
+            // getting visible so we also wait for them.
+            forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
+        }
+        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
+                isVisible(), mVisibleRequested);
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
+        if (performLayout) {
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+                    false /*updateInputWindows*/);
+            mWmService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+        displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
         mUseTransferredAnimation = false;
 
-        delayed = isAnimating(CHILDREN);
+        postApplyAnimation(visible);
+    }
+
+    /**
+     * Post process after applying an app transition animation.
+     *
+     * <p class="note"><strong>Note: </strong> This function must be called after the animations
+     * have been applied and {@link #commitVisibility}.</p>
+     *
+     * @param visible {@code true} if this {@link ActivityRecord} has become visible, otherwise
+     *                this has become invisible.
+     */
+    private void postApplyAnimation(boolean visible) {
+        final boolean delayed = isAnimating(PARENTS | CHILDREN);
         if (!delayed) {
-            // We aren't animating anything, but exiting windows rely on the animation finished
-            // callback being called in case the ActivityRecord was pretending to be animating,
+            // We aren't delayed anything, but exiting windows rely on the animation finished
+            // callback being called in case the ActivityRecord was pretending to be delayed,
             // which we might have done because we were in closing/opening apps list.
             onAnimationFinished();
-        }
-
-        if (visibilityChanged) {
-            if (visible && !delayed) {
+            if (visible) {
                 // The token was made immediately visible, there will be no entrance animation.
                 // We need to inform the client the enter animation was finished.
                 mEnteringAnimation = true;
                 mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
                         token);
             }
+        }
 
-            // If we're becoming visible, immediately change client visibility as well. there seem
-            // to be some edge cases where we change our visibility but client visibility never gets
-            // updated.
-            // If we're becoming invisible, update the client visibility if we are not running an
-            // animation. Otherwise, we'll update client visibility in onAnimationFinished.
-            if (visible || !isAnimating()) {
-                setClientVisible(visible);
-            }
+        // If we're becoming visible, immediately change client visibility as well. there seem
+        // to be some edge cases where we change our visibility but client visibility never gets
+        // updated.
+        // If we're becoming invisible, update the client visibility if we are not running an
+        // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+        if (visible || !isAnimating(PARENTS)) {
+            setClientVisible(visible);
+        }
 
-            if (!displayContent.mClosingApps.contains(this)
-                    && !displayContent.mOpeningApps.contains(this)) {
-                // The token is not closing nor opening, so even if there is an animation set, that
-                // doesn't mean that it goes through the normal app transition cycle so we have
-                // to inform the docked controller about visibility change.
-                // TODO(multi-display): notify docked divider on all displays where visibility was
-                // affected.
-                displayContent.getDockedDividerController().notifyAppVisibilityChanged();
+        final DisplayContent displayContent = getDisplayContent();
+        if (!displayContent.mClosingApps.contains(this)
+                && !displayContent.mOpeningApps.contains(this)) {
+            // The token is not closing nor opening, so even if there is an animation set, that
+            // doesn't mean that it goes through the normal app transition cycle so we have
+            // to inform the docked controller about visibility change.
+            // TODO(multi-display): notify docked divider on all displays where visibility was
+            // affected.
+            displayContent.getDockedDividerController().notifyAppVisibilityChanged();
 
-                // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
-                // will not be taken.
-                mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
-            }
+            // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
+            // will not be taken.
+            mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+        }
 
-            // If we are hidden but there is no delay needed we immediately
-            // apply the Surface transaction so that the ActivityManager
-            // can have some guarantee on the Surface state following
-            // setting the visibility. This captures cases like dismissing
-            // the docked or pinned stack where there is no app transition.
-            //
-            // In the case of a "Null" animation, there will be
-            // no animation but there will still be a transition set.
-            // We still need to delay hiding the surface such that it
-            // can be synchronized with showing the next surface in the transition.
-            if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
-                SurfaceControl.openTransaction();
-                for (int i = mChildren.size() - 1; i >= 0; i--) {
-                    mChildren.get(i).mWinAnimator.hide("immediately hidden");
-                }
+        // If we are hidden but there is no delay needed we immediately
+        // apply the Surface transaction so that the ActivityManager
+        // can have some guarantee on the Surface state following
+        // setting the visibility. This captures cases like dismissing
+        // the docked or pinned stack where there is no app transition.
+        //
+        // In the case of a "Null" animation, there will be
+        // no animation but there will still be a transition set.
+        // We still need to delay hiding the surface such that it
+        // can be synchronized with showing the next surface in the transition.
+        if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+            SurfaceControl.openTransaction();
+            try {
+                forAllWindows(win -> {
+                    win.mWinAnimator.hide("immediately hidden"); }, true);
+            } finally {
                 SurfaceControl.closeTransaction();
             }
         }
+    }
 
-        return delayed;
+    /**
+     * Check if visibility of this {@link ActivityRecord} should be updated as part of an app
+     * transition.
+     *
+     * <p class="note><strong>Note:</strong> If the visibility of this {@link ActivityRecord} is
+     * already set to {@link #visible}, we don't need to update the visibility. So {@code false} is
+     * returned.</p>
+     *
+     * @param visible {@code true} if this {@link ActivityRecord} should become visible,
+     *                {@code false} if this should become invisible.
+     * @return {@code true} if visibility of this {@link ActivityRecord} should be updated, and
+     *         an app transition animation should be run.
+     */
+    boolean shouldApplyAnimation(boolean visible) {
+        // Allow for state update and animation to be applied if:
+        // * token is transitioning visibility state
+        // * or the token was marked as hidden and is exiting before we had a chance to play the
+        // transition animation
+        // * or this is an opening app and windows are being replaced
+        // * or the token is the opening app and visible while opening task behind existing one.
+        final DisplayContent displayContent = getDisplayContent();
+        return isVisible() != visible || (!isVisible() && mIsExiting)
+                || (visible && forAllWindows(WindowState::waitingForReplacement, true))
+                || (visible && displayContent.mOpeningApps.contains(this)
+                && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND);
     }
 
     /**
@@ -5635,19 +5654,6 @@
         return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
     }
 
-    @Override
-    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-            boolean isVoiceInteraction) {
-        if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
-                    "applyAnimation: transition animation is disabled or skipped. "
-                            + "container=%s", this);
-            cancelAnimation();
-            return false;
-        }
-        return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
-    }
-
     /**
      * Creates a layer to apply crop to an animation.
      */
@@ -5914,14 +5920,14 @@
     protected void onAnimationFinished() {
         super.onAnimationFinished();
 
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
         mTransit = TRANSIT_UNSET;
         mTransitFlags = 0;
         mNeedsZBoost = false;
         mNeedsAnimationBoundsLayer = false;
 
         setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
-                "AppWindowToken");
+                "ActivityRecord");
 
         clearThumbnail();
         setClientVisible(isVisible() || mVisibleRequested);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d7f4b34..6ea0650 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -48,6 +48,8 @@
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -61,6 +63,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionType;
 import android.view.animation.Animation;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -177,9 +180,15 @@
         final int layoutRedo;
         mService.mSurfaceAnimationRunner.deferStartingAnimations();
         try {
-            handleClosingApps(transit, animLp, voiceInteraction);
-            handleOpeningApps(transit, animLp, voiceInteraction);
-            handleChangingApps(transit, animLp, voiceInteraction);
+            // TODO: Apply an app transition animation on TaskStack instead of ActivityRecord when
+            //  appropriate.
+            applyAnimations(mDisplayContent.mClosingApps, transit, false /* visible */,
+                    animLp, voiceInteraction);
+            applyAnimations(mDisplayContent.mOpeningApps, transit, true /* visible */,
+                    animLp, voiceInteraction);
+            handleClosingApps();
+            handleOpeningApps();
+            handleChangingApps(transit);
 
             appTransition.setLastAppTransition(transit, topOpeningApp,
                     topClosingApp, topChangingApp);
@@ -227,8 +236,8 @@
         return mainWindow != null ? mainWindow.mAttrs : null;
     }
 
-    RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity, int transit,
-            ArraySet<Integer> activityTypes) {
+    RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity,
+            @TransitionType int transit, ArraySet<Integer> activityTypes) {
         final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
         if (definition != null) {
             final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
@@ -246,8 +255,8 @@
      * Overrides the pending transition with the remote animation defined for the transition in the
      * set of defined remote animations in the app window token.
      */
-    private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity, int transit,
-            ArraySet<Integer> activityTypes) {
+    private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity,
+            @TransitionType int transit, ArraySet<Integer> activityTypes) {
         if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
             // The crash transition has higher priority than any involved remote animations.
             return;
@@ -266,7 +275,7 @@
     /**
      * @return The window token that determines the animation theme.
      */
-    private ActivityRecord findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
+    private ActivityRecord findAnimLayoutParamsToken(@TransitionType int transit,
             ArraySet<Integer> activityTypes) {
         ActivityRecord result;
         final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
@@ -340,26 +349,60 @@
         return false;
     }
 
-    private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+    /**
+     * Apply an app transition animation on a set of {@link ActivityRecord}
+     *
+     * @param apps The list of apps to which an app transition animation applies.
+     * @param transit The current transition type.
+     * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
+     *                invisible.
+     * @param animLp Layout parameters in which an app transition animation runs.
+     * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
+     *                         interaction session driving task.
+     */
+    private void applyAnimations(ArraySet<ActivityRecord> apps, @TransitionType int transit,
+            boolean visible, LayoutParams animLp, boolean voiceInteraction) {
+        final int appsCount = apps.size();
+        for (int i = 0; i < appsCount; i++) {
+            final ActivityRecord app = apps.valueAt(i);
+            if (transit != WindowManager.TRANSIT_UNSET && app.shouldApplyAnimation(visible)) {
+                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Changing app %s visible=%b performLayout=%b",
+                        app, app.isVisible(), false);
+                if (!app.mUseTransferredAnimation) {
+                    app.applyAnimation(animLp, transit, visible, voiceInteraction);
+                }
+                final WindowState window = app.findMainWindow();
+                final AccessibilityController accessibilityController =
+                        app.mWmService.mAccessibilityController;
+                if (window != null && accessibilityController != null) {
+                    accessibilityController.onAppWindowTransitionLocked(window, transit);
+                }
+            }
+        }
+    }
+
+    private void handleOpeningApps() {
         final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
         final int appsCount = openingApps.size();
-        for (int i = 0; i < appsCount; i++) {
-            ActivityRecord wtoken = openingApps.valueAt(i);
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", wtoken);
 
-            if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
+        for (int i = 0; i < appsCount; i++) {
+            final ActivityRecord app = openingApps.valueAt(i);
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
+
+            app.commitVisibility(true /* visible */, false /* performLayout */);
+            if (!app.isAnimating(PARENTS | CHILDREN)) {
                 // This token isn't going to be animating. Add it to the list of tokens to
                 // be notified of app transition complete since the notification will not be
                 // sent be the app window animator.
-                mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
+                mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(app.token);
             }
-            wtoken.updateReportedVisibilityLocked();
-            wtoken.waitingToShow = false;
+            app.updateReportedVisibilityLocked();
+            app.waitingToShow = false;
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     ">>> OPEN TRANSACTION handleAppTransitionReady()");
             mService.openSurfaceTransaction();
             try {
-                wtoken.showAllWindowsLocked();
+                app.showAllWindowsLocked();
             } finally {
                 mService.closeSurfaceTransaction("handleAppTransitionReady");
                 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
@@ -367,41 +410,40 @@
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
-                wtoken.attachThumbnailAnimation();
+                app.attachThumbnailAnimation();
             } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
-                wtoken.attachCrossProfileAppsThumbnailAnimation();
+                app.attachCrossProfileAppsThumbnailAnimation();
             }
         }
     }
 
-    private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+    private void handleClosingApps() {
         final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
         final int appsCount = closingApps.size();
-        for (int i = 0; i < appsCount; i++) {
-            ActivityRecord wtoken = closingApps.valueAt(i);
 
-            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", wtoken);
-            // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
-            //       animating?
-            wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
-            wtoken.updateReportedVisibilityLocked();
+        for (int i = 0; i < appsCount; i++) {
+            final ActivityRecord app = closingApps.valueAt(i);
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);
+
+            app.commitVisibility(false /* visible */, false /* performLayout */);
+            app.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
             // this guy's animations regardless of whether it's
             // gotten drawn.
-            wtoken.allDrawn = true;
+            app.allDrawn = true;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
-                wtoken.removeStartingWindow();
+            if (app.startingWindow != null && !app.startingWindow.mAnimatingExit) {
+                app.removeStartingWindow();
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
-                wtoken.attachThumbnailAnimation();
+                app.attachThumbnailAnimation();
             }
         }
     }
 
-    private void handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
+    private void handleChangingApps(@TransitionType int transit) {
         final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
         final int appsCount = apps.size();
         for (int i = 0; i < appsCount; i++) {
@@ -419,7 +461,7 @@
         }
     }
 
-    private void handleNonAppWindowsInTransition(int transit, int flags) {
+    private void handleNonAppWindowsInTransition(@TransitionType int transit, int flags) {
         if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
                     && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
@@ -510,8 +552,8 @@
         return true;
     }
 
-    private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
-            boolean closingAppHasWallpaper) {
+    private int maybeUpdateTransitToWallpaper(@TransitionType int transit,
+            boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) {
         // Given no app transition pass it through instead of a wallpaper transition.
         // Never convert the crashing transition.
         // Never update the transition for the wallpaper if we are just docking from recents
@@ -604,7 +646,7 @@
      *         situation.
      */
     @VisibleForTesting
-    int maybeUpdateTransitToTranslucentAnim(int transit) {
+    int maybeUpdateTransitToTranslucentAnim(@TransitionType int transit) {
         if (AppTransition.isChangeTransit(transit)) {
             // There's no special animation to handle change animations with translucent apps
             return transit;
@@ -644,7 +686,7 @@
      * to determine whether animations should be clipped to the task bounds instead of stack bounds.
      */
     @VisibleForTesting
-    boolean isTransitWithinTask(int transit, Task task) {
+    boolean isTransitWithinTask(@TransitionType int transit, Task task) {
         if (task == null
                 || !mDisplayContent.mChangingApps.isEmpty()) {
             // if there is no task, then we can't constrain to the task.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bc8e718..aa0e973 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -55,8 +55,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
-import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -3266,38 +3264,6 @@
         return mWindowContainers.getSurfaceControl();
     }
 
-    boolean getNeedsMenu(WindowState top, WindowManagerPolicy.WindowState bottom) {
-        if (top.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
-            return top.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
-        }
-
-        // Used to indicate we have reached the first window in the range we are interested in.
-        mTmpWindow = null;
-
-        // TODO: Figure-out a more efficient way to do this.
-        final WindowState candidate = getWindow(w -> {
-            if (w == top) {
-                // Reached the first window in the range we are interested in.
-                mTmpWindow = w;
-            }
-            if (mTmpWindow == null) {
-                return false;
-            }
-
-            if (w.mAttrs.needsMenuKey != NEEDS_MENU_UNSET) {
-                return true;
-            }
-            // If we reached the bottom of the range of windows we are considering,
-            // assume no menu is needed.
-            if (w == bottom) {
-                return true;
-            }
-            return false;
-        });
-
-        return candidate != null && candidate.mAttrs.needsMenuKey == NEEDS_MENU_SET_TRUE;
-    }
-
     void setLayoutNeeded() {
         if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
         mLayoutNeeded = true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6a1aa02..5b7f36d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1239,11 +1239,6 @@
     }
 
     @Override
-    public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
-        return getDisplayContent().getNeedsMenu(this, bottom);
-    }
-
-    @Override
     public int getSystemUiVisibility() {
         return mSystemUiVisibility;
     }
@@ -1738,28 +1733,22 @@
         super.onMovedByResize();
     }
 
-    boolean onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
-        boolean changed = false;
-
+    void onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = mChildren.get(i);
-            changed |= c.onAppVisibilityChanged(visible, runningAppAnimation);
+            mChildren.get(i).onAppVisibilityChanged(visible, runningAppAnimation);
         }
 
+        final boolean isVisibleNow = isVisibleNow();
         if (mAttrs.type == TYPE_APPLICATION_STARTING) {
             // Starting window that's exiting will be removed when the animation finishes.
             // Mark all relevant flags for that onExitAnimationDone will proceed all the way
             // to actually remove it.
-            if (!visible && isVisibleNow() && mActivityRecord.isAnimating(TRANSITION)) {
+            if (!visible && isVisibleNow && mActivityRecord.isAnimating(TRANSITION)) {
                 mAnimatingExit = true;
                 mRemoveOnExit = true;
                 mWindowRemovalAllowed = true;
             }
-            return changed;
-        }
-
-        final boolean isVisibleNow = isVisibleNow();
-        if (visible != isVisibleNow) {
+        } else if (visible != isVisibleNow) {
             // Run exit animation if:
             // 1. App visibility and WS visibility are different
             // 2. App is not running an animation
@@ -1773,11 +1762,8 @@
                     accessibilityController.onWindowTransitionLocked(this, winTransit);
                 }
             }
-            changed = true;
             setDisplayLayoutNeeded();
         }
-
-        return changed;
     }
 
     boolean onSetAppExiting() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 0382bf8..6f1e6df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -31,7 +31,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_UNSET;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
@@ -303,8 +302,8 @@
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
         closingWindow.mRemoveOnExit = true;
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
 
         // We pretended that we were running an exit animation, but that should have been cleared up
         // by changing visibility of ActivityRecord
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 6ebaf29..7174e5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.TRANSIT_UNSET;
 
 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME;
 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
@@ -50,8 +49,8 @@
     public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
@@ -66,10 +65,10 @@
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
                 FIRST_APPLICATION_WINDOW, "openingWindow");
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
-        openingWindow.mActivityRecord.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
+        openingWindow.mActivityRecord.commitVisibility(
+                true /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
@@ -81,8 +80,8 @@
     public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
-                true /* performLayout */, false /* isVoiceInteraction */);
+        closingWindow.mActivityRecord.commitVisibility(
+                false /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();