From d6ec7d4dc0eaa1ab8829c654176bb1521f4ac005 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Thu, 27 Mar 2025 16:31:52 -0700 Subject: [PATCH] [android] only release background image readers on Android 14. (#165942) Fixes https://github.com/flutter/flutter/issues/163561 Fixes https://github.com/flutter/flutter/issues/156488 Fixes https://github.com/flutter/flutter/issues/156489 Fixes https://github.com/flutter/flutter/issues/163520 Forked from https://github.com/flutter/flutter/pull/163692 Only release background image readers on Android 14. I believe reader disposal is the ultimate cause of https://github.com/flutter/flutter/issues/162147 , where older android devices don't seem to handle backgrounding the same way we expect on newer devices. The result of this is a crash in HWUI, which is unexpected. Since we only ever did the background release to work around an ANdroid 14 bug, and because it breaks other functionality like background playback - we should remove it for all targets besides 14. --- .../android/image_external_texture.cc | 21 ++++++-- .../platform/android/image_external_texture.h | 7 ++- .../android/image_external_texture_gl.cc | 5 +- .../android/image_external_texture_gl.h | 3 +- .../image_external_texture_gl_impeller.cc | 5 +- .../image_external_texture_gl_impeller.h | 3 +- .../android/image_external_texture_gl_skia.cc | 5 +- .../android/image_external_texture_gl_skia.h | 3 +- .../image_external_texture_vk_impeller.cc | 5 +- .../image_external_texture_vk_impeller.h | 3 +- .../flutter/embedding/engine/FlutterJNI.java | 10 ++-- .../engine/renderer/FlutterRenderer.java | 50 ++++--------------- .../platform/PlatformViewsController.java | 7 ++- .../io/flutter/view/TextureRegistry.java | 49 +++++++++++++++++- .../platform/android/platform_view_android.cc | 9 ++-- .../platform/android/platform_view_android.h | 4 +- .../android/platform_view_android_jni_impl.cc | 14 ++++-- .../engine/renderer/FlutterRendererTest.java | 12 +++-- .../platform/PlatformViewsControllerTest.java | 2 +- 19 files changed, 141 insertions(+), 76 deletions(-) diff --git a/engine/src/flutter/shell/platform/android/image_external_texture.cc b/engine/src/flutter/shell/platform/android/image_external_texture.cc index 69a0e4d129..4b8ea52c9a 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture.cc +++ b/engine/src/flutter/shell/platform/android/image_external_texture.cc @@ -16,10 +16,12 @@ namespace flutter { ImageExternalTexture::ImageExternalTexture( int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, - const std::shared_ptr& jni_facade) + const std::shared_ptr& jni_facade, + ImageLifecycle lifecycle) : Texture(id), image_texture_entry_(image_texture_entry), - jni_facade_(jni_facade) {} + jni_facade_(jni_facade), + texture_lifecycle_(lifecycle) {} ImageExternalTexture::~ImageExternalTexture() = default; @@ -66,8 +68,19 @@ void ImageExternalTexture::OnGrContextCreated() { // Implementing flutter::ContextListener. void ImageExternalTexture::OnGrContextDestroyed() { if (state_ == AttachmentState::kAttached) { - dl_image_.reset(); - image_lru_.Clear(); + switch (texture_lifecycle_) { + case ImageLifecycle::kReset: { + dl_image_.reset(); + image_lru_.Clear(); + } break; + case ImageLifecycle::kKeepAlive: + // Intentionally do nothing. + /// + // If we reset the image, we are not able to re-acquire it, but the + // producer of the image will not know to reproduce it, resulting in a + // blank image. See https://github.com/flutter/flutter/issues/163561. + break; + } Detach(); } state_ = AttachmentState::kDetached; diff --git a/engine/src/flutter/shell/platform/android/image_external_texture.h b/engine/src/flutter/shell/platform/android/image_external_texture.h index d235b26aaf..96b78ed68f 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture.h +++ b/engine/src/flutter/shell/platform/android/image_external_texture.h @@ -35,10 +35,14 @@ namespace flutter { /// class ImageExternalTexture : public flutter::Texture { public: + /// Whether the last image should be reset when the context is destroyed. + enum class ImageLifecycle { kReset, kKeepAlive }; + explicit ImageExternalTexture( int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, - const std::shared_ptr& jni_facade); + const std::shared_ptr& jni_facade, + ImageLifecycle lifecycle); // |flutter::Texture| virtual ~ImageExternalTexture(); @@ -100,6 +104,7 @@ class ImageExternalTexture : public flutter::Texture { // |flutter::ContextListener| void OnGrContextDestroyed() override; + const ImageLifecycle texture_lifecycle_; FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTexture); }; diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_gl.cc b/engine/src/flutter/shell/platform/android/image_external_texture_gl.cc index 4ff9a0043c..21cf09ae59 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/android/image_external_texture_gl.cc @@ -24,8 +24,9 @@ namespace flutter { ImageExternalTextureGL::ImageExternalTextureGL( int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, - const std::shared_ptr& jni_facade) - : ImageExternalTexture(id, image_texture_entry, jni_facade) {} + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle) + : ImageExternalTexture(id, image_texture_entry, jni_facade, lifecycle) {} void ImageExternalTextureGL::Attach(PaintContext& context) { if (state_ == AttachmentState::kUninitialized) { diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_gl.h b/engine/src/flutter/shell/platform/android/image_external_texture_gl.h index 9f923e816d..ccb9d7d485 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/android/image_external_texture_gl.h @@ -20,7 +20,8 @@ class ImageExternalTextureGL : public ImageExternalTexture { ImageExternalTextureGL( int64_t id, const fml::jni::ScopedJavaGlobalRef& image_textury_entry, - const std::shared_ptr& jni_facade); + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle); protected: // |ImageExternalTexture| diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.cc b/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.cc index b756aa4f41..1deb2f56b0 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.cc @@ -13,8 +13,9 @@ ImageExternalTextureGLImpeller::ImageExternalTextureGLImpeller( const std::shared_ptr& context, int64_t id, const fml::jni::ScopedJavaGlobalRef& image_textury_entry, - const std::shared_ptr& jni_facade) - : ImageExternalTextureGL(id, image_textury_entry, jni_facade), + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle) + : ImageExternalTextureGL(id, image_textury_entry, jni_facade, lifecycle), impeller_context_(context) {} void ImageExternalTextureGLImpeller::Detach() {} diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.h b/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.h index 05771e0f34..e02a04ea68 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.h +++ b/engine/src/flutter/shell/platform/android/image_external_texture_gl_impeller.h @@ -22,7 +22,8 @@ class ImageExternalTextureGLImpeller : public ImageExternalTextureGL { int64_t id, const fml::jni::ScopedJavaGlobalRef& hardware_buffer_texture_entry, - const std::shared_ptr& jni_facade); + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle); private: // |ImageExternalTexture| diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.cc b/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.cc index cc2799f407..bf27acc7a8 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.cc +++ b/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.cc @@ -13,8 +13,9 @@ ImageExternalTextureGLSkia::ImageExternalTextureGLSkia( const std::shared_ptr& context, int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, - const std::shared_ptr& jni_facade) - : ImageExternalTextureGL(id, image_texture_entry, jni_facade) {} + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle) + : ImageExternalTextureGL(id, image_texture_entry, jni_facade, lifecycle) {} void ImageExternalTextureGLSkia::Attach(PaintContext& context) { if (state_ == AttachmentState::kUninitialized) { diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.h b/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.h index df181f2add..722c92b27e 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.h +++ b/engine/src/flutter/shell/platform/android/image_external_texture_gl_skia.h @@ -19,7 +19,8 @@ class ImageExternalTextureGLSkia : public ImageExternalTextureGL { const std::shared_ptr& context, int64_t id, const fml::jni::ScopedJavaGlobalRef& image_textury_entry, - const std::shared_ptr& jni_facade); + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle); private: // |ImageExternalTexture| diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.cc b/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.cc index 648bd1bbc8..557cc19402 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.cc +++ b/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.cc @@ -20,8 +20,9 @@ ImageExternalTextureVKImpeller::ImageExternalTextureVKImpeller( const std::shared_ptr& impeller_context, int64_t id, const fml::jni::ScopedJavaGlobalRef& image_texture_entry, - const std::shared_ptr& jni_facade) - : ImageExternalTexture(id, image_texture_entry, jni_facade), + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle) + : ImageExternalTexture(id, image_texture_entry, jni_facade, lifecycle), impeller_context_(impeller_context) {} ImageExternalTextureVKImpeller::~ImageExternalTextureVKImpeller() {} diff --git a/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.h b/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.h index 70f5320689..8e102d2ce6 100644 --- a/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.h +++ b/engine/src/flutter/shell/platform/android/image_external_texture_vk_impeller.h @@ -23,7 +23,8 @@ class ImageExternalTextureVKImpeller : public ImageExternalTexture { int64_t id, const fml::jni::ScopedJavaGlobalRef& hardware_buffer_texture_entry, - const std::shared_ptr& jni_facade); + const std::shared_ptr& jni_facade, + ImageExternalTexture::ImageLifecycle lifecycle); ~ImageExternalTextureVKImpeller() override; diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 656fefddf4..27ebed8e44 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -938,19 +938,23 @@ public class FlutterJNI { */ @UiThread public void registerImageTexture( - long textureId, @NonNull TextureRegistry.ImageConsumer imageTexture) { + long textureId, + @NonNull TextureRegistry.ImageConsumer imageTexture, + boolean resetOnBackground) { ensureRunningOnMainThread(); ensureAttachedToNative(); nativeRegisterImageTexture( nativeShellHolderId, textureId, - new WeakReference(imageTexture)); + new WeakReference(imageTexture), + resetOnBackground); } private native void nativeRegisterImageTexture( long nativeShellHolderId, long textureId, - @NonNull WeakReference imageTexture); + @NonNull WeakReference imageTexture, + boolean resetOnBackground); /** * Call this method to inform Flutter that a texture previously registered with {@link diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index ba6419e70a..3946ed50d2 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -186,37 +186,16 @@ public class FlutterRenderer implements TextureRegistry { */ @NonNull @Override - public SurfaceProducer createSurfaceProducer() { - // Prior to Impeller, Flutter on Android *only* ran on OpenGLES (via Skia). That - // meant that - // plugins (i.e. end-users) either explicitly created a SurfaceTexture (via - // createX/registerX) or an ImageTexture (via createX/registerX). - // - // In an Impeller world, which for the first time uses (if available) a Vulkan - // rendering - // backend, it is no longer possible (at least not trivially) to render an - // OpenGLES-provided - // texture (SurfaceTexture) in a Vulkan context. - // - // This function picks the "best" rendering surface based on the Android - // runtime, and - // provides a consumer-agnostic SurfaceProducer (which in turn vends a Surface), - // and has - // plugins (i.e. end-users) use the Surface instead, letting us "hide" the - // consumer-side - // of the implementation. - // - // tl;dr: If ImageTexture is available, we use it, otherwise we use a - // SurfaceTexture. - // Coincidentally, if ImageTexture is available, we are also on an Android - // version that is - // running Vulkan, so we don't have to worry about it not being supported. + public SurfaceProducer createSurfaceProducer(SurfaceLifecycle lifecycle) { final SurfaceProducer entry; if (!debugForceSurfaceProducerGlTextures && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { final long id = nextTextureId.getAndIncrement(); final ImageReaderSurfaceProducer producer = new ImageReaderSurfaceProducer(id); - registerImageTexture(id, producer); - addOnTrimMemoryListener(producer); + boolean reset = lifecycle == SurfaceLifecycle.resetInBackground; + registerImageTexture(id, producer, reset); + if (reset) { + addOnTrimMemoryListener(producer); + } imageReaderProducers.add(producer); Log.v(TAG, "New ImageReaderSurfaceProducer ID: " + id); entry = producer; @@ -282,7 +261,7 @@ public class FlutterRenderer implements TextureRegistry { final ImageTextureRegistryEntry entry = new ImageTextureRegistryEntry(nextTextureId.getAndIncrement()); Log.v(TAG, "New ImageTextureEntry ID: " + entry.id()); - registerImageTexture(entry.id(), entry); + registerImageTexture(entry.id(), entry, /*resetOnBackground=*/ false); return entry; } @@ -1279,25 +1258,23 @@ public class FlutterRenderer implements TextureRegistry { displayFeaturesState); } - // TODO(mattcarroll): describe the native behavior that this invokes - // TODO(mattcarroll): determine if this is nullable or nonnull public Bitmap getBitmap() { return flutterJNI.getBitmap(); } - // TODO(mattcarroll): describe the native behavior that this invokes public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) { flutterJNI.dispatchPointerDataPacket(buffer, position); } - // TODO(mattcarroll): describe the native behavior that this invokes private void registerTexture(long textureId, @NonNull SurfaceTextureWrapper textureWrapper) { flutterJNI.registerTexture(textureId, textureWrapper); } private void registerImageTexture( - long textureId, @NonNull TextureRegistry.ImageConsumer imageTexture) { - flutterJNI.registerImageTexture(textureId, imageTexture); + long textureId, + @NonNull TextureRegistry.ImageConsumer imageTexture, + boolean resetOnBackground) { + flutterJNI.registerImageTexture(textureId, imageTexture, resetOnBackground); } @VisibleForTesting @@ -1305,27 +1282,22 @@ public class FlutterRenderer implements TextureRegistry { flutterJNI.scheduleFrame(); } - // TODO(mattcarroll): describe the native behavior that this invokes private void unregisterTexture(long textureId) { flutterJNI.unregisterTexture(textureId); } - // TODO(mattcarroll): describe the native behavior that this invokes public boolean isSoftwareRenderingEnabled() { return flutterJNI.getIsSoftwareRenderingEnabled(); } - // TODO(mattcarroll): describe the native behavior that this invokes public void setAccessibilityFeatures(int flags) { flutterJNI.setAccessibilityFeatures(flags); } - // TODO(mattcarroll): describe the native behavior that this invokes public void setSemanticsEnabled(boolean enabled) { flutterJNI.setSemanticsEnabled(enabled); } - // TODO(mattcarroll): describe the native behavior that this invokes public void dispatchSemanticsAction( int nodeId, int action, @Nullable ByteBuffer args, int argsPosition) { flutterJNI.dispatchSemanticsAction(nodeId, action, args, argsPosition); diff --git a/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index ac18a1733f..c430192d1d 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -977,7 +977,12 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega private static PlatformViewRenderTarget makePlatformViewRenderTarget( TextureRegistry textureRegistry) { if (enableSurfaceProducerRenderTarget && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { - final TextureRegistry.SurfaceProducer textureEntry = textureRegistry.createSurfaceProducer(); + TextureRegistry.SurfaceLifecycle lifecycle = + Build.VERSION.SDK_INT == API_LEVELS.API_34 + ? TextureRegistry.SurfaceLifecycle.resetInBackground + : TextureRegistry.SurfaceLifecycle.manual; + final TextureRegistry.SurfaceProducer textureEntry = + textureRegistry.createSurfaceProducer(lifecycle); Log.i(TAG, "PlatformView is using SurfaceProducer backend"); return new SurfaceProducerPlatformViewRenderTarget(textureEntry); } diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java b/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java index 423071c41a..5d97904954 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -18,12 +18,57 @@ import androidx.annotation.Nullable; */ public interface TextureRegistry { /** - * Creates and registers a SurfaceProducer texture managed by the Flutter engine. + * Creates and registers a {@link SurfaceProducer}, or a Flutter-managed {@link Surface}. + * + *

Uses the {@link SurfaceLifecycle#manual} lifecycle implicitly. * * @return A SurfaceProducer. */ @NonNull - SurfaceProducer createSurfaceProducer(); + default SurfaceProducer createSurfaceProducer() { + return createSurfaceProducer(SurfaceLifecycle.manual); + } + /** + * How a {@link SurfaceProducer} created by {@link #createSurfaceProducer()} manages the lifecycle + * of the created surface. + */ + enum SurfaceLifecycle { + /** + * The surface and latest image should be kept, even if the app enters the background. + * + *

The application, or calling code, can choose to (manually) reset the surface at the + * appropriate time (such as to lower memory pressure, or cleanup an unused surface), but by + * default the surface will never be reset, and as a result, new images do not have to be drawn + * to the surface. + * + *

This is an appropriate lifecycle for external textures, as it is not guaranteed that new + * images will be drawn to the surface, and whether the image should be kept when the app is + * backgrounded. + */ + manual, + + /** + * The surface will be reset if the app enters the background. + * + *

While the application can choose to manually reset the surface, Flutter may automatically + * reset the surface when the app enters the background. If the surface is reset, and no new + * images are drawn to the surface, the texture will appear blank. + * + *

This is an appropriate lifecycle for platform views, as the platform implementation will + * request a new surface, and draw to, as appropriate when resuming from the background, and + * producing a new image when coming back to the foreground. + */ + resetInBackground + } + + /** + * Creates and a {@link SurfaceProducer}, or a Flutter-managed {@link Surface}. + * + * @param lifecycle Whether to automatically reset the last image and release the surface. + * @return A SurfaceProducer. + */ + @NonNull + SurfaceProducer createSurfaceProducer(SurfaceLifecycle lifecycle); /** * Creates and registers a SurfaceTexture managed by the Flutter engine. diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.cc b/engine/src/flutter/shell/platform/android/platform_view_android.cc index caeb2fe46c..5d438df980 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -348,26 +348,27 @@ void PlatformViewAndroid::RegisterExternalTexture( void PlatformViewAndroid::RegisterImageTexture( int64_t texture_id, - const fml::jni::ScopedJavaGlobalRef& image_texture_entry) { + const fml::jni::ScopedJavaGlobalRef& image_texture_entry, + ImageExternalTexture::ImageLifecycle lifecycle) { switch (android_context_->RenderingApi()) { case AndroidRenderingAPI::kImpellerOpenGLES: // Impeller GLES. RegisterTexture(std::make_shared( std::static_pointer_cast( android_context_->GetImpellerContext()), - texture_id, image_texture_entry, jni_facade_)); + texture_id, image_texture_entry, jni_facade_, lifecycle)); break; case AndroidRenderingAPI::kSkiaOpenGLES: // Legacy GL. RegisterTexture(std::make_shared( std::static_pointer_cast(android_context_), - texture_id, image_texture_entry, jni_facade_)); + texture_id, image_texture_entry, jni_facade_, lifecycle)); break; case AndroidRenderingAPI::kImpellerVulkan: RegisterTexture(std::make_shared( std::static_pointer_cast( android_context_->GetImpellerContext()), - texture_id, image_texture_entry, jni_facade_)); + texture_id, image_texture_entry, jni_facade_, lifecycle)); break; case AndroidRenderingAPI::kSoftware: FML_LOG(INFO) << "Software rendering does not support external textures."; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.h b/engine/src/flutter/shell/platform/android/platform_view_android.h index c1e40a3130..c019f4a112 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.h +++ b/engine/src/flutter/shell/platform/android/platform_view_android.h @@ -20,6 +20,7 @@ #include "flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h" #include "flutter/shell/platform/android/surface/android_native_window.h" #include "flutter/shell/platform/android/surface/android_surface.h" +#include "shell/platform/android/image_external_texture.h" namespace flutter { @@ -93,7 +94,8 @@ class PlatformViewAndroid final : public PlatformView { void RegisterImageTexture( int64_t texture_id, - const fml::jni::ScopedJavaGlobalRef& image_texture_entry); + const fml::jni::ScopedJavaGlobalRef& image_texture_entry, + ImageExternalTexture::ImageLifecycle lifecycle); // |PlatformView| void LoadDartDeferredLibrary( diff --git a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc index 400d714fe3..29ed47dbcb 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc @@ -530,10 +530,16 @@ static void RegisterImageTexture(JNIEnv* env, jobject jcaller, jlong shell_holder, jlong texture_id, - jobject image_texture_entry) { + jobject image_texture_entry, + jboolean reset_on_background) { + ImageExternalTexture::ImageLifecycle lifecycle = + reset_on_background ? ImageExternalTexture::ImageLifecycle::kReset + : ImageExternalTexture::ImageLifecycle::kKeepAlive; + ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterImageTexture( - static_cast(texture_id), // - fml::jni::ScopedJavaGlobalRef(env, image_texture_entry) // + static_cast(texture_id), // + fml::jni::ScopedJavaGlobalRef(env, image_texture_entry), // + lifecycle // ); } @@ -811,7 +817,7 @@ bool RegisterApi(JNIEnv* env) { { .name = "nativeRegisterImageTexture", .signature = "(JJLjava/lang/ref/" - "WeakReference;)V", + "WeakReference;Z)V", .fnPtr = reinterpret_cast(&RegisterImageTexture), }, { diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java index a36e244563..f911ec432c 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java @@ -829,7 +829,8 @@ public class FlutterRendererTest { @SuppressWarnings({"deprecation", "removal"}) public void ImageReaderSurfaceProducerIsCleanedUpOnTrimMemory() { FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer(); - TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer(); + TextureRegistry.SurfaceProducer producer = + flutterRenderer.createSurfaceProducer(TextureRegistry.SurfaceLifecycle.resetInBackground); // Create and set a mock callback. TextureRegistry.SurfaceProducer.Callback callback = @@ -851,7 +852,8 @@ public class FlutterRendererTest { public void ImageReaderSurfaceProducerSignalsCleanupBeforeDestroying() throws Exception { // Regression test for https://github.com/flutter/flutter/issues/160933. FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer(); - TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer(); + TextureRegistry.SurfaceProducer producer = + flutterRenderer.createSurfaceProducer(TextureRegistry.SurfaceLifecycle.resetInBackground); // Ensure the callbacks were actually called. // Note this needs to be an object in order to be accessed in the callback. @@ -902,7 +904,8 @@ public class FlutterRendererTest { public void ImageReaderSurfaceProducerUnsubscribesWhenReleased() { // Regression test for https://github.com/flutter/flutter/issues/156434. FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer(); - TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer(); + TextureRegistry.SurfaceProducer producer = + flutterRenderer.createSurfaceProducer(TextureRegistry.SurfaceLifecycle.resetInBackground); // Create and set a mock callback. TextureRegistry.SurfaceProducer.Callback callback = @@ -924,7 +927,8 @@ public class FlutterRendererTest { @SuppressWarnings({"deprecation", "removal"}) public void ImageReaderSurfaceProducerIsCreatedOnLifecycleResume() throws Exception { FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer(); - TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer(); + TextureRegistry.SurfaceProducer producer = + flutterRenderer.createSurfaceProducer(TextureRegistry.SurfaceLifecycle.resetInBackground); // Create a callback. CountDownLatch latch = new CountDownLatch(1); diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java index 3c3a33cd76..d30731aba9 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java @@ -1621,7 +1621,7 @@ public class PlatformViewsControllerTest { @NonNull @Override - public SurfaceProducer createSurfaceProducer() { + public SurfaceProducer createSurfaceProducer(SurfaceLifecycle lifecycle) { return new SurfaceProducer() { @Override public void setCallback(SurfaceProducer.Callback cb) {}