Merge "ImageReader: Fix rogue RuntimeException in #detachImage"

This commit is contained in:
Avichal Rakesh
2022-08-04 20:42:09 +00:00
committed by Android (Google) Code Review
2 changed files with 61 additions and 13 deletions

View File

@@ -18,7 +18,11 @@ package android.media;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.graphics.GraphicBuffer;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
@@ -29,6 +33,7 @@ import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
import android.hardware.SyncFence;
import android.hardware.camera2.MultiResolutionImageReader;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -85,6 +90,38 @@ public class ImageReader implements AutoCloseable {
*/
private static final int ACQUIRE_MAX_IMAGES = 2;
/**
* <p>
* Flag to gate correct exception thrown by {@code #detachImage}.
* </p>
* <p>
* {@code #detachImage} is documented as throwing {@link java.lang.IllegalStateException} in
* the event of an error; a native helper method to this threw
* {@link java.lang.RuntimeException} if the surface was abandoned while detaching the
* {@code Image}.
* <p>
* This previously undocumented exception behavior continues through Android T.
* </p>
* <p>
* After Android T, the native helper method only throws {@code IllegalStateExceptions} in
* accordance with the documentation.
* </p>
* <p>
* {@code #detachImage} will now throw only ISEs if it runs into errors while detaching
* the image. Behavior on apps targeting API levels <= T remains unchanged.
* </p>
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long DETACH_THROWS_ISE_ONLY = 236825255L;
/**
* Cached value of {@link #DETACH_THROWS_ISE_ONLY} flag to prevent repeated calls when
* detaching image.
*/
private final boolean mDetachThrowsIseOnly =
CompatChanges.isChangeEnabled(DETACH_THROWS_ISE_ONLY);
/**
* <p>
* Create a new reader for images of the desired size and format.
@@ -825,10 +862,10 @@ public class ImageReader implements AutoCloseable {
* </p>
* <p>
* After this call, the ImageReader no longer owns this image, and the image
* ownership can be transfered to another entity like {@link ImageWriter}
* ownership can be transferred to another entity like {@link ImageWriter}
* via {@link ImageWriter#queueInputImage}. It's up to the new owner to
* release the resources held by this image. For example, if the ownership
* of this image is transfered to an {@link ImageWriter}, the image will be
* of this image is transferred to an {@link ImageWriter}, the image will be
* freed by the ImageWriter after the image data consumption is done.
* </p>
* <p>
@@ -849,16 +886,22 @@ public class ImageReader implements AutoCloseable {
* @throws IllegalStateException If the ImageReader or image have been
* closed, or the has been detached, or has not yet been
* acquired.
* @throws RuntimeException If there is an error detaching {@code Image} from {@code Surface}.
* {@code RuntimeException} is only thrown for applications targeting SDK <=
* {@link android.os.Build.VERSION_CODES#TIRAMISU}.
* For applications targeting SDK >
* {@link android.os.Build.VERSION_CODES#TIRAMISU},
* this method only throws {@code IllegalStateException}.
* @hide
*/
public void detachImage(Image image) {
if (image == null) {
public void detachImage(@Nullable Image image) {
if (image == null) {
throw new IllegalArgumentException("input image must not be null");
}
if (!isImageOwnedbyMe(image)) {
}
if (!isImageOwnedbyMe(image)) {
throw new IllegalArgumentException("Trying to detach an image that is not owned by"
+ " this ImageReader");
}
}
SurfaceImage si = (SurfaceImage) image;
si.throwISEIfImageIsInvalid();
@@ -867,7 +910,7 @@ public class ImageReader implements AutoCloseable {
throw new IllegalStateException("Image was already detached from this ImageReader");
}
nativeDetachImage(image);
nativeDetachImage(image, mDetachThrowsIseOnly);
si.clearSurfacePlanes();
si.mPlanes = null;
si.setDetached(true);
@@ -1408,7 +1451,7 @@ public class ImageReader implements AutoCloseable {
private synchronized native void nativeClose();
private synchronized native void nativeReleaseImage(Image i);
private synchronized native Surface nativeGetSurface();
private synchronized native int nativeDetachImage(Image i);
private synchronized native int nativeDetachImage(Image i, boolean throwISEOnly);
private synchronized native void nativeDiscardFreeBuffers();
/**

View File

@@ -643,7 +643,8 @@ static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image,
return ACQUIRE_SUCCESS;
}
static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image,
jboolean throwISEOnly) {
ALOGV("%s:", __FUNCTION__);
JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
if (ctx == NULL) {
@@ -666,8 +667,12 @@ static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
res = bufferConsumer->detachBuffer(buffer->mSlot);
if (res != OK) {
ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
jniThrowRuntimeException(env,
"nativeDetachImage failed for image!!!");
if ((bool) throwISEOnly) {
jniThrowException(env, "java/lang/IllegalStateException",
"nativeDetachImage failed for image!!!");
} else {
jniThrowRuntimeException(env, "nativeDetachImage failed for image!!!");
}
return res;
}
return OK;
@@ -965,7 +970,7 @@ static const JNINativeMethod gImageReaderMethods[] = {
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
{"nativeImageSetup", "(Landroid/media/Image;Z)I", (void*)ImageReader_imageSetup },
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
{"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
{"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage },
{"nativeCreateImagePlanes",
"(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
(void*)ImageReader_createImagePlanes },