Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 97974eb

Browse files
Fix resize crash in Android virtual display (#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 flutter/flutter#114095
1 parent f4ca3e7 commit 97974eb

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ public void resize(
314314
final int viewId = request.viewId;
315315

316316
if (usesVirtualDisplay(viewId)) {
317+
final float originalDisplayDensity = getDisplayDensity();
317318
final VirtualDisplayController vdController = vdControllers.get(viewId);
318319
// Resizing involved moving the platform view to a new virtual display. Doing so
319320
// potentially results in losing an active input connection. To make sure we preserve
@@ -325,10 +326,15 @@ public void resize(
325326
physicalHeight,
326327
() -> {
327328
unlockInputConnection(vdController);
329+
// Converting back to logic pixels requires a context, which may no longer be
330+
// available. If that happens, assume the same logic/physical relationship as
331+
// was present when the request arrived.
332+
final float displayDensity =
333+
context == null ? originalDisplayDensity : getDisplayDensity();
328334
onComplete.run(
329335
new PlatformViewsChannel.PlatformViewBufferSize(
330-
toLogicalPixels(vdController.getBufferWidth()),
331-
toLogicalPixels(vdController.getBufferHeight())));
336+
toLogicalPixels(vdController.getBufferWidth(), displayDensity),
337+
toLogicalPixels(vdController.getBufferHeight(), displayDensity)));
332338
});
333339
return;
334340
}
@@ -1002,8 +1008,12 @@ private int toPhysicalPixels(double logicalPixels) {
10021008
return (int) Math.round(logicalPixels * getDisplayDensity());
10031009
}
10041010

1011+
private int toLogicalPixels(double physicalPixels, float displayDensity) {
1012+
return (int) Math.round(physicalPixels / displayDensity);
1013+
}
1014+
10051015
private int toLogicalPixels(double physicalPixels) {
1006-
return (int) Math.round(physicalPixels / getDisplayDensity());
1016+
return toLogicalPixels(physicalPixels, getDisplayDensity());
10071017
}
10081018

10091019
private void diposeAllViews() {

shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,32 @@ public void itCancelsOldPresentationOnResize() {
124124
assertEquals(presentation.isShowing(), false);
125125
}
126126

127+
@Test
128+
@Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
129+
public void virtualDisplay_handlesResizeResponseWithoutContext() {
130+
final int platformViewId = 0;
131+
FlutterView fakeFlutterView = new FlutterView(ApplicationProvider.getApplicationContext());
132+
VirtualDisplayController fakeVdController = mock(VirtualDisplayController.class);
133+
PlatformViewsController platformViewsController = new PlatformViewsController();
134+
platformViewsController.vdControllers.put(platformViewId, fakeVdController);
135+
136+
platformViewsController.attachToView(fakeFlutterView);
137+
138+
FlutterJNI jni = new FlutterJNI();
139+
attach(jni, platformViewsController);
140+
141+
resize(jni, platformViewsController, platformViewId, 10.0, 20.0);
142+
143+
ArgumentCaptor<Runnable> resizeCallbackCaptor = ArgumentCaptor.forClass(Runnable.class);
144+
verify(fakeVdController, times(1)).resize(anyInt(), anyInt(), resizeCallbackCaptor.capture());
145+
146+
// Simulate a detach call before the resize completes.
147+
platformViewsController.detach();
148+
149+
// Trigger the callback to ensure that it doesn't crash.
150+
resizeCallbackCaptor.getValue().run();
151+
}
152+
127153
@Test
128154
public void itUsesActionEventTypeFromFrameworkEventForVirtualDisplays() {
129155
MotionEventTracker motionEventTracker = MotionEventTracker.getInstance();

0 commit comments

Comments
 (0)