[android] release background image readers on <= Android 14 (#171193)

Fixes the version of https://github.com/flutter/flutter/issues/162147
that repros on 3.32 (but not the version that the original submitter
experiences on 3.27 - I've not been able to reproduce that issue). See
https://github.com/flutter/flutter/issues/162147#issuecomment-3006117342

Result of bisecting https://github.com/flutter/flutter/issues/162147 to
https://github.com/flutter/flutter/pull/165942

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Gray Mackall <mackall@google.com>
This commit is contained in:
Gray Mackall 2025-07-07 14:05:12 -07:00 committed by GitHub
parent 9de6f68267
commit df21e0b0ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 128 additions and 80 deletions

View File

@ -78,7 +78,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
private FlutterJNI flutterJNI = null;
// The texture registry maintaining the textures into which the embedded views will be rendered.
@Nullable private TextureRegistry textureRegistry;
@VisibleForTesting @Nullable TextureRegistry textureRegistry;
@Nullable private TextInputPlugin textInputPlugin;
@ -999,7 +999,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
TextureRegistry textureRegistry) {
if (enableSurfaceProducerRenderTarget && Build.VERSION.SDK_INT >= API_LEVELS.API_29) {
TextureRegistry.SurfaceLifecycle lifecycle =
Build.VERSION.SDK_INT == API_LEVELS.API_34
Build.VERSION.SDK_INT <= API_LEVELS.API_34
? TextureRegistry.SurfaceLifecycle.resetInBackground
: TextureRegistry.SurfaceLifecycle.manual;
final TextureRegistry.SurfaceProducer textureEntry =

View File

@ -106,7 +106,9 @@ public class PlatformViewsControllerTest {
}
@Test
@Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
@Config(
shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class},
minSdk = 35)
public void itRemovesPlatformViewBeforeDiposeIsCalled() {
PlatformViewsController platformViewsController = new PlatformViewsController();
FlutterJNI jni = new FlutterJNI();
@ -141,11 +143,56 @@ public class PlatformViewsControllerTest {
assertTrue(pView instanceof CountingPlatformView);
CountingPlatformView cpv = (CountingPlatformView) pView;
platformViewsController.configureForTextureLayerComposition(pView, request);
verify(platformViewsController.textureRegistry, times(1))
.createSurfaceProducer(TextureRegistry.SurfaceLifecycle.manual);
assertEquals(0, cpv.disposeCalls);
platformViewsController.disposePlatformView(viewId);
assertEquals(1, cpv.disposeCalls);
}
@Test
@Config(
shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class},
minSdk = 29,
maxSdk = 34)
public void itPassesSurfaceLifecyleResetInBackgroundLeqApi34() {
PlatformViewsController platformViewsController = new PlatformViewsController();
FlutterJNI jni = new FlutterJNI();
platformViewsController.setFlutterJNI(jni);
attach(jni, platformViewsController);
// Get the platform view registry.
PlatformViewRegistry registry = platformViewsController.getRegistry();
// Register a factory for our platform view.
registry.registerViewFactory(
CountingPlatformView.VIEW_TYPE_ID,
new PlatformViewFactory(StandardMessageCodec.INSTANCE) {
@Override
public PlatformView create(Context context, int viewId, Object args) {
return new CountingPlatformView(context);
}
});
// Create the platform view.
int viewId = 0;
final PlatformViewsChannel.PlatformViewCreationRequest request =
new PlatformViewsChannel.PlatformViewCreationRequest(
viewId,
CountingPlatformView.VIEW_TYPE_ID,
0,
0,
128,
128,
View.LAYOUT_DIRECTION_LTR,
null);
PlatformView pView = platformViewsController.createPlatformView(request, true);
assertTrue(pView instanceof CountingPlatformView);
CountingPlatformView cpv = (CountingPlatformView) pView;
platformViewsController.configureForTextureLayerComposition(pView, request);
verify(platformViewsController.textureRegistry, times(1))
.createSurfaceProducer(TextureRegistry.SurfaceLifecycle.resetInBackground);
}
@Test
@Config(shadows = {ShadowFlutterJNI.class, ShadowPlatformTaskQueue.class})
public void itNotifiesPlatformViewsOfEngineAttachmentAndDetachment() {
@ -1609,100 +1656,101 @@ public class PlatformViewsControllerTest {
final Context context = ApplicationProvider.getApplicationContext();
final TextureRegistry registry =
new TextureRegistry() {
public void TextureRegistry() {}
spy(
new TextureRegistry() {
public void TextureRegistry() {}
@NonNull
@Override
public SurfaceTextureEntry createSurfaceTexture() {
return registerSurfaceTexture(mock(SurfaceTexture.class));
}
@NonNull
@Override
public SurfaceTextureEntry registerSurfaceTexture(
@NonNull SurfaceTexture surfaceTexture) {
return new SurfaceTextureEntry() {
@NonNull
@Override
public SurfaceTexture surfaceTexture() {
return mock(SurfaceTexture.class);
public SurfaceTextureEntry createSurfaceTexture() {
return registerSurfaceTexture(mock(SurfaceTexture.class));
}
@NonNull
@Override
public long id() {
return 0;
public SurfaceTextureEntry registerSurfaceTexture(
@NonNull SurfaceTexture surfaceTexture) {
return new SurfaceTextureEntry() {
@NonNull
@Override
public SurfaceTexture surfaceTexture() {
return mock(SurfaceTexture.class);
}
@Override
public long id() {
return 0;
}
@Override
public void release() {}
};
}
@NonNull
@Override
public void release() {}
};
}
public ImageTextureEntry createImageTexture() {
return new ImageTextureEntry() {
@Override
public long id() {
return 0;
}
@NonNull
@Override
public ImageTextureEntry createImageTexture() {
return new ImageTextureEntry() {
@Override
public long id() {
return 0;
@Override
public void release() {}
@Override
public void pushImage(Image image) {}
};
}
@NonNull
@Override
public void release() {}
public SurfaceProducer createSurfaceProducer(SurfaceLifecycle lifecycle) {
return new SurfaceProducer() {
@Override
public void setCallback(SurfaceProducer.Callback cb) {}
@Override
public void pushImage(Image image) {}
};
}
@Override
public long id() {
return 0;
}
@NonNull
@Override
public SurfaceProducer createSurfaceProducer(SurfaceLifecycle lifecycle) {
return new SurfaceProducer() {
@Override
public void setCallback(SurfaceProducer.Callback cb) {}
@Override
public void release() {}
@Override
public long id() {
return 0;
@Override
public int getWidth() {
return 0;
}
@Override
public int getHeight() {
return 0;
}
@Override
public void setSize(int width, int height) {}
@Override
public Surface getSurface() {
return null;
}
@Override
public Surface getForcedNewSurface() {
return null;
}
@Override
public boolean handlesCropAndRotation() {
return false;
}
public void scheduleFrame() {}
};
}
@Override
public void release() {}
@Override
public int getWidth() {
return 0;
}
@Override
public int getHeight() {
return 0;
}
@Override
public void setSize(int width, int height) {}
@Override
public Surface getSurface() {
return null;
}
@Override
public Surface getForcedNewSurface() {
return null;
}
@Override
public boolean handlesCropAndRotation() {
return false;
}
public void scheduleFrame() {}
};
}
};
});
platformViewsController.attach(context, registry, executor);