diff --git a/api/current.txt b/api/current.txt index b0a18dee29dbd..424a376b98715 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12348,8 +12348,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(); @@ -12360,13 +12360,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); } diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index 8278579c0fa8c..a2e9ae89e2557 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -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(); } diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 915b09e5a37cf..a346e176984e4 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -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}.
+ * throw an {@link IllegalStateException}. * * @see ImageReader */ diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index e5bcb3e5b0cc5..aee83627bb0ee 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -49,43 +49,20 @@ import java.nio.ByteOrder; public class ImageReader implements AutoCloseable { /** - *- * 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}. - *
- * - *- * To recover from this exception, release existing {@link Image images} back to the - * reader with {@link Image#close}. - *
- * - * @see Image#close - * @see ImageReader#acquireLatestImage - * @see ImageReader#acquireNextImage + * Returned by nativeImageSetup when acquiring the image was successful. */ - public static class MaxImagesAcquiredException extends Exception { - /** - * Suppress Eclipse warnings - */ - 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_SUCCESS = 0; + /** + * Returned by nativeImageSetup when we couldn't acquire the buffer, + * because there were no buffers available to acquire. + */ + 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; /** *Create a new reader for images of the desired size and format.
@@ -195,7 +172,7 @@ public class ImageReader implements AutoCloseable { * * *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.
* @@ -243,26 +220,26 @@ public class ImageReader implements AutoCloseable { * {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected. * *- * 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}. *
* * @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. + * + *+ * Note that unexpected failures will throw at the JNI level. + *
+ * + * @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 { * * *- * 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}. *
* * @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); - return si; + 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); } - return null; } /** @@ -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 diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 56ae6b44a03fa..e0195c6734c40 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -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