Skip to content

Commit

Permalink
[Re-land] Remove WindowManager reflection in SingleViewPresentation.j…
Browse files Browse the repository at this point in the history
…ava (flutter#50890)

relands flutter#49996

Context b/326363243

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
gmackall authored Mar 4, 2024
1 parent 728aa4e commit bd01246
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 158 deletions.
6 changes: 6 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -36291,8 +36291,11 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platf
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
Expand Down Expand Up @@ -39156,10 +39159,13 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor
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
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,13 @@ android_java_sources = [
"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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -68,7 +61,7 @@ static class PresentationState {

// 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
Expand Down Expand Up @@ -153,7 +146,7 @@ protected void onCreate(Bundle savedInstanceState) {
// 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 =
Expand Down Expand Up @@ -223,59 +216,6 @@ public PlatformView getView() {
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
Expand Down Expand Up @@ -354,7 +294,7 @@ public Object getSystemService(String name) {

private WindowManager getWindowManager() {
if (windowManager == null) {
windowManager = windowManagerHandler.getWindowManager();
windowManager = windowManagerHandler;
}
return windowManager;
}
Expand All @@ -371,101 +311,6 @@ private boolean isCalledFromAlertDialog() {
}
}

/*
* 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;
Expand Down
Loading

0 comments on commit bd01246

Please sign in to comment.