Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b2b2802 authored by Igor Murashkin's avatar Igor Murashkin Committed by The Android Automerger
Browse files

media: Update ImageReader to remove MaxImagesAcquiredException

* acquiring images now throws IllegalStateException instead of
    MaxImagesAcquiredException

Bug: 10691447
Change-Id: I7ce68f990fb96703705b9181012a28633fea0b7a
parent 8ce62489
Loading
Loading
Loading
Loading
+2 −9
Original line number Diff line number Diff line
@@ -12350,8 +12350,8 @@ package android.media {
  }
  public class ImageReader implements java.lang.AutoCloseable {
    method public android.media.Image acquireLatestImage() throws android.media.ImageReader.MaxImagesAcquiredException;
    method public android.media.Image acquireNextImage() throws android.media.ImageReader.MaxImagesAcquiredException;
    method public android.media.Image acquireLatestImage();
    method public android.media.Image acquireNextImage();
    method public void close();
    method public int getHeight();
    method public int getImageFormat();
@@ -12362,13 +12362,6 @@ package android.media {
    method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler);
  }
  public static class ImageReader.MaxImagesAcquiredException extends java.lang.Exception {
    ctor public ImageReader.MaxImagesAcquiredException();
    ctor public ImageReader.MaxImagesAcquiredException(java.lang.String);
    ctor public ImageReader.MaxImagesAcquiredException(java.lang.String, java.lang.Throwable);
    ctor public ImageReader.MaxImagesAcquiredException(java.lang.Throwable);
  }
  public static abstract interface ImageReader.OnImageAvailableListener {
    method public abstract void onImageAvailable(android.media.ImageReader);
  }
+0 −4
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.ImageReader.MaxImagesAcquiredException;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -427,9 +426,6 @@ public class VirtualDisplayTest extends AndroidTestCase {
                        image.close();
                    }
                }
            } catch (MaxImagesAcquiredException e) {
                // We should never try to consume more buffers than maxImages.
                throw new IllegalStateException(e);
            } finally {
                mImageReaderLock.unlock();
            }
+1 −2
Original line number Diff line number Diff line
@@ -40,8 +40,7 @@ import java.lang.AutoCloseable;
 * availability of new Images once
 * {@link ImageReader#getMaxImages the maximum outstanding image count} is
 * reached. When this happens, the function acquiring new Images will typically
 * throw a
 * {@link ImageReader.MaxImagesAcquiredException MaxImagesAcquiredException}.</p>
 * throw an {@link IllegalStateException}.</p>
 *
 * @see ImageReader
 */
+87 −58
Original line number Diff line number Diff line
@@ -49,43 +49,20 @@ import java.nio.ByteOrder;
public class ImageReader implements AutoCloseable {

    /**
     * <p>
     * This exception is thrown when the user of an {@link ImageReader} tries to acquire a new
     * {@link Image} when the maximum number of {@link Image Images} have already been acquired.
     * The maximum number is determined by the {@code maxBuffers} argument of
     * {@link ImageReader#newInstance newInstance}.
     * </p>
     *
     * <p>
     * To recover from this exception, release existing {@link Image images} back to the
     * reader with {@link Image#close}.
     * </p>
     *
     * @see Image#close
     * @see ImageReader#acquireLatestImage
     * @see ImageReader#acquireNextImage
     * Returned by nativeImageSetup when acquiring the image was successful.
     */
    public static class MaxImagesAcquiredException extends Exception {
    private static final int ACQUIRE_SUCCESS = 0;
    /**
         * Suppress Eclipse warnings
     * Returned by nativeImageSetup when we couldn't acquire the buffer,
     * because there were no buffers available to acquire.
     */
        private static final long serialVersionUID = 761231231236L;

        public MaxImagesAcquiredException() {
        }

        public MaxImagesAcquiredException(String message) {
            super(message);
        }

        public MaxImagesAcquiredException(String message, Throwable throwable) {
            super(message, throwable);
        }

        public MaxImagesAcquiredException(Throwable throwable) {
            super(throwable);
        }
    }
    private static final int ACQUIRE_NO_BUFS = 1;
    /**
     * Returned by nativeImageSetup when we couldn't acquire the buffer
     * because the consumer has already acquired {@maxImages} and cannot
     * acquire more than that.
     */
    private static final int ACQUIRE_MAX_IMAGES = 2;

    /**
     * <p>Create a new reader for images of the desired size and format.</p>
@@ -195,7 +172,7 @@ public class ImageReader implements AutoCloseable {
     * </p>
     *
     * <p>Attempting to acquire more than {@code maxImages} concurrently will result in the
     * acquire function throwing a {@link MaxImagesAcquiredException}. Furthermore,
     * acquire function throwing a {@link IllegalStateException}. Furthermore,
     * while the max number of images have been acquired by the ImageReader user, the producer
     * enqueueing additional images may stall until at least one image has been released. </p>
     *
@@ -243,26 +220,26 @@ public class ImageReader implements AutoCloseable {
     * {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected.
     * </p>
     * <p>
     * This operation will fail by throwing an {@link MaxImagesAcquiredException} if
     * This operation will fail by throwing an {@link IllegalStateException} if
     * {@code maxImages} have been acquired with {@link #acquireLatestImage} or
     * {@link #acquireNextImage}. In particular a sequence of {@link #acquireLatestImage}
     * calls greater than {@link #getMaxImages} without calling {@link Image#close} in-between
     * will exhaust the underlying queue. At such a time, {@link MaxImagesAcquiredException}
     * will exhaust the underlying queue. At such a time, {@link IllegalStateException}
     * will be thrown until more images are
     * released with {@link Image#close}.
     * </p>
     *
     * @return latest frame of image data, or {@code null} if no image data is available.
     * @throws MaxImagesAcquiredException if too many images are currently acquired
     * @throws IllegalStateException if too many images are currently acquired
     */
    public Image acquireLatestImage() throws MaxImagesAcquiredException {
    public Image acquireLatestImage() {
        Image image = acquireNextImage();
        if (image == null) {
            return null;
        }
        try {
            for (;;) {
                Image next = acquireNextImageNoThrow();
                Image next = acquireNextImageNoThrowISE();
                if (next == null) {
                    Image result = image;
                    image = null;
@@ -278,12 +255,48 @@ public class ImageReader implements AutoCloseable {
        }
    }

    private Image acquireNextImageNoThrow() {
        try {
            return acquireNextImage();
        } catch (MaxImagesAcquiredException ex) {
            return null;
    /**
     * Don't throw IllegalStateException if there are too many images acquired.
     *
     * @return Image if acquiring succeeded, or null otherwise.
     *
     * @hide
     */
    public Image acquireNextImageNoThrowISE() {
        SurfaceImage si = new SurfaceImage();
        return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null;
    }

    /**
     * Attempts to acquire the next image from the underlying native implementation.
     *
     * <p>
     * Note that unexpected failures will throw at the JNI level.
     * </p>
     *
     * @param si A blank SurfaceImage.
     * @return One of the {@code ACQUIRE_*} codes that determine success or failure.
     *
     * @see #ACQUIRE_MAX_IMAGES
     * @see #ACQUIRE_NO_BUFS
     * @see #ACQUIRE_SUCCESS
     */
    private int acquireNextSurfaceImage(SurfaceImage si) {

        int status = nativeImageSetup(si);

        switch (status) {
            case ACQUIRE_SUCCESS:
                si.createSurfacePlanes();
                si.setImageValid(true);
            case ACQUIRE_NO_BUFS:
            case ACQUIRE_MAX_IMAGES:
                break;
            default:
                throw new AssertionError("Unknown nativeImageSetup return code " + status);
        }

        return status;
    }

    /**
@@ -301,28 +314,36 @@ public class ImageReader implements AutoCloseable {
     * </p>
     *
     * <p>
     * This operation will fail by throwing an {@link MaxImagesAcquiredException} if
     * This operation will fail by throwing an {@link IllegalStateException} if
     * {@code maxImages} have been acquired with {@link #acquireNextImage} or
     * {@link #acquireLatestImage}. In particular a sequence of {@link #acquireNextImage} or
     * {@link #acquireLatestImage} calls greater than {@link #getMaxImages maxImages} without
     * calling {@link Image#close} in-between will exhaust the underlying queue. At such a time,
     * {@link MaxImagesAcquiredException} will be thrown until more images are released with
     * {@link IllegalStateException} will be thrown until more images are released with
     * {@link Image#close}.
     * </p>
     *
     * @return a new frame of image data, or {@code null} if no image data is available.
     * @throws MaxImagesAcquiredException if {@code maxImages} images are currently acquired
     * @throws IllegalStateException if {@code maxImages} images are currently acquired
     * @see #acquireLatestImage
     */
    public Image acquireNextImage() throws MaxImagesAcquiredException {
    public Image acquireNextImage() {
        SurfaceImage si = new SurfaceImage();
        if (nativeImageSetup(si)) {
            // create SurfacePlane objects
            si.createSurfacePlanes();
            si.setImageValid(true);
        int status = acquireNextSurfaceImage(si);

        switch (status) {
            case ACQUIRE_SUCCESS:
                return si;
        }
            case ACQUIRE_NO_BUFS:
                return null;
            case ACQUIRE_MAX_IMAGES:
                throw new IllegalStateException(
                        String.format(
                                "maxImages (%d) has already been acquired, " +
                                "call #close before acquiring more.", mMaxImages));
            default:
                throw new AssertionError("Unknown nativeImageSetup return code " + status);
        }
    }

    /**
@@ -658,7 +679,15 @@ 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 boolean nativeImageSetup(Image i);

    /**
     * @return A return code {@code ACQUIRE_*}
     *
     * @see #ACQUIRE_SUCCESS
     * @see #ACQUIRE_NO_BUFS
     * @see #ACQUIRE_MAX_IMAGES
     */
    private synchronized native int nativeImageSetup(Image i);

    /**
     * We use a class initializer to allow the native code to cache some
+17 −18
Original line number Diff line number Diff line
@@ -43,13 +43,16 @@

using namespace android;

static const char* const MaxImagesAcquiredException =
    "android/media/ImageReader$MaxImagesAcquiredException";

enum {
    IMAGE_READER_MAX_NUM_PLANES = 3,
};

enum {
    ACQUIRE_SUCCESS = 0,
    ACQUIRE_NO_BUFFERS = 1,
    ACQUIRE_MAX_IMAGES = 2,
};

static struct {
    jfieldID mNativeContext;
    jmethodID postEventFromNative;
@@ -685,14 +688,14 @@ static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image)
    ctx->returnLockedBuffer(buffer);
}

static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz,
static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz,
                                             jobject image)
{
    ALOGV("%s:", __FUNCTION__);
    JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
    if (ctx == NULL) {
        jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
        return false;
        return -1;
    }

    CpuConsumer* consumer = ctx->getCpuConsumer();
@@ -700,27 +703,22 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz,
    if (buffer == NULL) {
        ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
            " maxImages buffers");
        jniThrowException(env, MaxImagesAcquiredException,
                  "Too many outstanding images, close existing images"
                  " to be able to acquire more.");
        return false;
        return ACQUIRE_MAX_IMAGES;
    }
    status_t res = consumer->lockNextBuffer(buffer);
    if (res != NO_ERROR) {
        if (res != BAD_VALUE /*no buffers*/) {
            if (res == NOT_ENOUGH_DATA) {
                jniThrowException(env, MaxImagesAcquiredException,
                          "Too many outstanding images, close existing images"
                          " to be able to acquire more.");
                return ACQUIRE_MAX_IMAGES;
            } else {
                ALOGE("%s Fail to lockNextBuffer with error: %d ",
                      __FUNCTION__, res);
                jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
                jniThrowExceptionFmt(env, "java/lang/AssertionError",
                          "Unknown error (%d) when we tried to lock buffer.",
                          res);
            }
        }
        return false;
        return ACQUIRE_NO_BUFFERS;
    }

    // Check if the left-top corner of the crop rect is origin, we currently assume this point is
@@ -730,7 +728,7 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz,
        ALOGE("crop left: %d, top = %d", lt.x, lt.y);
        jniThrowException(env, "java/lang/UnsupportedOperationException",
                          "crop left top corner need to at origin");
        return false;
        return -1;
    }

    // Check if the producer buffer configurations match what ImageReader configured.
@@ -761,6 +759,7 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz,
        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
                "Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
                outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
        return -1;
    }

    if (ctx->getBufferFormat() != buffer->format) {
@@ -777,14 +776,14 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz,
                buffer->format, ctx->getBufferFormat());
        jniThrowException(env, "java/lang/UnsupportedOperationException",
                msg.string());
        return false;
        return -1;
    }
    // Set SurfaceImage instance member variables
    Image_setBuffer(env, image, buffer);
    env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
            static_cast<jlong>(buffer->timestamp));

    return true;
    return ACQUIRE_SUCCESS;
}

static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz)
@@ -855,7 +854,7 @@ static JNINativeMethod gImageReaderMethods[] = {
    {"nativeInit",             "(Ljava/lang/Object;IIII)V",  (void*)ImageReader_init },
    {"nativeClose",            "()V",                        (void*)ImageReader_close },
    {"nativeReleaseImage",     "(Landroid/media/Image;)V",   (void*)ImageReader_imageRelease },
    {"nativeImageSetup",       "(Landroid/media/Image;)Z",    (void*)ImageReader_imageSetup },
    {"nativeImageSetup",       "(Landroid/media/Image;)I",    (void*)ImageReader_imageSetup },
    {"nativeGetSurface",       "()Landroid/view/Surface;",   (void*)ImageReader_getSurface },
};

Loading