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

Commit

Permalink
I52370 3.22.cp attempt2 (#52491)
Browse files Browse the repository at this point in the history
- **Replace LinkedLists that are used as a queue in android FlutterRenderer with ArrayDeques (#51494)**
- **Workaround HardwareRenderer breakage in Android 14 (#52370)**
Fixes flutter/flutter#147644

Includes #51494 to avoid merge conflicts and have a cleaner merge.
  • Loading branch information
reidbaker authored May 1, 2024
1 parent b4bfd45 commit 3793869
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ void onPostResume() {
ensureAlive();
if (flutterEngine != null) {
updateSystemUiOverlays();
flutterEngine.getPlatformViewsController().onResume();
} else {
Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity.");
}
Expand Down Expand Up @@ -1020,6 +1021,7 @@ void onTrimMemory(int level) {
flutterEngine.getSystemChannel().sendMemoryPressureWarning();
}
flutterEngine.getRenderer().onTrimMemory(level);
flutterEngine.getPlatformViewsController().onTrimMemory(level);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static io.flutter.Build.API_LEVELS;

import android.annotation.TargetApi;
import android.content.ComponentCallbacks2;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Rect;
Expand All @@ -29,11 +30,11 @@
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
Expand Down Expand Up @@ -415,12 +416,18 @@ final class ImageReaderSurfaceProducer
// Flip when debugging to see verbose logs.
private static final boolean VERBOSE_LOGS = false;

// We must always cleanup on memory pressure on Android 14 due to a bug in Android.
// It is safe to do on all versions so we unconditionally have this set to true.
private static final boolean CLEANUP_ON_MEMORY_PRESSURE = true;

private final long id;

private boolean released;
// Will be true in tests and on Android API < 33.
private boolean ignoringFence = false;

private boolean trimOnMemoryPressure = CLEANUP_ON_MEMORY_PRESSURE;

// The requested width and height are updated by setSize.
private int requestedWidth = 1;
private int requestedHeight = 1;
Expand All @@ -434,10 +441,11 @@ final class ImageReaderSurfaceProducer
private long lastDequeueTime = 0;
private long lastQueueTime = 0;
private long lastScheduleTime = 0;
private int numTrims = 0;

private Object lock = new Object();
// REQUIRED: The following fields must only be accessed when lock is held.
private final LinkedList<PerImageReader> imageReaderQueue = new LinkedList<PerImageReader>();
private final ArrayDeque<PerImageReader> imageReaderQueue = new ArrayDeque<PerImageReader>();
private final HashMap<ImageReader, PerImageReader> perImageReaders =
new HashMap<ImageReader, PerImageReader>();
private PerImage lastDequeuedImage = null;
Expand All @@ -457,7 +465,7 @@ public PerImage(Image image, long queuedTime) {
/** Internal class: state held per ImageReader. */
private class PerImageReader {
public final ImageReader reader;
private final LinkedList<PerImage> imageQueue = new LinkedList<PerImage>();
private final ArrayDeque<PerImage> imageQueue = new ArrayDeque<PerImage>();
private boolean closed = false;

private final ImageReader.OnImageAvailableListener onImageAvailableListener =
Expand Down Expand Up @@ -651,6 +659,15 @@ PerImage dequeueImage() {

@Override
public void onTrimMemory(int level) {
if (!trimOnMemoryPressure) {
return;
}
if (level < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
return;
}
synchronized (lock) {
numTrims++;
}
cleanup();
createNewReader = true;
}
Expand Down Expand Up @@ -867,6 +884,13 @@ public int numImageReaders() {
}
}

@VisibleForTesting
public int numTrims() {
synchronized (lock) {
return numTrims;
}
}

@VisibleForTesting
public int numImages() {
int r = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static io.flutter.Build.API_LEVELS;

import android.annotation.TargetApi;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.MutableContextWrapper;
import android.os.Build;
Expand Down Expand Up @@ -1053,6 +1054,24 @@ private void diposeAllViews() {
}
}

// Invoked when the Android system is requesting we reduce memory usage.
public void onTrimMemory(int level) {
if (level < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
return;
}
for (VirtualDisplayController vdc : vdControllers.values()) {
vdc.clearSurface();
}
}

// Called after the application has been resumed.
// This is where we undo whatever may have been done in onTrimMemory.
public void onResume() {
for (VirtualDisplayController vdc : vdControllers.values()) {
vdc.resetSurface();
}
}

/**
* Disposes a single
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,49 @@ public void dispatchTouchEvent(MotionEvent event) {
presentation.dispatchTouchEvent(event);
}

public void clearSurface() {
virtualDisplay.setSurface(null);
}

public void resetSurface() {
final int width = getRenderTargetWidth();
final int height = getRenderTargetHeight();
final boolean isFocused = getView().isFocused();
final SingleViewPresentation.PresentationState presentationState = presentation.detachState();

// We detach the surface to prevent it being destroyed when releasing the vd.
virtualDisplay.setSurface(null);
virtualDisplay.release();
final DisplayManager displayManager =
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
int flags = 0;
virtualDisplay =
displayManager.createVirtualDisplay(
"flutter-vd#" + viewId,
width,
height,
densityDpi,
renderTarget.getSurface(),
flags,
callback,
null /* handler */);
// Create a new SingleViewPresentation and show() it before we cancel() the existing
// presentation. Calling show() and cancel() in this order fixes
// https://github.com/flutter/flutter/issues/26345 and maintains seamless transition
// of the contents of the presentation.
SingleViewPresentation newPresentation =
new SingleViewPresentation(
context,
virtualDisplay.getDisplay(),
accessibilityEventsDelegate,
presentationState,
focusChangeListener,
isFocused);
newPresentation.show();
presentation.cancel();
presentation = newPresentation;
}

static class OneTimeOnDrawListener implements ViewTreeObserver.OnDrawListener {
static void schedule(View view, Runnable runnable) {
OneTimeOnDrawListener listener = new OneTimeOnDrawListener(view, runnable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,12 +647,41 @@ public void ImageReaderSurfaceProducerTrimMemoryCallback() {
assertEquals(1, texture.numImageReaders());
assertEquals(1, texture.numImages());

// Invoke the onTrimMemory callback.
// Invoke the onTrimMemory callback with level 0.
// This should do nothing.
texture.onTrimMemory(0);
shadowOf(Looper.getMainLooper()).idle();

assertEquals(1, texture.numImageReaders());
assertEquals(1, texture.numImages());
assertEquals(0, texture.numTrims());

// Invoke the onTrimMemory callback with level 40.
// This should result in a trim.
texture.onTrimMemory(40);
shadowOf(Looper.getMainLooper()).idle();

assertEquals(0, texture.numImageReaders());
assertEquals(0, texture.numImages());
assertEquals(1, texture.numTrims());

// Request the surface, this should result in a new image reader.
surface = texture.getSurface();
assertEquals(1, texture.numImageReaders());
assertEquals(0, texture.numImages());
assertEquals(1, texture.numTrims());

// Render an image.
canvas = surface.lockHardwareCanvas();
canvas.drawARGB(255, 255, 0, 0);
surface.unlockCanvasAndPost(canvas);

// Let callbacks run, this will produce a single frame.
shadowOf(Looper.getMainLooper()).idle();

assertEquals(1, texture.numImageReaders());
assertEquals(1, texture.numImages());
assertEquals(1, texture.numTrims());
}

// A 0x0 ImageReader is a runtime error.
Expand Down

0 comments on commit 3793869

Please sign in to comment.