xref: /aosp_15_r20/frameworks/base/media/jni/android_media_ImageWriter.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ImageWriter_JNI"
19 #include "android_media_Utils.h"
20 
21 #include <utils/Condition.h>
22 #include <utils/Log.h>
23 #include <utils/Mutex.h>
24 #include <utils/String8.h>
25 #include <utils/Thread.h>
26 
27 #include <gui/Surface.h>
28 #include <ui/PublicFormat.h>
29 #include <android_runtime/AndroidRuntime.h>
30 #include <android_runtime/android_view_Surface.h>
31 #include <android_runtime/android_graphics_GraphicBuffer.h>
32 #include <android_runtime/android_hardware_HardwareBuffer.h>
33 #include <private/android/AHardwareBufferHelpers.h>
34 #include <jni.h>
35 #include <nativehelper/JNIHelp.h>
36 
37 #include <stdint.h>
38 #include <inttypes.h>
39 #include <android/hardware_buffer_jni.h>
40 
41 #include <deque>
42 
43 #define IMAGE_BUFFER_JNI_ID           "mNativeBuffer"
44 // ----------------------------------------------------------------------------
45 
46 using namespace android;
47 
48 static struct {
49     jmethodID postEventFromNative;
50     jfieldID mWriterFormat;
51 } gImageWriterClassInfo;
52 
53 static struct {
54     jfieldID mDataSpace;
55     jfieldID mNativeBuffer;
56     jfieldID mNativeFenceFd;
57     jfieldID mPlanes;
58 } gSurfaceImageClassInfo;
59 
60 static struct {
61     jclass clazz;
62     jmethodID ctor;
63 } gSurfacePlaneClassInfo;
64 
65 // ----------------------------------------------------------------------------
66 
67 class JNIImageWriterContext : public SurfaceListener {
68 public:
69     JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz);
70 
71     virtual ~JNIImageWriterContext();
72 
73     // Implementation of SurfaceListener, used to notify the ImageWriter that the consumer
74     // has returned a buffer and it is ready for ImageWriter to dequeue.
75     virtual void onBufferReleased();
needsReleaseNotify()76     virtual bool needsReleaseNotify() override { return true; };
onBuffersDiscarded(const std::vector<sp<GraphicBuffer>> &)77     virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {};
onBufferDetached(int)78     virtual void onBufferDetached(int /*slot*/) override {};
79 
setProducer(const sp<Surface> & producer)80     void setProducer(const sp<Surface>& producer) { mProducer = producer; }
getProducer()81     Surface* getProducer() { return mProducer.get(); }
82 
setBufferFormat(int format)83     void setBufferFormat(int format) { mFormat = format; }
getBufferFormat()84     int getBufferFormat() { return mFormat; }
85 
setBufferWidth(int width)86     void setBufferWidth(int width) { mWidth = width; }
getBufferWidth()87     int getBufferWidth() { return mWidth; }
88 
setBufferHeight(int height)89     void setBufferHeight(int height) { mHeight = height; }
getBufferHeight()90     int getBufferHeight() { return mHeight; }
91 
setBufferDataSpace(android_dataspace dataSpace)92     void setBufferDataSpace(android_dataspace dataSpace) { mDataSpace = dataSpace; }
getBufferDataSpace()93     android_dataspace getBufferDataSpace() { return mDataSpace; }
94 
queueAttachedFlag(bool isAttached)95     void queueAttachedFlag(bool isAttached) {
96         Mutex::Autolock l(mAttachedFlagQueueLock);
97         mAttachedFlagQueue.push_back(isAttached);
98     }
dequeueAttachedFlag()99     void dequeueAttachedFlag() {
100         Mutex::Autolock l(mAttachedFlagQueueLock);
101         mAttachedFlagQueue.pop_back();
102     }
103 private:
104     static JNIEnv* getJNIEnv(bool* needsDetach);
105     static void detachJNI();
106 
107     sp<Surface> mProducer;
108     jobject mWeakThiz;
109     jclass mClazz;
110     int mFormat;
111     int mWidth;
112     int mHeight;
113     android_dataspace mDataSpace;
114 
115     // Class for a shared thread used to detach buffers from buffer queues
116     // to discard buffers after consumers are done using them.
117     // This is needed because detaching buffers in onBufferReleased callback
118     // can lead to deadlock when consumer/producer are on the same process.
119     class BufferDetacher {
120     public:
121         // Called by JNIImageWriterContext ctor. Will start the thread for first ref.
122         void addRef();
123         // Called by JNIImageWriterContext dtor. Will stop the thread after ref goes to 0.
124         void removeRef();
125         // Called by onBufferReleased to signal this thread to detach a buffer
126         void detach(wp<Surface>);
127 
128     private:
129 
130         class DetachThread : public Thread {
131         public:
DetachThread()132             DetachThread() : Thread(/*canCallJava*/false) {};
133 
134             void detach(wp<Surface>);
135 
136             virtual void requestExit() override;
137 
138         private:
139             virtual bool threadLoop() override;
140 
141             Mutex     mLock;
142             Condition mCondition;
143             std::deque<wp<Surface>> mQueue;
144 
145             static const nsecs_t kWaitDuration = 500000000; // 500 ms
146         };
147         sp<DetachThread> mThread;
148 
149         Mutex     mLock;
150         int       mRefCount = 0;
151     };
152 
153     static BufferDetacher sBufferDetacher;
154 
155     // Buffer queue guarantees both producer and consumer side buffer flows are
156     // in order. See b/19977520. As a result, we can use a queue here.
157     Mutex mAttachedFlagQueueLock;
158     std::deque<bool> mAttachedFlagQueue;
159 };
160 
161 JNIImageWriterContext::BufferDetacher JNIImageWriterContext::sBufferDetacher;
162 
addRef()163 void JNIImageWriterContext::BufferDetacher::addRef() {
164     Mutex::Autolock l(mLock);
165     mRefCount++;
166     if (mRefCount == 1) {
167         mThread = new DetachThread();
168         mThread->run("BufDtchThrd");
169     }
170 }
171 
removeRef()172 void JNIImageWriterContext::BufferDetacher::removeRef() {
173     Mutex::Autolock l(mLock);
174     mRefCount--;
175     if (mRefCount == 0) {
176         mThread->requestExit();
177         mThread->join();
178         mThread.clear();
179     }
180 }
181 
detach(wp<Surface> bq)182 void JNIImageWriterContext::BufferDetacher::detach(wp<Surface> bq) {
183     Mutex::Autolock l(mLock);
184     if (mThread == nullptr) {
185         ALOGE("%s: buffer detach thread is gone!", __FUNCTION__);
186         return;
187     }
188     mThread->detach(bq);
189 }
190 
detach(wp<Surface> bq)191 void JNIImageWriterContext::BufferDetacher::DetachThread::detach(wp<Surface> bq) {
192     Mutex::Autolock l(mLock);
193     mQueue.push_back(bq);
194     mCondition.signal();
195 }
196 
requestExit()197 void JNIImageWriterContext::BufferDetacher::DetachThread::requestExit() {
198     Thread::requestExit();
199     {
200         Mutex::Autolock l(mLock);
201         mQueue.clear();
202     }
203     mCondition.signal();
204 }
205 
threadLoop()206 bool JNIImageWriterContext::BufferDetacher::DetachThread::threadLoop() {
207     Mutex::Autolock l(mLock);
208     mCondition.waitRelative(mLock, kWaitDuration);
209 
210     while (!mQueue.empty()) {
211         if (exitPending()) {
212             return false;
213         }
214 
215         wp<Surface> wbq = mQueue.front();
216         mQueue.pop_front();
217         sp<Surface> bq = wbq.promote();
218         if (bq != nullptr) {
219             sp<Fence> fence;
220             sp<GraphicBuffer> buffer;
221             ALOGV("%s: One buffer is detached", __FUNCTION__);
222             mLock.unlock();
223             bq->detachNextBuffer(&buffer, &fence);
224             mLock.lock();
225         }
226     }
227     return !exitPending();
228 }
229 
JNIImageWriterContext(JNIEnv * env,jobject weakThiz,jclass clazz)230 JNIImageWriterContext::JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz) :
231         mWeakThiz(env->NewGlobalRef(weakThiz)),
232         mClazz((jclass)env->NewGlobalRef(clazz)),
233         mFormat(0),
234         mWidth(-1),
235         mHeight(-1) {
236     sBufferDetacher.addRef();
237 }
238 
~JNIImageWriterContext()239 JNIImageWriterContext::~JNIImageWriterContext() {
240     ALOGV("%s", __FUNCTION__);
241     bool needsDetach = false;
242     JNIEnv* env = getJNIEnv(&needsDetach);
243     if (env != NULL) {
244         env->DeleteGlobalRef(mWeakThiz);
245         env->DeleteGlobalRef(mClazz);
246     } else {
247         ALOGW("leaking JNI object references");
248     }
249     if (needsDetach) {
250         detachJNI();
251     }
252 
253     mProducer.clear();
254     sBufferDetacher.removeRef();
255 }
256 
getJNIEnv(bool * needsDetach)257 JNIEnv* JNIImageWriterContext::getJNIEnv(bool* needsDetach) {
258     ALOGV("%s", __FUNCTION__);
259     LOG_ALWAYS_FATAL_IF(needsDetach == NULL, "needsDetach is null!!!");
260     *needsDetach = false;
261     JNIEnv* env = AndroidRuntime::getJNIEnv();
262     if (env == NULL) {
263         JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
264         JavaVM* vm = AndroidRuntime::getJavaVM();
265         int result = vm->AttachCurrentThread(&env, (void*) &args);
266         if (result != JNI_OK) {
267             ALOGE("thread attach failed: %#x", result);
268             return NULL;
269         }
270         *needsDetach = true;
271     }
272     return env;
273 }
274 
detachJNI()275 void JNIImageWriterContext::detachJNI() {
276     ALOGV("%s", __FUNCTION__);
277     JavaVM* vm = AndroidRuntime::getJavaVM();
278     int result = vm->DetachCurrentThread();
279     if (result != JNI_OK) {
280         ALOGE("thread detach failed: %#x", result);
281     }
282 }
283 
onBufferReleased()284 void JNIImageWriterContext::onBufferReleased() {
285     ALOGV("%s: buffer released", __FUNCTION__);
286     bool needsDetach = false;
287     JNIEnv* env = getJNIEnv(&needsDetach);
288 
289     bool bufferIsAttached = false;
290     {
291         Mutex::Autolock l(mAttachedFlagQueueLock);
292         if (!mAttachedFlagQueue.empty()) {
293             bufferIsAttached = mAttachedFlagQueue.front();
294             mAttachedFlagQueue.pop_front();
295         } else {
296             ALOGW("onBufferReleased called with no attached flag queued");
297         }
298     }
299 
300     if (env != NULL) {
301         // Detach the buffer every time when a buffer consumption is done,
302         // need let this callback give a BufferItem, then only detach if it was attached to this
303         // Writer. see b/19977520
304         if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED || bufferIsAttached) {
305             sBufferDetacher.detach(mProducer);
306         }
307 
308         env->CallStaticVoidMethod(mClazz, gImageWriterClassInfo.postEventFromNative, mWeakThiz);
309     } else {
310         ALOGW("onBufferReleased event will not posted");
311     }
312 
313     if (needsDetach) {
314         detachJNI();
315     }
316 }
317 
318 // ----------------------------------------------------------------------------
319 
320 extern "C" {
321 
322 // -------------------------------Private method declarations--------------
323 
324 static void Image_setNativeContext(JNIEnv* env, jobject thiz,
325         sp<GraphicBuffer> buffer, int fenceFd, int dataSpace);
326 static void Image_getNativeContext(JNIEnv* env, jobject thiz,
327         GraphicBuffer** buffer, int* fenceFd);
328 static void Image_unlockIfLocked(JNIEnv* env, jobject thiz);
329 
330 // --------------------------ImageWriter methods---------------------------------------
331 
ImageWriter_classInit(JNIEnv * env,jclass clazz)332 static void ImageWriter_classInit(JNIEnv* env, jclass clazz) {
333     ALOGV("%s:", __FUNCTION__);
334     jclass imageClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage");
335     LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
336             "can't find android/media/ImageWriter$WriterSurfaceImage");
337 
338     gSurfaceImageClassInfo.mDataSpace = env->GetFieldID(
339             imageClazz, "mDataSpace", "I");
340     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mDataSpace == NULL,
341             "can't find android/media/ImageWriter$WriterSurfaceImage.mDataSpace");
342 
343     gSurfaceImageClassInfo.mNativeBuffer = env->GetFieldID(
344             imageClazz, IMAGE_BUFFER_JNI_ID, "J");
345     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeBuffer == NULL,
346             "can't find android/media/ImageWriter$WriterSurfaceImage.%s", IMAGE_BUFFER_JNI_ID);
347 
348     gSurfaceImageClassInfo.mNativeFenceFd = env->GetFieldID(
349             imageClazz, "mNativeFenceFd", "I");
350     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeFenceFd == NULL,
351             "can't find android/media/ImageWriter$WriterSurfaceImage.mNativeFenceFd");
352 
353     gSurfaceImageClassInfo.mPlanes = env->GetFieldID(
354             imageClazz, "mPlanes", "[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;");
355     LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mPlanes == NULL,
356             "can't find android/media/ImageWriter$WriterSurfaceImage.mPlanes");
357 
358     gImageWriterClassInfo.postEventFromNative = env->GetStaticMethodID(
359             clazz, "postEventFromNative", "(Ljava/lang/Object;)V");
360     LOG_ALWAYS_FATAL_IF(gImageWriterClassInfo.postEventFromNative == NULL,
361                         "can't find android/media/ImageWriter.postEventFromNative");
362 
363     gImageWriterClassInfo.mWriterFormat = env->GetFieldID(
364             clazz, "mWriterFormat", "I");
365     LOG_ALWAYS_FATAL_IF(gImageWriterClassInfo.mWriterFormat == NULL,
366                         "can't find android/media/ImageWriter.mWriterFormat");
367 
368     jclass planeClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage$SurfacePlane");
369     LOG_ALWAYS_FATAL_IF(planeClazz == NULL, "Can not find SurfacePlane class");
370     // FindClass only gives a local reference of jclass object.
371     gSurfacePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
372     gSurfacePlaneClassInfo.ctor = env->GetMethodID(gSurfacePlaneClassInfo.clazz, "<init>",
373             "(Landroid/media/ImageWriter$WriterSurfaceImage;IILjava/nio/ByteBuffer;)V");
374     LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL,
375             "Can not find SurfacePlane constructor");
376 }
377 
ImageWriter_init(JNIEnv * env,jobject thiz,jobject weakThiz,jobject jsurface,jint maxImages,jint userWidth,jint userHeight,jboolean useSurfaceImageFormatInfo,jint hardwareBufferFormat,jint dataSpace,jlong ndkUsage)378 static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
379         jint maxImages, jint userWidth, jint userHeight, jboolean useSurfaceImageFormatInfo,
380         jint hardwareBufferFormat, jint dataSpace, jlong ndkUsage) {
381     status_t res;
382 
383     ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
384 
385     sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
386     if (surface == NULL) {
387         jniThrowException(env,
388                 "java/lang/IllegalArgumentException",
389                 "The surface has been released");
390         return 0;
391      }
392     sp<IGraphicBufferProducer> bufferProducer = surface->getIGraphicBufferProducer();
393 
394     jclass clazz = env->GetObjectClass(thiz);
395     if (clazz == NULL) {
396         jniThrowRuntimeException(env, "Can't find android/graphics/ImageWriter");
397         return 0;
398     }
399     sp<JNIImageWriterContext> ctx(new JNIImageWriterContext(env, weakThiz, clazz));
400 
401     sp<Surface> producer = new Surface(bufferProducer, /*controlledByApp*/false);
402     ctx->setProducer(producer);
403     /**
404      * NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not connectable
405      * after disconnect. MEDIA or CAMERA are treated the same internally. The producer listener
406      * will be cleared after disconnect call.
407      */
408     res = producer->connect(/*api*/NATIVE_WINDOW_API_CAMERA, /*listener*/ctx);
409     if (res != OK) {
410         ALOGE("%s: Connecting to surface producer interface failed: %s (%d)",
411                 __FUNCTION__, strerror(-res), res);
412         jniThrowRuntimeException(env, "Failed to connect to native window");
413         return 0;
414     }
415 
416     jlong nativeCtx = reinterpret_cast<jlong>(ctx.get());
417 
418     // Get the dimension and format of the producer.
419     sp<ANativeWindow> anw = producer;
420     int32_t width, height, surfaceHalFormat;
421     int32_t surfaceFormat = 0;
422     int32_t surfaceDataspace = 0;
423     if (userWidth < 0) {
424         if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) {
425             ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res);
426             jniThrowRuntimeException(env, "Failed to query Surface width");
427             return 0;
428         }
429     } else {
430         width = userWidth;
431     }
432 
433     ctx->setBufferWidth(width);
434 
435     if (userHeight < 0) {
436         if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) {
437             ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res);
438             jniThrowRuntimeException(env, "Failed to query Surface height");
439             return 0;
440         }
441     } else {
442         height = userHeight;
443     }
444     ctx->setBufferHeight(height);
445 
446     if ((userWidth > 0) && (userHeight > 0)) {
447         res = native_window_set_buffers_user_dimensions(anw.get(), userWidth, userHeight);
448         if (res != OK) {
449             ALOGE("%s: Set buffer dimensions failed: %s (%d)", __FUNCTION__, strerror(-res), res);
450             jniThrowRuntimeException(env, "Set buffer dimensions failed");
451             return 0;
452         }
453     }
454 
455     // Query surface format if no valid user format is specified, otherwise, override surface format
456     // with user format.
457     if (useSurfaceImageFormatInfo) {
458         // retrieve hal format
459         if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceHalFormat)) != OK) {
460             ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
461             jniThrowRuntimeException(env, "Failed to query Surface format");
462             return 0;
463         }
464         if ((res = anw->query(
465                 anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, &surfaceDataspace)) != OK) {
466             ALOGE("%s: Query Surface dataspace failed: %s (%d)", __FUNCTION__, strerror(-res), res);
467             jniThrowRuntimeException(env, "Failed to query Surface dataspace");
468             return 0;
469         }
470     } else {
471         // Set consumer buffer format to user specified format
472         android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace);
473         res = native_window_set_buffers_format(anw.get(), hardwareBufferFormat);
474         if (res != OK) {
475             ALOGE("%s: Unable to configure consumer native buffer format to %#x",
476                     __FUNCTION__, hardwareBufferFormat);
477             jniThrowRuntimeException(env, "Failed to set Surface format");
478             return 0;
479         }
480 
481         res = native_window_set_buffers_data_space(anw.get(), nativeDataspace);
482         if (res != OK) {
483             ALOGE("%s: Unable to configure consumer dataspace %#x",
484                     __FUNCTION__, nativeDataspace);
485             jniThrowRuntimeException(env, "Failed to set Surface dataspace");
486             return 0;
487         }
488         ctx->setBufferDataSpace(nativeDataspace);
489         surfaceDataspace = dataSpace;
490         surfaceHalFormat = hardwareBufferFormat;
491     }
492 
493     ctx->setBufferFormat(surfaceHalFormat);
494     ctx->setBufferDataSpace(static_cast<android_dataspace>(surfaceDataspace));
495 
496     // update class.mWriterFormat
497     surfaceFormat = static_cast<int32_t>(mapHalFormatDataspaceToPublicFormat(
498                 surfaceHalFormat, static_cast<android_dataspace>(surfaceDataspace)));
499     env->SetIntField(thiz,
500             gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
501 
502     // ndkUsage == -1 means setUsage in ImageWriter class is not called.
503     // skip usage setting if setUsage in ImageWriter is not called and imageformat is opaque.
504     if (!(ndkUsage == -1 && isFormatOpaque(surfaceHalFormat))) {
505         if (ndkUsage == -1) {
506             ndkUsage = GRALLOC_USAGE_SW_WRITE_OFTEN;
507         }
508         res = native_window_set_usage(anw.get(), ndkUsage);
509         if (res != OK) {
510             ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
511                   __FUNCTION__, static_cast<unsigned int>(ndkUsage),
512                   surfaceFormat, strerror(-res), res);
513             jniThrowRuntimeException(env,
514                                      "Failed to SW_WRITE_OFTEN configure usage");
515             return 0;
516         }
517     }
518 
519     int minUndequeuedBufferCount = 0;
520     res = anw->query(anw.get(),
521                 NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufferCount);
522     if (res != OK) {
523         ALOGE("%s: Query producer undequeued buffer count failed: %s (%d)",
524                 __FUNCTION__, strerror(-res), res);
525         jniThrowRuntimeException(env, "Query producer undequeued buffer count failed");
526         return 0;
527      }
528 
529     size_t totalBufferCount = maxImages + minUndequeuedBufferCount;
530     res = native_window_set_buffer_count(anw.get(), totalBufferCount);
531     if (res != OK) {
532         ALOGE("%s: Set buffer count failed: %s (%d)", __FUNCTION__, strerror(-res), res);
533         jniThrowRuntimeException(env, "Set buffer count failed");
534         return 0;
535     }
536 
537     if (ctx != 0) {
538         ctx->incStrong((void*)ImageWriter_init);
539     }
540     return nativeCtx;
541 }
542 
ImageWriter_dequeueImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject image)543 static void ImageWriter_dequeueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image) {
544     ALOGV("%s", __FUNCTION__);
545     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
546     if (ctx == NULL || thiz == NULL) {
547         jniThrowException(env, "java/lang/IllegalStateException",
548                 "ImageWriterContext is not initialized");
549         return;
550     }
551 
552     sp<ANativeWindow> anw = ctx->getProducer();
553     android_native_buffer_t *anb = NULL;
554     int fenceFd = -1;
555     status_t res = anw->dequeueBuffer(anw.get(), &anb, &fenceFd);
556     if (res != OK) {
557         ALOGE("%s: Dequeue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
558         switch (res) {
559             case NO_INIT:
560                 jniThrowException(env, "java/lang/IllegalStateException",
561                     "Surface has been abandoned");
562                 break;
563             default:
564                 // TODO: handle other error cases here.
565                 jniThrowRuntimeException(env, "dequeue buffer failed");
566         }
567         return;
568     }
569     // New GraphicBuffer object doesn't own the handle, thus the native buffer
570     // won't be freed when this object is destroyed.
571     sp<GraphicBuffer> buffer(GraphicBuffer::from(anb));
572 
573     // Note that:
574     // 1. No need to lock buffer now, will only lock it when the first getPlanes() is called.
575     // 2. Fence will be saved to mNativeFenceFd, and will consumed by lock/queue/cancel buffer
576     //    later.
577     // 3. need use lockAsync here, as it will handle the dequeued fence for us automatically.
578 
579     // Finally, set the native info into image object.
580     Image_setNativeContext(env, image, buffer, fenceFd, ctx->getBufferDataSpace());
581 }
582 
ImageWriter_close(JNIEnv * env,jobject thiz,jlong nativeCtx)583 static void ImageWriter_close(JNIEnv* env, jobject thiz, jlong nativeCtx) {
584     ALOGV("%s:", __FUNCTION__);
585     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
586     if (ctx == NULL || thiz == NULL) {
587         // ImageWriter is already closed.
588         return;
589     }
590 
591     ANativeWindow* producer = ctx->getProducer();
592     if (producer != NULL) {
593         /**
594          * NATIVE_WINDOW_API_CPU isn't a good choice here, as it makes the bufferQueue not
595          * connectable after disconnect. MEDIA or CAMERA are treated the same internally.
596          * The producer listener will be cleared after disconnect call.
597          */
598         status_t res = native_window_api_disconnect(producer, /*api*/NATIVE_WINDOW_API_CAMERA);
599         /**
600          * This is not an error. if client calling process dies, the window will
601          * also die and all calls to it will return DEAD_OBJECT, thus it's already
602          * "disconnected"
603          */
604         if (res == DEAD_OBJECT) {
605             ALOGW("%s: While disconnecting ImageWriter from native window, the"
606                     " native window died already", __FUNCTION__);
607         } else if (res != OK) {
608             ALOGE("%s: native window disconnect failed: %s (%d)",
609                     __FUNCTION__, strerror(-res), res);
610             jniThrowRuntimeException(env, "Native window disconnect failed");
611             return;
612         }
613     }
614 
615     ctx->decStrong((void*)ImageWriter_init);
616 }
617 
ImageWriter_cancelImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject image)618 static void ImageWriter_cancelImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image) {
619     ALOGV("%s", __FUNCTION__);
620     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
621     if (ctx == NULL || thiz == NULL) {
622         ALOGW("ImageWriter#close called before Image#close, consider calling Image#close first");
623         return;
624     }
625 
626     sp<ANativeWindow> anw = ctx->getProducer();
627 
628     GraphicBuffer *buffer = NULL;
629     int fenceFd = -1;
630     Image_getNativeContext(env, image, &buffer, &fenceFd);
631     if (buffer == NULL) {
632         // Cancel an already cancelled image is harmless.
633         return;
634     }
635 
636     // Unlock the image if it was locked
637     Image_unlockIfLocked(env, image);
638 
639     anw->cancelBuffer(anw.get(), buffer, fenceFd);
640 
641     Image_setNativeContext(env, image, NULL, -1, HAL_DATASPACE_UNKNOWN);
642 }
643 
ImageWriter_queueImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject image,jlong timestampNs,jint dataSpace,jint left,jint top,jint right,jint bottom,jint transform,jint scalingMode)644 static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image,
645         jlong timestampNs, jint dataSpace, jint left, jint top, jint right,
646         jint bottom, jint transform, jint scalingMode) {
647     ALOGV("%s", __FUNCTION__);
648     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
649     if (ctx == NULL || thiz == NULL) {
650         jniThrowException(env, "java/lang/IllegalStateException",
651                 "ImageWriterContext is not initialized");
652         return;
653     }
654 
655     status_t res = OK;
656     sp<ANativeWindow> anw = ctx->getProducer();
657 
658     GraphicBuffer *buffer = NULL;
659     int fenceFd = -1;
660     Image_getNativeContext(env, image, &buffer, &fenceFd);
661     if (buffer == NULL) {
662         jniThrowException(env, "java/lang/IllegalStateException",
663                 "Image is not initialized");
664         return;
665     }
666 
667     // Unlock image if it was locked.
668     Image_unlockIfLocked(env, image);
669 
670     // Set timestamp
671     ALOGV("timestamp to be queued: %" PRId64, timestampNs);
672     res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
673     if (res != OK) {
674         jniThrowRuntimeException(env, "Set timestamp failed");
675         return;
676     }
677 
678     // Set dataSpace
679     ALOGV("dataSpace to be queued: %d", dataSpace);
680     res = native_window_set_buffers_data_space(
681         anw.get(), static_cast<android_dataspace>(dataSpace));
682     if (res != OK) {
683         jniThrowRuntimeException(env, "Set dataspace failed");
684         return;
685     }
686 
687     // Set crop
688     android_native_rect_t cropRect;
689     cropRect.left = left;
690     cropRect.top = top;
691     cropRect.right = right;
692     cropRect.bottom = bottom;
693     res = native_window_set_crop(anw.get(), &cropRect);
694     if (res != OK) {
695         jniThrowRuntimeException(env, "Set crop rect failed");
696         return;
697     }
698 
699     res = native_window_set_buffers_transform(anw.get(), transform);
700     if (res != OK) {
701         jniThrowRuntimeException(env, "Set transform failed");
702         return;
703     }
704 
705     res = native_window_set_scaling_mode(anw.get(), scalingMode);
706     if (res != OK) {
707         jniThrowRuntimeException(env, "Set scaling mode failed");
708         return;
709     }
710 
711     // Finally, queue input buffer.
712     //
713     // Because onBufferReleased may be called before queueBuffer() returns,
714     // queue the "attached" flag before calling queueBuffer. In case
715     // queueBuffer() fails, remove it from the queue.
716     ctx->queueAttachedFlag(false);
717     res = anw->queueBuffer(anw.get(), buffer, fenceFd);
718     if (res != OK) {
719         ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
720         ctx->dequeueAttachedFlag();
721         switch (res) {
722             case NO_INIT:
723                 jniThrowException(env, "java/lang/IllegalStateException",
724                     "Surface has been abandoned");
725                 break;
726             default:
727                 // TODO: handle other error cases here.
728                 jniThrowRuntimeException(env, "Queue input buffer failed");
729         }
730         return;
731     }
732 
733     // Clear the image native context: end of this image's lifecycle in public API.
734     Image_setNativeContext(env, image, NULL, -1, HAL_DATASPACE_UNKNOWN);
735 }
736 
attachAndQeueuGraphicBuffer(JNIEnv * env,JNIImageWriterContext * ctx,sp<GraphicBuffer> gb,jlong timestampNs,jint dataSpace,jint left,jint top,jint right,jint bottom,jint transform,jint scalingMode)737 static status_t attachAndQeueuGraphicBuffer(JNIEnv* env, JNIImageWriterContext *ctx,
738         sp<GraphicBuffer> gb, jlong timestampNs, jint dataSpace,
739         jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
740     status_t res = OK;
741     // Step 1. Attach Image
742     sp<Surface> surface = ctx->getProducer();
743     if (surface == nullptr) {
744         jniThrowException(env, "java/lang/IllegalStateException",
745                 "Producer surface is null, ImageWriter seems already closed");
746     }
747     res = surface->attachBuffer(gb.get());
748     if (res != OK) {
749         ALOGE("Attach image failed: %s (%d)", strerror(-res), res);
750         switch (res) {
751             case NO_INIT:
752                 jniThrowException(env, "java/lang/IllegalStateException",
753                     "Surface has been abandoned");
754                 break;
755             default:
756                 // TODO: handle other error cases here.
757                 jniThrowRuntimeException(env, "nativeAttachImage failed!!!");
758         }
759         return res;
760     }
761     sp < ANativeWindow > anw = surface;
762 
763     // Step 2. Set timestamp, dataspace, crop, transform and scaling mode.
764     // Note that we do not need unlock the image because it was not locked.
765     ALOGV("timestamp to be queued: %" PRId64, timestampNs);
766     res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
767     if (res != OK) {
768         jniThrowRuntimeException(env, "Set timestamp failed");
769         return res;
770     }
771 
772     ALOGV("dataSpace to be queued: %" PRId32, dataSpace);
773     res = native_window_set_buffers_data_space(
774         anw.get(), static_cast<android_dataspace>(dataSpace));
775     if (res != OK) {
776         jniThrowRuntimeException(env, "Set dataSpace failed");
777         return res;
778     }
779 
780     android_native_rect_t cropRect;
781     cropRect.left = left;
782     cropRect.top = top;
783     cropRect.right = right;
784     cropRect.bottom = bottom;
785     res = native_window_set_crop(anw.get(), &cropRect);
786     if (res != OK) {
787         jniThrowRuntimeException(env, "Set crop rect failed");
788         return res;
789     }
790 
791     res = native_window_set_buffers_transform(anw.get(), transform);
792     if (res != OK) {
793         jniThrowRuntimeException(env, "Set transform failed");
794         return res;
795     }
796 
797     res = native_window_set_scaling_mode(anw.get(), scalingMode);
798     if (res != OK) {
799         jniThrowRuntimeException(env, "Set scaling mode failed");
800         return res;
801     }
802 
803     // Step 3. Queue Image.
804     //
805     // Because onBufferReleased may be called before queueBuffer() returns,
806     // queue the "attached" flag before calling queueBuffer. In case
807     // queueBuffer() fails, remove it from the queue.
808     ctx->queueAttachedFlag(true);
809     res = anw->queueBuffer(anw.get(), gb.get(), /*fenceFd*/
810             -1);
811     if (res != OK) {
812         ALOGE("%s: Queue buffer failed: %s (%d)", __FUNCTION__, strerror(-res), res);
813         ctx->dequeueAttachedFlag();
814         switch (res) {
815             case NO_INIT:
816                 jniThrowException(env, "java/lang/IllegalStateException",
817                     "Surface has been abandoned");
818                 break;
819             default:
820                 // TODO: handle other error cases here.
821                 jniThrowRuntimeException(env, "Queue input buffer failed");
822         }
823         return res;
824     }
825 
826     // Do not set the image native context. Since it would overwrite the existing native context
827     // of the image that is from ImageReader, the subsequent image close will run into issues.
828 
829     return res;
830 }
831 
ImageWriter_attachAndQueueImage(JNIEnv * env,jobject thiz,jlong nativeCtx,jlong nativeBuffer,jint nativeHalFormat,jlong timestampNs,jint dataSpace,jint left,jint top,jint right,jint bottom,jint transform,jint scalingMode)832 static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
833         jlong nativeBuffer, jint nativeHalFormat, jlong timestampNs, jint dataSpace,
834         jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
835     ALOGV("%s", __FUNCTION__);
836     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
837     if (ctx == NULL || thiz == NULL) {
838         jniThrowException(env, "java/lang/IllegalStateException",
839                 "ImageWriterContext is not initialized");
840         return -1;
841     }
842 
843     if (isFormatOpaque(ctx->getBufferFormat()) != isFormatOpaque(nativeHalFormat)) {
844         jniThrowException(env, "java/lang/IllegalStateException",
845                 "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
846         return -1;
847     }
848 
849     // Image is guaranteed to be from ImageReader at this point, so it is safe to
850     // cast to BufferItem pointer.
851     BufferItem* buffer = reinterpret_cast<BufferItem*>(nativeBuffer);
852     if (buffer == NULL) {
853         jniThrowException(env, "java/lang/IllegalStateException",
854                 "Image is not initialized or already closed");
855         return -1;
856     }
857 
858     return attachAndQeueuGraphicBuffer(env, ctx, buffer->mGraphicBuffer, timestampNs,
859             dataSpace, left, top, right, bottom, transform, scalingMode);
860 }
861 
ImageWriter_attachAndQueueGraphicBuffer(JNIEnv * env,jobject thiz,jlong nativeCtx,jobject buffer,jint nativeHalFormat,jlong timestampNs,jint dataSpace,jint left,jint top,jint right,jint bottom,jint transform,jint scalingMode)862 static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, jlong nativeCtx,
863         jobject buffer, jint nativeHalFormat, jlong timestampNs, jint dataSpace,
864         jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
865     ALOGV("%s", __FUNCTION__);
866     JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
867     if (ctx == NULL || thiz == NULL) {
868         jniThrowException(env, "java/lang/IllegalStateException",
869                 "ImageWriterContext is not initialized");
870         return -1;
871     }
872 
873     if (isFormatOpaque(ctx->getBufferFormat()) != isFormatOpaque(nativeHalFormat)) {
874         jniThrowException(env, "java/lang/IllegalStateException",
875                 "Trying to attach an opaque image into a non-opaque ImageWriter, or vice versa");
876         return -1;
877     }
878 
879     sp<GraphicBuffer> graphicBuffer = android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env,
880             buffer);
881     if (graphicBuffer.get() == NULL) {
882         jniThrowException(env, "java/lang/IllegalArgumentException",
883                 "Trying to attach an invalid graphic buffer");
884         return -1;
885     }
886 
887     return attachAndQeueuGraphicBuffer(env, ctx, graphicBuffer, timestampNs,
888             dataSpace, left, top, right, bottom, transform, scalingMode);
889 }
890 
891 // --------------------------Image methods---------------------------------------
892 
Image_getNativeContext(JNIEnv * env,jobject thiz,GraphicBuffer ** buffer,int * fenceFd)893 static void Image_getNativeContext(JNIEnv* env, jobject thiz,
894         GraphicBuffer** buffer, int* fenceFd) {
895     ALOGV("%s", __FUNCTION__);
896     if (buffer != NULL) {
897         GraphicBuffer *gb = reinterpret_cast<GraphicBuffer *>
898                   (env->GetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer));
899         *buffer = gb;
900     }
901 
902     if (fenceFd != NULL) {
903         *fenceFd = reinterpret_cast<jint>(env->GetIntField(
904                 thiz, gSurfaceImageClassInfo.mNativeFenceFd));
905     }
906 }
907 
Image_setNativeContext(JNIEnv * env,jobject thiz,sp<GraphicBuffer> buffer,int fenceFd,int dataSpace)908 static void Image_setNativeContext(JNIEnv* env, jobject thiz,
909         sp<GraphicBuffer> buffer, int fenceFd, int dataSpace) {
910     ALOGV("%s:", __FUNCTION__);
911     GraphicBuffer* p = NULL;
912     Image_getNativeContext(env, thiz, &p, /*fenceFd*/NULL);
913     if (buffer != 0) {
914         buffer->incStrong((void*)Image_setNativeContext);
915     }
916     if (p) {
917         p->decStrong((void*)Image_setNativeContext);
918     }
919     env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer,
920             reinterpret_cast<jlong>(buffer.get()));
921 
922     env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
923 
924     env->SetIntField(thiz, gSurfaceImageClassInfo.mDataSpace, dataSpace);
925 }
926 
Image_unlockIfLocked(JNIEnv * env,jobject thiz)927 static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) {
928     ALOGV("%s", __FUNCTION__);
929     GraphicBuffer* buffer;
930     Image_getNativeContext(env, thiz, &buffer, NULL);
931     if (buffer == NULL) {
932         jniThrowException(env, "java/lang/IllegalStateException",
933                 "Image is not initialized");
934         return;
935     }
936 
937     // Is locked?
938     bool isLocked = false;
939     jobject planes = NULL;
940     if (!isFormatOpaque(buffer->getPixelFormat())) {
941         planes = env->GetObjectField(thiz, gSurfaceImageClassInfo.mPlanes);
942     }
943     isLocked = (planes != NULL);
944     if (isLocked) {
945         // no need to use fence here, as we it will be consumed by either cancel or queue buffer.
946         status_t res = buffer->unlock();
947         if (res != OK) {
948             jniThrowRuntimeException(env, "unlock buffer failed");
949             return;
950         }
951         ALOGV("Successfully unlocked the image");
952     }
953 }
954 
Image_getWidth(JNIEnv * env,jobject thiz)955 static jint Image_getWidth(JNIEnv* env, jobject thiz) {
956     ALOGV("%s", __FUNCTION__);
957     GraphicBuffer* buffer;
958     Image_getNativeContext(env, thiz, &buffer, NULL);
959     if (buffer == NULL) {
960         jniThrowException(env, "java/lang/IllegalStateException",
961                 "Image is not initialized");
962         return -1;
963     }
964 
965     return buffer->getWidth();
966 }
967 
Image_getHeight(JNIEnv * env,jobject thiz)968 static jint Image_getHeight(JNIEnv* env, jobject thiz) {
969     ALOGV("%s", __FUNCTION__);
970     GraphicBuffer* buffer;
971     Image_getNativeContext(env, thiz, &buffer, NULL);
972     if (buffer == NULL) {
973         jniThrowException(env, "java/lang/IllegalStateException",
974                 "Image is not initialized");
975         return -1;
976     }
977 
978     return buffer->getHeight();
979 }
980 
Image_getFormat(JNIEnv * env,jobject thiz,jint dataSpace)981 static jint Image_getFormat(JNIEnv* env, jobject thiz, jint dataSpace) {
982     ALOGV("%s", __FUNCTION__);
983     GraphicBuffer* buffer;
984     Image_getNativeContext(env, thiz, &buffer, NULL);
985     if (buffer == NULL) {
986         jniThrowException(env, "java/lang/IllegalStateException",
987                 "Image is not initialized");
988         return 0;
989     }
990 
991     PublicFormat publicFmt = mapHalFormatDataspaceToPublicFormat(buffer->getPixelFormat(),
992         static_cast<android_dataspace>(dataSpace));
993 
994     return static_cast<jint>(publicFmt);
995 }
996 
Image_getHardwareBuffer(JNIEnv * env,jobject thiz)997 static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
998     GraphicBuffer* buffer;
999     Image_getNativeContext(env, thiz, &buffer, NULL);
1000     if (buffer == NULL) {
1001         jniThrowException(env, "java/lang/IllegalStateException",
1002                 "Image is not initialized");
1003         return NULL;
1004     }
1005     AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer);
1006     // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
1007     // to link against libandroid.so
1008     return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
1009 }
1010 
Image_setFenceFd(JNIEnv * env,jobject thiz,int fenceFd)1011 static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) {
1012     ALOGV("%s:", __FUNCTION__);
1013     int curtFenceFd = reinterpret_cast<jint>(
1014         env->GetIntField(thiz,gSurfaceImageClassInfo.mNativeFenceFd));
1015     if (curtFenceFd != -1) {
1016         close(curtFenceFd);
1017     }
1018     env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
1019 }
1020 
Image_getLockedImage(JNIEnv * env,jobject thiz,LockedImage * image)1021 static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) {
1022     ALOGV("%s", __FUNCTION__);
1023     GraphicBuffer* buffer;
1024     int fenceFd = -1;
1025     Image_getNativeContext(env, thiz, &buffer, &fenceFd);
1026     if (buffer == NULL) {
1027         jniThrowException(env, "java/lang/IllegalStateException",
1028                 "Image is not initialized");
1029         return;
1030     }
1031 
1032     // Maintain a StrongPointer so that the GraphicBuffer isn't destroyed when the
1033     // StrongPointer in lockImageFromBuffer goes out of scope.
1034     sp<GraphicBuffer> bufferSp(buffer);
1035     // ImageWriter doesn't use crop by itself, app sets it, use the no crop version.
1036     const Rect noCrop(bufferSp->width, bufferSp->height);
1037     status_t res = lockImageFromBuffer(
1038             bufferSp, GRALLOC_USAGE_SW_WRITE_OFTEN, noCrop, fenceFd, image);
1039     // Clear the fenceFd as it is already consumed by lock call.
1040     env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, -1);
1041     if (res != OK) {
1042         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
1043                 "lock buffer failed for format 0x%x",
1044                 bufferSp->getPixelFormat());
1045         return;
1046     }
1047 
1048     ALOGV("%s: Successfully locked the image", __FUNCTION__);
1049     // crop, transform, scalingMode, timestamp, and frameNumber should be set by producer,
1050     // and we don't set them here.
1051 }
1052 
Image_getLockedImageInfo(JNIEnv * env,LockedImage * buffer,int idx,int32_t halFormat,uint8_t ** base,uint32_t * size,int * pixelStride,int * rowStride)1053 static bool Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
1054         int32_t halFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) {
1055     ALOGV("%s", __FUNCTION__);
1056 
1057     status_t res = getLockedImageInfo(buffer, idx, halFormat, base, size,
1058             pixelStride, rowStride);
1059     if (res != OK) {
1060         jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
1061                              "Pixel format: 0x%x is unsupported", halFormat);
1062         return false;
1063     }
1064     return true;
1065 }
1066 
Image_createSurfacePlanes(JNIEnv * env,jobject thiz,int numPlanes,int writerFormat)1067 static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
1068         int numPlanes, int writerFormat) {
1069     ALOGV("%s: create SurfacePlane array with size %d", __FUNCTION__, numPlanes);
1070     int rowStride, pixelStride;
1071     uint8_t *pData;
1072     uint32_t dataSize;
1073     jobject byteBuffer;
1074     int halFormat = mapPublicFormatToHalFormat(static_cast<PublicFormat>(writerFormat));
1075 
1076     if (isFormatOpaque(halFormat) && numPlanes > 0) {
1077         String8 msg;
1078         msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
1079                 " must be 0", writerFormat, numPlanes);
1080         jniThrowException(env, "java/lang/IllegalArgumentException", msg.c_str());
1081         return NULL;
1082     }
1083 
1084     jobjectArray surfacePlanes = env->NewObjectArray(numPlanes, gSurfacePlaneClassInfo.clazz,
1085             /*initial_element*/NULL);
1086     if (surfacePlanes == NULL) {
1087         jniThrowRuntimeException(env, "Failed to create SurfacePlane arrays,"
1088                 " probably out of memory");
1089         return NULL;
1090     }
1091 
1092     if (isFormatOpaque(halFormat)) {
1093         return surfacePlanes;
1094     }
1095 
1096     // Buildup buffer info: rowStride, pixelStride and byteBuffers.
1097     LockedImage lockedImg = LockedImage();
1098     Image_getLockedImage(env, thiz, &lockedImg);
1099 
1100     // Create all SurfacePlanes
1101     for (int i = 0; i < numPlanes; i++) {
1102         if (!Image_getLockedImageInfo(env, &lockedImg, i, halFormat,
1103                 &pData, &dataSize, &pixelStride, &rowStride)) {
1104             return NULL;
1105         }
1106         byteBuffer = env->NewDirectByteBuffer(pData, dataSize);
1107         if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
1108             jniThrowException(env, "java/lang/IllegalStateException",
1109                     "Failed to allocate ByteBuffer");
1110             return NULL;
1111         }
1112 
1113         // Finally, create this SurfacePlane.
1114         jobject surfacePlane = env->NewObject(gSurfacePlaneClassInfo.clazz,
1115                     gSurfacePlaneClassInfo.ctor, thiz, rowStride, pixelStride, byteBuffer);
1116         env->SetObjectArrayElement(surfacePlanes, i, surfacePlane);
1117     }
1118 
1119     return surfacePlanes;
1120 }
1121 
1122 } // extern "C"
1123 
1124 // ----------------------------------------------------------------------------
1125 
1126 static JNINativeMethod gImageWriterMethods[] = {
1127     {"nativeClassInit",         "()V",                        (void*)ImageWriter_classInit },
1128     {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;IIIZIIJ)J",
1129                                                               (void*)ImageWriter_init },
1130     {"nativeClose",              "(J)V",                      (void*)ImageWriter_close },
1131     {"nativeAttachAndQueueImage",
1132         "(JJIJIIIIIII)I",
1133         (void*)ImageWriter_attachAndQueueImage },
1134     {"nativeAttachAndQueueGraphicBuffer",
1135         "(JLandroid/graphics/GraphicBuffer;IJIIIIIII)I",
1136         (void*)ImageWriter_attachAndQueueGraphicBuffer },
1137     {"nativeDequeueInputImage", "(JLandroid/media/Image;)V",  (void*)ImageWriter_dequeueImage },
1138     {"nativeQueueInputImage",   "(JLandroid/media/Image;JIIIIIII)V",
1139                                                                (void*)ImageWriter_queueImage },
1140     {"cancelImage",             "(JLandroid/media/Image;)V",   (void*)ImageWriter_cancelImage },
1141 };
1142 
1143 static JNINativeMethod gImageMethods[] = {
1144     {"nativeCreatePlanes",      "(II)[Landroid/media/ImageWriter$WriterSurfaceImage$SurfacePlane;",
1145                                                                (void*)Image_createSurfacePlanes },
1146     {"nativeGetWidth",          "()I",                         (void*)Image_getWidth },
1147     {"nativeGetHeight",         "()I",                         (void*)Image_getHeight },
1148     {"nativeGetFormat",         "(I)I",                        (void*)Image_getFormat },
1149     {"nativeSetFenceFd",        "(I)V",                        (void*)Image_setFenceFd },
1150     {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
1151                                                                (void*)Image_getHardwareBuffer },
1152 };
1153 
register_android_media_ImageWriter(JNIEnv * env)1154 int register_android_media_ImageWriter(JNIEnv *env) {
1155 
1156     int ret1 = AndroidRuntime::registerNativeMethods(env,
1157                    "android/media/ImageWriter", gImageWriterMethods, NELEM(gImageWriterMethods));
1158 
1159     int ret2 = AndroidRuntime::registerNativeMethods(env,
1160                    "android/media/ImageWriter$WriterSurfaceImage", gImageMethods, NELEM(gImageMethods));
1161 
1162     return (ret1 || ret2);
1163 }
1164