-
Notifications
You must be signed in to change notification settings - Fork 6k
[android] Fixes 'drawRenderNode called on a context with no surface' crash #33655
Conversation
a9813bd
to
c6c0cae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The systems we can reproduce locally are Android 10, 11, and 12.
Android 8.1 and 9 are only present in the crash info collection system, probably for different reasons.
if (!flutterJNI.isAttached()) { | ||
return; | ||
} | ||
Log.v(TAG, "Releasing a SurfaceTexture (" + id + ")."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this log - it's not really useful to end developers and will just end up adding to noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering the complexity of the finalize
method, e.g., resurrection, I do not modify its logic here.
I'll try to replace the finalize
method with PhantomReferece
in combination with ReferenceQueue
in a new PR later.
@@ -860,6 +862,7 @@ void onLowMemory() { | |||
ensureAlive(); | |||
flutterEngine.getDartExecutor().notifyLowMemoryWarning(); | |||
flutterEngine.getSystemChannel().sendMemoryPressureWarning(); | |||
flutterEngine.getRenderer().onTrimMemory(TRIM_MEMORY_COMPLETE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird. there's no call to delegate.onLowMemory
from io.flutter.embedding.android.FlutterActivity
, but there's one from io.flutter.embedding.android.FlutterFragment
. This seems wrong. Do we know why that's the case? cc @dnfield
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Activity is using onTrimMemory
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, onTrimMemory is preferable over onLowMemory - whenever onLowMemory will get called, onTrimMemory will also get called (right before or right after, can't remember).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the Android documentation, onLowMemory
is only used for API levels lower than 14:
To support API levels lower than 14, you can use the ComponentCallbacks.onLowMemory() method as a fallback that's roughly equivalent to the ComponentCallbacks2#TRIM_MEMORY_COMPLETE level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should override onLowMemory
in io.flutter.embedding.android.FlutterActivity
class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's confusing that onLowMemory
is called from FlutterFragment
but not FlutterActivity
. We don't support API level 14, so should we remove all instances of onLowMemory
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's confusing that
onLowMemory
is called fromFlutterFragment
but notFlutterActivity
. We don't support API level 14, so should we remove all instances ofonLowMemory
?
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the unnecessary log
@@ -200,34 +241,30 @@ protected void finalize() throws Throwable { | |||
return; | |||
} | |||
|
|||
handler.post(new SurfaceTextureFinalizerRunnable(id, flutterJNI)); | |||
handler.post( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ColdPaleLight, @jason-simmons I was going over the PR ce8fe8b
Can you comment on why the PR posted a task to the main looper instead of just running directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because 'unregisterTexture' must be called on the main thread. And 'finalize' method is called on 'Finalizer' thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like there's a race when reading released
, or when unregisterTexture
is called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I see, here we need to make sure that the finalizer thread can see changes of released
value (for example: use keyword volatile). Or we can defer released
check to the main thread. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, deferring sounds good. Also, can we add comments about what is happening here, why we need all of this? and clarify that this is a case where someone forgot to do the right thing. flutter/flutter#88571
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The finalize()
execution is not guaranteed. Can we consider using ReferenceQueue
here?
@@ -44,6 +46,10 @@ public class FlutterRenderer implements TextureRegistry { | |||
private boolean isDisplayingFlutterUi = false; | |||
private Handler handler = new Handler(); | |||
|
|||
@NonNull | |||
private final Set<TextureRegistry.OnLowMemoryListener> onLowMemoryListeners = | |||
new CopyOnWriteArraySet<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this being accessed from different threads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is still unresolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one has been solved.
// surface. If we continue to use the surface (e.g., call lockHardwareCanvas), a crash | ||
// occurs, and we found that this crash appeared on Android10 and above. | ||
// | ||
// Here our workaround is to recreate the surface before using it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: also point to the issue this is fixing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
@@ -91,6 +97,19 @@ public void removeIsDisplayingFlutterUiListener(@NonNull FlutterUiDisplayListene | |||
flutterJNI.removeIsDisplayingFlutterUiListener(listener); | |||
} | |||
|
|||
/** Adds a listener that is invoked when a memory pressure warning was forward. */ | |||
public void addOnLowMemoryListener(@NonNull OnLowMemoryListener listener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make this private or package-only, and add VisibleForTesting
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
* Removes a {@link OnLowMemoryListener} that was added with {@link | ||
* #addOnLowMemoryListener(OnLowMemoryListener)}. | ||
*/ | ||
public void removeOnLowMemoryListener(@NonNull OnLowMemoryListener listener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment about visibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/** Listener invoked when a memory pressure warning was forward. */ | ||
interface OnLowMemoryListener { | ||
/** This method will be invoked when a memory pressure warning was forward. */ | ||
void onLowMemory(int level); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be called onTrimMemory
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
@@ -55,4 +65,10 @@ interface OnFrameConsumedListener { | |||
*/ | |||
void onFrameConsumed(); | |||
} | |||
|
|||
/** Listener invoked when a memory pressure warning was forward. */ | |||
interface OnLowMemoryListener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be called OnTrimMemoryListener
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
@@ -45,6 +52,9 @@ interface SurfaceTextureEntry { | |||
|
|||
/** Set a listener that will be notified when the most recent image has been consumed. */ | |||
default void setOnFrameConsumedListener(@Nullable OnFrameConsumedListener listener) {} | |||
|
|||
/** Set a listener that will be notified when a memory pressure warning was forward. */ | |||
default void setOnLowMemoryListener(@Nullable OnLowMemoryListener listener) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be called setOnTrimMemoryListener
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
a6179a0
to
96e6410
Compare
This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again. |
I think it would be good to at least see this pass Google's internal testing before requesting a hotfix for it. |
This is undergoing testing internally via cl/456284311 If that lands without issue I'll file a CP request. |
This has passed internal tests - filed flutter/flutter#106746 |
…crash (flutter#33655) Fixes cherry-pick conflict for deleted test, see flutter@031fa76#diff-553d8d6ccf46421df54ff1b840606a323494ca2c73f3ce1731169c4d44504b2fL866
…crash (#33655) (#34400) Fixes cherry-pick conflict for deleted test, see 031fa76#diff-553d8d6ccf46421df54ff1b840606a323494ca2c73f3ce1731169c4d44504b2fL866 Co-authored-by: Rulong Chen(陈汝龙) <rulong.crl@alibaba-inc.com>
When we could get this fix in stable channel? |
The cherry pick has landed, but it will probably be a little bit at this point before it's propagated (next week is a US holiday and many people are taking vacation). |
This shipped an hour ago in the 3.0.4 stable. |
Oh, sorry, forgot that @CaseyHillers was on the job and he gets stuff done. |
I tested with version @0xZOne and everyone else who worked on this fix, thanks. |
The problem is still there with flutter V3.3.2. |
I also experienced this problem with 3.0.4,3.0.5,3.3.4. It's not reproducible with |
@weicheng59 你们解决了这个问题吗 |
@raywoocn Is any fix ? |
@pakeng we avoid it by removing the platform view when the app go to background and rebuild it when the app go to foreground. You can get messages of app going background and foreground by having android native code send them to you. After this work around, no more crashes. |
When a memory pressure warning is received and the level equal {@code ComponentCallbacks2.TRIM_MEMORY_COMPLETE}, the Android system release the underlying surface. If we continue to use the surface (e.g., call lockHardwareCanvas), a crash occurs, and we found that this crash appeared on Android 10 and above.
Steps to reproduce:
adb shell am send-trim-memory <your_package_name_here> COMPLETE
;Here our workaround is to recreate the surface before using it.
Fixes flutter/flutter#103870
Pre-launch Checklist
writing and running engine tests.
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.