[Re-land] Remove WindowManager reflection in SingleViewPresentation.java (#50890)

relands https://github.com/flutter/engine/pull/49996

Context b/326363243

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 97d4270..b8b483f 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -36291,8 +36291,11 @@
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java + ../../../flutter/LICENSE
@@ -39156,10 +39159,13 @@
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SurfaceProducerPlatformViewRenderTarget.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java
+FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java
 FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java
diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn
index 8d7d5e9..97eaf28 100644
--- a/shell/platform/android/BUILD.gn
+++ b/shell/platform/android/BUILD.gn
@@ -324,10 +324,13 @@
   "io/flutter/plugin/platform/PlatformViewWrapper.java",
   "io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java",
   "io/flutter/plugin/platform/PlatformViewsController.java",
+  "io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java",
   "io/flutter/plugin/platform/SingleViewPresentation.java",
+  "io/flutter/plugin/platform/SingleViewWindowManager.java",
   "io/flutter/plugin/platform/SurfaceProducerPlatformViewRenderTarget.java",
   "io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java",
   "io/flutter/plugin/platform/VirtualDisplayController.java",
+  "io/flutter/plugin/platform/WindowManagerHandler.java",
   "io/flutter/plugin/text/ProcessTextPlugin.java",
   "io/flutter/util/HandlerCompat.java",
   "io/flutter/util/PathUtils.java",
diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java
new file mode 100644
index 0000000..fa16b52
--- /dev/null
+++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java
@@ -0,0 +1,65 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugin.platform;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+/*
+ * A view group that implements the same layout protocol that exist between the WindowManager and its direct
+ * children.
+ *
+ * Currently only a subset of the protocol is supported (gravity, x, and y).
+ */
+class SingleViewFakeWindowViewGroup extends ViewGroup {
+  // Used in onLayout to keep the bounds of the current view.
+  // We keep it as a member to avoid object allocations during onLayout which are discouraged.
+  private final Rect viewBounds;
+
+  // Used in onLayout to keep the bounds of the child views.
+  // We keep it as a member to avoid object allocations during onLayout which are discouraged.
+  private final Rect childRect;
+
+  public SingleViewFakeWindowViewGroup(Context context) {
+    super(context);
+    viewBounds = new Rect();
+    childRect = new Rect();
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int l, int t, int r, int b) {
+    for (int i = 0; i < getChildCount(); i++) {
+      View child = getChildAt(i);
+      WindowManager.LayoutParams params = (WindowManager.LayoutParams) child.getLayoutParams();
+      viewBounds.set(l, t, r, b);
+      Gravity.apply(
+          params.gravity,
+          child.getMeasuredWidth(),
+          child.getMeasuredHeight(),
+          viewBounds,
+          params.x,
+          params.y,
+          childRect);
+      child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
+    }
+  }
+
+  @Override
+  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    for (int i = 0; i < getChildCount(); i++) {
+      View child = getChildAt(i);
+      child.measure(atMost(widthMeasureSpec), atMost(heightMeasureSpec));
+    }
+    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+  }
+
+  private static int atMost(int measureSpec) {
+    return MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(measureSpec), MeasureSpec.AT_MOST);
+  }
+}
diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java
index c272f85..82a8013 100644
--- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java
+++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java
@@ -12,13 +12,10 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.MutableContextWrapper;
-import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.view.Display;
-import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.InputMethodManager;
@@ -27,10 +24,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import io.flutter.Log;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 
 /*
  * A presentation used for hosting a single Android view in a virtual display.
@@ -68,7 +61,7 @@
 
     // Contains views that were added directly to the window manager (e.g
     // android.widget.PopupWindow).
-    private FakeWindowViewGroup fakeWindowViewGroup;
+    private SingleViewFakeWindowViewGroup fakeWindowViewGroup;
   }
 
   // A reference to the current accessibility bridge to which accessibility events will be
@@ -153,7 +146,7 @@
     // This makes sure we preserve alpha for the VD's content.
     getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
     if (state.fakeWindowViewGroup == null) {
-      state.fakeWindowViewGroup = new FakeWindowViewGroup(getContext());
+      state.fakeWindowViewGroup = new SingleViewFakeWindowViewGroup(getContext());
     }
     if (state.windowManagerHandler == null) {
       WindowManager windowManagerDelegate =
@@ -223,59 +216,6 @@
     return state.platformView;
   }
 
-  /*
-   * A view group that implements the same layout protocol that exist between the WindowManager and its direct
-   * children.
-   *
-   * Currently only a subset of the protocol is supported (gravity, x, and y).
-   */
-  static class FakeWindowViewGroup extends ViewGroup {
-    // Used in onLayout to keep the bounds of the current view.
-    // We keep it as a member to avoid object allocations during onLayout which are discouraged.
-    private final Rect viewBounds;
-
-    // Used in onLayout to keep the bounds of the child views.
-    // We keep it as a member to avoid object allocations during onLayout which are discouraged.
-    private final Rect childRect;
-
-    public FakeWindowViewGroup(Context context) {
-      super(context);
-      viewBounds = new Rect();
-      childRect = new Rect();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-      for (int i = 0; i < getChildCount(); i++) {
-        View child = getChildAt(i);
-        WindowManager.LayoutParams params = (WindowManager.LayoutParams) child.getLayoutParams();
-        viewBounds.set(l, t, r, b);
-        Gravity.apply(
-            params.gravity,
-            child.getMeasuredWidth(),
-            child.getMeasuredHeight(),
-            viewBounds,
-            params.x,
-            params.y,
-            childRect);
-        child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
-      }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-      for (int i = 0; i < getChildCount(); i++) {
-        View child = getChildAt(i);
-        child.measure(atMost(widthMeasureSpec), atMost(heightMeasureSpec));
-      }
-      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    private static int atMost(int measureSpec) {
-      return MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(measureSpec), MeasureSpec.AT_MOST);
-    }
-  }
-
   /** Answers calls for {@link InputMethodManager} with an instance cached at creation time. */
   // TODO(mklim): This caches the IMM at construction time and won't pick up any changes. In rare
   // cases where the FlutterView changes windows this will return an outdated instance. This
@@ -354,7 +294,7 @@
 
     private WindowManager getWindowManager() {
       if (windowManager == null) {
-        windowManager = windowManagerHandler.getWindowManager();
+        windowManager = windowManagerHandler;
       }
       return windowManager;
     }
@@ -371,101 +311,6 @@
     }
   }
 
-  /*
-   * A dynamic proxy handler for a WindowManager with custom overrides.
-   *
-   * The presentation's window manager delegates all calls to the default window manager.
-   * WindowManager#addView calls triggered by views that are attached to the virtual display are crashing
-   * (see: https://github.com/flutter/flutter/issues/20714). This was triggered when selecting text in an embedded
-   * WebView (as the selection handles are implemented as popup windows).
-   *
-   * This dynamic proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout methods
-   * to prevent these crashes.
-   *
-   * This will be more efficient as a static proxy that's not using reflection, but as the engine is currently
-   * not being built against the latest Android SDK we cannot override all relevant method.
-   * Tracking issue for upgrading the engine's Android sdk: https://github.com/flutter/flutter/issues/20717
-   */
-  static class WindowManagerHandler implements InvocationHandler {
-    private static final String TAG = "PlatformViewsController";
-
-    private final WindowManager delegate;
-    FakeWindowViewGroup fakeWindowRootView;
-
-    WindowManagerHandler(WindowManager delegate, FakeWindowViewGroup fakeWindowViewGroup) {
-      this.delegate = delegate;
-      fakeWindowRootView = fakeWindowViewGroup;
-    }
-
-    public WindowManager getWindowManager() {
-      return (WindowManager)
-          Proxy.newProxyInstance(
-              WindowManager.class.getClassLoader(), new Class<?>[] {WindowManager.class}, this);
-    }
-
-    @Override
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-      switch (method.getName()) {
-        case "addView":
-          addView(args);
-          return null;
-        case "removeView":
-          removeView(args);
-          return null;
-        case "removeViewImmediate":
-          removeViewImmediate(args);
-          return null;
-        case "updateViewLayout":
-          updateViewLayout(args);
-          return null;
-      }
-      try {
-        return method.invoke(delegate, args);
-      } catch (InvocationTargetException e) {
-        throw e.getCause();
-      }
-    }
-
-    private void addView(Object[] args) {
-      if (fakeWindowRootView == null) {
-        Log.w(TAG, "Embedded view called addView while detached from presentation");
-        return;
-      }
-      View view = (View) args[0];
-      WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1];
-      fakeWindowRootView.addView(view, layoutParams);
-    }
-
-    private void removeView(Object[] args) {
-      if (fakeWindowRootView == null) {
-        Log.w(TAG, "Embedded view called removeView while detached from presentation");
-        return;
-      }
-      View view = (View) args[0];
-      fakeWindowRootView.removeView(view);
-    }
-
-    private void removeViewImmediate(Object[] args) {
-      if (fakeWindowRootView == null) {
-        Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation");
-        return;
-      }
-      View view = (View) args[0];
-      view.clearAnimation();
-      fakeWindowRootView.removeView(view);
-    }
-
-    private void updateViewLayout(Object[] args) {
-      if (fakeWindowRootView == null) {
-        Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation");
-        return;
-      }
-      View view = (View) args[0];
-      WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1];
-      fakeWindowRootView.updateViewLayout(view, layoutParams);
-    }
-  }
-
   private static class AccessibilityDelegatingFrameLayout extends FrameLayout {
     private final AccessibilityEventsDelegate accessibilityEventsDelegate;
     private final View embeddedView;
diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java
new file mode 100644
index 0000000..ad004ac
--- /dev/null
+++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java
@@ -0,0 +1,127 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugin.platform;
+
+import android.os.Build;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import io.flutter.Log;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A static proxy handler for a WindowManager with custom overrides.
+ *
+ * <p>The presentation's window manager delegates all calls to the default window manager.
+ * WindowManager#addView calls triggered by views that are attached to the virtual display are
+ * crashing (see: https://github.com/flutter/flutter/issues/20714). This was triggered when
+ * selecting text in an embedded WebView (as the selection handles are implemented as popup
+ * windows).
+ *
+ * <p>This static proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout
+ * methods to prevent these crashes, and forwards all other calls to the delegate.
+ *
+ * <p>This is an abstract class because some clients of Flutter compile the Android embedding with
+ * the Android System SDK, which has additional abstract methods that need to be overriden.
+ */
+abstract class SingleViewWindowManager implements WindowManager {
+  private static final String TAG = "PlatformViewsController";
+
+  final WindowManager delegate;
+  SingleViewFakeWindowViewGroup fakeWindowRootView;
+
+  SingleViewWindowManager(
+      WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) {
+    this.delegate = delegate;
+    fakeWindowRootView = fakeWindowViewGroup;
+  }
+
+  @Override
+  @Deprecated
+  public Display getDefaultDisplay() {
+    return delegate.getDefaultDisplay();
+  }
+
+  @Override
+  public void removeViewImmediate(View view) {
+    if (fakeWindowRootView == null) {
+      Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation");
+      return;
+    }
+    view.clearAnimation();
+    fakeWindowRootView.removeView(view);
+  }
+
+  @Override
+  public void addView(View view, ViewGroup.LayoutParams params) {
+    if (fakeWindowRootView == null) {
+      Log.w(TAG, "Embedded view called addView while detached from presentation");
+      return;
+    }
+    fakeWindowRootView.addView(view, params);
+  }
+
+  @Override
+  public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+    if (fakeWindowRootView == null) {
+      Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation");
+      return;
+    }
+    fakeWindowRootView.updateViewLayout(view, params);
+  }
+
+  @Override
+  public void removeView(View view) {
+    if (fakeWindowRootView == null) {
+      Log.w(TAG, "Embedded view called removeView while detached from presentation");
+      return;
+    }
+    fakeWindowRootView.removeView(view);
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.R)
+  @NonNull
+  @Override
+  public WindowMetrics getCurrentWindowMetrics() {
+    return delegate.getCurrentWindowMetrics();
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.R)
+  @NonNull
+  @Override
+  public WindowMetrics getMaximumWindowMetrics() {
+    return delegate.getMaximumWindowMetrics();
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.S)
+  @Override
+  public boolean isCrossWindowBlurEnabled() {
+    return delegate.isCrossWindowBlurEnabled();
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.S)
+  @Override
+  public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+    delegate.addCrossWindowBlurEnabledListener(listener);
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.S)
+  @Override
+  public void addCrossWindowBlurEnabledListener(
+      @NonNull Executor executor, @NonNull Consumer<Boolean> listener) {
+    delegate.addCrossWindowBlurEnabledListener(executor, listener);
+  }
+
+  @RequiresApi(api = Build.VERSION_CODES.S)
+  @Override
+  public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
+    delegate.removeCrossWindowBlurEnabledListener(listener);
+  }
+}
diff --git a/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java
new file mode 100644
index 0000000..5072420
--- /dev/null
+++ b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java
@@ -0,0 +1,15 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugin.platform;
+
+import android.view.WindowManager;
+
+/** Default implementation when using the regular Android SDK. */
+final class WindowManagerHandler extends SingleViewWindowManager {
+
+  WindowManagerHandler(WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) {
+    super(delegate, fakeWindowViewGroup);
+  }
+}
diff --git a/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java
new file mode 100644
index 0000000..c7fd1d3
--- /dev/null
+++ b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java
@@ -0,0 +1,135 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugin.platform;
+
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import android.annotation.TargetApi;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@Config(manifest = Config.NONE)
+@RunWith(AndroidJUnit4.class)
+@TargetApi(P)
+public class WindowManagerHandlerTest {
+  @Test
+  @Config(minSdk = R)
+  public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() {
+    // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler.
+    WindowManager mockWindowManager = mock(WindowManager.class);
+    SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup =
+        mock(SingleViewFakeWindowViewGroup.class);
+
+    View mockView = mock(View.class);
+    ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class);
+
+    WindowManagerHandler windowManagerHandler =
+        new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup);
+
+    // removeViewImmediate
+    windowManagerHandler.removeViewImmediate(mockView);
+    verify(mockView).clearAnimation();
+    verify(mockSingleViewFakeWindowViewGroup).removeView(mockView);
+    verifyNoInteractions(mockWindowManager);
+
+    // addView
+    windowManagerHandler.addView(mockView, mockLayoutParams);
+    verify(mockSingleViewFakeWindowViewGroup).addView(mockView, mockLayoutParams);
+    verifyNoInteractions(mockWindowManager);
+
+    // updateViewLayout
+    windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
+    verify(mockSingleViewFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams);
+    verifyNoInteractions(mockWindowManager);
+
+    // removeView
+    windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
+    verify(mockSingleViewFakeWindowViewGroup).removeView(mockView);
+    verifyNoInteractions(mockWindowManager);
+  }
+
+  @Test
+  @Config(minSdk = R)
+  public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() {
+    // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler.
+    WindowManager mockWindowManager = mock(WindowManager.class);
+
+    View mockView = mock(View.class);
+    ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class);
+
+    WindowManagerHandler windowManagerHandler = new WindowManagerHandler(mockWindowManager, null);
+
+    // removeViewImmediate
+    windowManagerHandler.removeViewImmediate(mockView);
+    verifyNoInteractions(mockView);
+    verifyNoInteractions(mockWindowManager);
+
+    // addView
+    windowManagerHandler.addView(mockView, mockLayoutParams);
+    verifyNoInteractions(mockWindowManager);
+
+    // updateViewLayout
+    windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
+    verifyNoInteractions(mockWindowManager);
+
+    // removeView
+    windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
+    verifyNoInteractions(mockWindowManager);
+  }
+
+  // This section tests that WindowManagerHandler forwards all of the non-special case calls to the
+  // delegate WindowManager. Because this must include some deprecated WindowManager method calls
+  // (because the proxy overrides every method), we suppress deprecation warnings here.
+  @Test
+  @Config(minSdk = S)
+  @SuppressWarnings("deprecation")
+  public void windowManagerHandler_forwardsAllOtherCallsToDelegate() {
+    // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler.
+    WindowManager mockWindowManager = mock(WindowManager.class);
+    SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup =
+        mock(SingleViewFakeWindowViewGroup.class);
+
+    WindowManagerHandler windowManagerHandler =
+        new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup);
+
+    // Verify that all other calls get forwarded to the delegate.
+    Executor mockExecutor = mock(Executor.class);
+    @SuppressWarnings("Unchecked cast")
+    Consumer<Boolean> mockListener = (Consumer<Boolean>) mock(Consumer.class);
+
+    windowManagerHandler.getDefaultDisplay();
+    verify(mockWindowManager).getDefaultDisplay();
+
+    windowManagerHandler.getCurrentWindowMetrics();
+    verify(mockWindowManager).getCurrentWindowMetrics();
+
+    windowManagerHandler.getMaximumWindowMetrics();
+    verify(mockWindowManager).getMaximumWindowMetrics();
+
+    windowManagerHandler.isCrossWindowBlurEnabled();
+    verify(mockWindowManager).isCrossWindowBlurEnabled();
+
+    windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener);
+    verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener);
+
+    windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener);
+    verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener);
+
+    windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener);
+    verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener);
+  }
+}