static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image)
{
    ALOGV("%s:", __FUNCTION__);
    JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
    if (ctx == NULL) {
        ALOGW("ImageReader#close called before Image#close, consider calling Image#close first");
        return;
    }

    CpuConsumer* consumer = ctx->getCpuConsumer();
    CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image);
    if (!buffer) {
        ALOGW("Image already released!!!");
        return;
    }
    consumer->unlockBuffer(*buffer);
    Image_setBuffer(env, image, NULL);
    ctx->returnLockedBuffer(buffer);
}
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 -1;
    }

    CpuConsumer* consumer = ctx->getCpuConsumer();
    CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer();
    if (buffer == NULL) {
        ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
            " maxImages buffers");
        return ACQUIRE_MAX_IMAGES;
    }
    status_t res = consumer->lockNextBuffer(buffer);
    if (res != NO_ERROR) {
        ctx->returnLockedBuffer(buffer);
        if (res != BAD_VALUE /*no buffers*/) {
            if (res == NOT_ENOUGH_DATA) {
                return ACQUIRE_MAX_IMAGES;
            } else {
                ALOGE("%s Fail to lockNextBuffer with error: %d ",
                      __FUNCTION__, res);
                jniThrowExceptionFmt(env, "java/lang/AssertionError",
                          "Unknown error (%d) when we tried to lock buffer.",
                          res);
            }
        }
        return ACQUIRE_NO_BUFFERS;
    }

    if (buffer->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
        jniThrowException(env, "java/lang/UnsupportedOperationException",
                "NV21 format is not supported by ImageReader");
        return -1;
    }

    // Check if the left-top corner of the crop rect is origin, we currently assume this point is
    // zero, will revist this once this assumption turns out problematic.
    Point lt = buffer->crop.leftTop();
    if (lt.x != 0 || lt.y != 0) {
        jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
                "crop left top corner [%d, %d] need to be at origin", lt.x, lt.y);
        return -1;
    }

    // Check if the producer buffer configurations match what ImageReader configured.
    // We want to fail for the very first image because this case is too bad.
    int outputWidth = buffer->width;
    int outputHeight = buffer->height;

    // Correct width/height when crop is set.
    if (!buffer->crop.isEmpty()) {
        outputWidth = buffer->crop.getWidth();
        outputHeight = buffer->crop.getHeight();
    }

    int imgReaderFmt = ctx->getBufferFormat();
    int imageReaderWidth = ctx->getBufferWidth();
    int imageReaderHeight = ctx->getBufferHeight();
    if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) &&
            (imageReaderWidth != outputWidth || imageReaderHeight > outputHeight)) {
        /**
         * For video decoder, the buffer height is actually the vertical stride,
         * which is always >= actual image height. For future, decoder need provide
         * right crop rectangle to CpuConsumer to indicate the actual image height,
         * see bug 9563986. After this bug is fixed, we can enforce the height equal
         * check. Right now, only make sure buffer height is no less than ImageReader
         * height.
         */
        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
                "Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
                outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
        return -1;
    }

    int bufFmt = buffer->format;
    if (imgReaderFmt != bufFmt) {

        if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
                HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
            // Special casing for when producer switches to a format compatible with flexible YUV
            // (HAL_PIXEL_FORMAT_YCbCr_420_888).
            ctx->setBufferFormat(bufFmt);
            ALOGD("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
        } else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB && bufFmt == HAL_PIXEL_FORMAT_RGBA_8888) {
            // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
            // write limitations for (b/17379185).
            ALOGD("%s: Receiving JPEG in HAL_PIXEL_FORMAT_RGBA_8888 buffer.", __FUNCTION__);
        } else {
            // Return the buffer to the queue.
            consumer->unlockBuffer(*buffer);
            ctx->returnLockedBuffer(buffer);

            // Throw exception
            ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
                    buffer->format, ctx->getBufferFormat());
            String8 msg;
            msg.appendFormat("The producer output buffer format 0x%x doesn't "
                    "match the ImageReader's configured buffer format 0x%x.",
                    buffer->format, ctx->getBufferFormat());
            jniThrowException(env, "java/lang/UnsupportedOperationException",
                    msg.string());
            return -1;
        }
    }
    // Set SurfaceImage instance member variables
    Image_setBuffer(env, image, buffer);
    env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
            static_cast<jlong>(buffer->timestamp));

    return ACQUIRE_SUCCESS;
}