Fix resize crash in Android virtual display (flutter/engine#37329)

In the Virtual Display codepath for Android platform views, resize
completes asynchronously. Currently it is attempting to access the
Context in the completion handler, but there is no guarantee that it
is still present at that point, leading to possible null pointer
crashes.

This adds a check for the current state of the Context, and uses a
fallback if it's not available.

Fixes https://github.com/flutter/flutter/issues/114095
This commit is contained in:
stuartmorgan
2022-11-16 14:06:30 -05:00
committed by GitHub
parent a1255b6105
commit 7bbe571f14
2 changed files with 39 additions and 3 deletions

View File

@@ -314,6 +314,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
final int viewId = request.viewId;
if (usesVirtualDisplay(viewId)) {
final float originalDisplayDensity = getDisplayDensity();
final VirtualDisplayController vdController = vdControllers.get(viewId);
// Resizing involved moving the platform view to a new virtual display. Doing so
// potentially results in losing an active input connection. To make sure we preserve
@@ -325,10 +326,15 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
physicalHeight,
() -> {
unlockInputConnection(vdController);
// Converting back to logic pixels requires a context, which may no longer be
// available. If that happens, assume the same logic/physical relationship as
// was present when the request arrived.
final float displayDensity =
context == null ? originalDisplayDensity : getDisplayDensity();
onComplete.run(
new PlatformViewsChannel.PlatformViewBufferSize(
toLogicalPixels(vdController.getBufferWidth()),
toLogicalPixels(vdController.getBufferHeight())));
toLogicalPixels(vdController.getBufferWidth(), displayDensity),
toLogicalPixels(vdController.getBufferHeight(), displayDensity)));
});
return;
}
@@ -1002,8 +1008,12 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
return (int) Math.round(logicalPixels * getDisplayDensity());
}
private int toLogicalPixels(double physicalPixels, float displayDensity) {
return (int) Math.round(physicalPixels / displayDensity);
}
private int toLogicalPixels(double physicalPixels) {
return (int) Math.round(physicalPixels / getDisplayDensity());
return toLogicalPixels(physicalPixels, getDisplayDensity());
}
private void diposeAllViews() {

View File

@@ -124,6 +124,32 @@ public class PlatformViewsControllerTest {
assertEquals(presentation.isShowing(), false);
}
@Test
@Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
public void virtualDisplay_handlesResizeResponseWithoutContext() {
final int platformViewId = 0;
FlutterView fakeFlutterView = new FlutterView(ApplicationProvider.getApplicationContext());
VirtualDisplayController fakeVdController = mock(VirtualDisplayController.class);
PlatformViewsController platformViewsController = new PlatformViewsController();
platformViewsController.vdControllers.put(platformViewId, fakeVdController);
platformViewsController.attachToView(fakeFlutterView);
FlutterJNI jni = new FlutterJNI();
attach(jni, platformViewsController);
resize(jni, platformViewsController, platformViewId, 10.0, 20.0);
ArgumentCaptor<Runnable> resizeCallbackCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(fakeVdController, times(1)).resize(anyInt(), anyInt(), resizeCallbackCaptor.capture());
// Simulate a detach call before the resize completes.
platformViewsController.detach();
// Trigger the callback to ensure that it doesn't crash.
resizeCallbackCaptor.getValue().run();
}
@Test
public void itUsesActionEventTypeFromFrameworkEventForVirtualDisplays() {
MotionEventTracker motionEventTracker = MotionEventTracker.getInstance();