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