Home | History | Annotate | Download | only in jni
      1 /*
      2 **
      3 ** Copyright 2008, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 //#define LOG_NDEBUG 0
     19 #define LOG_TAG "Camera-JNI"
     20 #include <utils/Log.h>
     21 
     22 #include "jni.h"
     23 #include "JNIHelp.h"
     24 #include "android_runtime/AndroidRuntime.h"
     25 
     26 #include <utils/Vector.h>
     27 
     28 #include <surfaceflinger/Surface.h>
     29 #include <camera/Camera.h>
     30 #include <binder/IMemory.h>
     31 
     32 using namespace android;
     33 
     34 struct fields_t {
     35     jfieldID    context;
     36     jfieldID    surface;
     37     jfieldID    facing;
     38     jfieldID    orientation;
     39     jmethodID   post_event;
     40 };
     41 
     42 static fields_t fields;
     43 static Mutex sLock;
     44 
     45 // provides persistent context for calls from native code to Java
     46 class JNICameraContext: public CameraListener
     47 {
     48 public:
     49     JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
     50     ~JNICameraContext() { release(); }
     51     virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
     52     virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
     53     virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
     54     void addCallbackBuffer(JNIEnv *env, jbyteArray cbb);
     55     void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
     56     sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
     57     void release();
     58 
     59 private:
     60     void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
     61     void clearCallbackBuffers_l(JNIEnv *env);
     62 
     63     jobject     mCameraJObjectWeak;     // weak reference to java object
     64     jclass      mCameraJClass;          // strong reference to java class
     65     sp<Camera>  mCamera;                // strong reference to native object
     66     Mutex       mLock;
     67 
     68     Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[]
     69     bool mManualBufferMode;              // Whether to use application managed buffers.
     70     bool mManualCameraCallbackSet;       // Whether the callback has been set, used to reduce unnecessary calls to set the callback.
     71 };
     72 
     73 sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
     74 {
     75     sp<Camera> camera;
     76     Mutex::Autolock _l(sLock);
     77     JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
     78     if (context != NULL) {
     79         camera = context->getCamera();
     80     }
     81     LOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
     82     if (camera == 0) {
     83         jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
     84     }
     85 
     86     if (pContext != NULL) *pContext = context;
     87     return camera;
     88 }
     89 
     90 JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
     91 {
     92     mCameraJObjectWeak = env->NewGlobalRef(weak_this);
     93     mCameraJClass = (jclass)env->NewGlobalRef(clazz);
     94     mCamera = camera;
     95 
     96     mManualBufferMode = false;
     97     mManualCameraCallbackSet = false;
     98 }
     99 
    100 void JNICameraContext::release()
    101 {
    102     LOGV("release");
    103     Mutex::Autolock _l(mLock);
    104     JNIEnv *env = AndroidRuntime::getJNIEnv();
    105 
    106     if (mCameraJObjectWeak != NULL) {
    107         env->DeleteGlobalRef(mCameraJObjectWeak);
    108         mCameraJObjectWeak = NULL;
    109     }
    110     if (mCameraJClass != NULL) {
    111         env->DeleteGlobalRef(mCameraJClass);
    112         mCameraJClass = NULL;
    113     }
    114     clearCallbackBuffers_l(env);
    115     mCamera.clear();
    116 }
    117 
    118 void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2)
    119 {
    120     LOGV("notify");
    121 
    122     // VM pointer will be NULL if object is released
    123     Mutex::Autolock _l(mLock);
    124     if (mCameraJObjectWeak == NULL) {
    125         LOGW("callback on dead camera object");
    126         return;
    127     }
    128     JNIEnv *env = AndroidRuntime::getJNIEnv();
    129     env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
    130             mCameraJObjectWeak, msgType, ext1, ext2, NULL);
    131 }
    132 
    133 void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
    134 {
    135     jbyteArray obj = NULL;
    136 
    137     // allocate Java byte array and copy data
    138     if (dataPtr != NULL) {
    139         ssize_t offset;
    140         size_t size;
    141         sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
    142         LOGV("postData: off=%d, size=%d", offset, size);
    143         uint8_t *heapBase = (uint8_t*)heap->base();
    144 
    145         if (heapBase != NULL) {
    146             const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
    147 
    148             if (!mManualBufferMode) {
    149                 LOGV("Allocating callback buffer");
    150                 obj = env->NewByteArray(size);
    151             } else {
    152                 // Vector access should be protected by lock in postData()
    153                 if(!mCallbackBuffers.isEmpty()) {
    154                     LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size());
    155                     jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);
    156                     mCallbackBuffers.removeAt(0);
    157 
    158                     obj = (jbyteArray)env->NewLocalRef(globalBuffer);
    159                     env->DeleteGlobalRef(globalBuffer);
    160 
    161                     if (obj != NULL) {
    162                         jsize bufferLength = env->GetArrayLength(obj);
    163                         if ((int)bufferLength < (int)size) {
    164                             LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!",
    165                                  size, bufferLength);
    166                             env->DeleteLocalRef(obj);
    167                             return;
    168                         }
    169                     }
    170                 }
    171 
    172                 if(mCallbackBuffers.isEmpty()) {
    173                     LOGV("Out of buffers, clearing callback!");
    174                     mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
    175                     mManualCameraCallbackSet = false;
    176 
    177                     if (obj == NULL) {
    178                         return;
    179                     }
    180                 }
    181             }
    182 
    183             if (obj == NULL) {
    184                 LOGE("Couldn't allocate byte array for JPEG data");
    185                 env->ExceptionClear();
    186             } else {
    187                 env->SetByteArrayRegion(obj, 0, size, data);
    188             }
    189         } else {
    190             LOGE("image heap is NULL");
    191         }
    192     }
    193 
    194     // post image data to Java
    195     env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
    196             mCameraJObjectWeak, msgType, 0, 0, obj);
    197     if (obj) {
    198         env->DeleteLocalRef(obj);
    199     }
    200 }
    201 
    202 void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
    203 {
    204     // VM pointer will be NULL if object is released
    205     Mutex::Autolock _l(mLock);
    206     JNIEnv *env = AndroidRuntime::getJNIEnv();
    207     if (mCameraJObjectWeak == NULL) {
    208         LOGW("callback on dead camera object");
    209         return;
    210     }
    211 
    212     // return data based on callback type
    213     switch(msgType) {
    214     case CAMERA_MSG_VIDEO_FRAME:
    215         // should never happen
    216         break;
    217     // don't return raw data to Java
    218     case CAMERA_MSG_RAW_IMAGE:
    219         LOGV("rawCallback");
    220         env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
    221                 mCameraJObjectWeak, msgType, 0, 0, NULL);
    222         break;
    223     default:
    224         // TODO: Change to LOGV
    225         LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
    226         copyAndPost(env, dataPtr, msgType);
    227         break;
    228     }
    229 }
    230 
    231 void JNICameraContext::postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
    232 {
    233     // TODO: plumb up to Java. For now, just drop the timestamp
    234     postData(msgType, dataPtr);
    235 }
    236 
    237 void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualMode)
    238 {
    239     Mutex::Autolock _l(mLock);
    240     mManualBufferMode = manualMode;
    241     mManualCameraCallbackSet = false;
    242 
    243     // In order to limit the over usage of binder threads, all non-manual buffer
    244     // callbacks use FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now.
    245     //
    246     // Continuous callbacks will have the callback re-registered from handleMessage.
    247     // Manual buffer mode will operate as fast as possible, relying on the finite supply
    248     // of buffers for throttling.
    249 
    250     if (!installed) {
    251         mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
    252         clearCallbackBuffers_l(env);
    253     } else if (mManualBufferMode) {
    254         if (!mCallbackBuffers.isEmpty()) {
    255             mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
    256             mManualCameraCallbackSet = true;
    257         }
    258     } else {
    259         mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
    260         clearCallbackBuffers_l(env);
    261     }
    262 }
    263 
    264 void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb)
    265 {
    266     if (cbb != NULL) {
    267         Mutex::Autolock _l(mLock);
    268         jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb);
    269         mCallbackBuffers.push(cbb);
    270 
    271         LOGV("Adding callback buffer to queue, %d total", mCallbackBuffers.size());
    272 
    273         // We want to make sure the camera knows we're ready for the next frame.
    274         // This may have come unset had we not had a callbackbuffer ready for it last time.
    275         if (mManualBufferMode && !mManualCameraCallbackSet) {
    276             mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
    277             mManualCameraCallbackSet = true;
    278         }
    279     } else {
    280        LOGE("Null byte array!");
    281     }
    282 }
    283 
    284 void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env)
    285 {
    286     LOGV("Clearing callback buffers, %d remained", mCallbackBuffers.size());
    287     while(!mCallbackBuffers.isEmpty()) {
    288         env->DeleteGlobalRef(mCallbackBuffers.top());
    289         mCallbackBuffers.pop();
    290     }
    291 }
    292 
    293 static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz)
    294 {
    295     return Camera::getNumberOfCameras();
    296 }
    297 
    298 static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz,
    299     jint cameraId, jobject info_obj)
    300 {
    301     CameraInfo cameraInfo;
    302     status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo);
    303     if (rc != NO_ERROR) {
    304         jniThrowException(env, "java/lang/RuntimeException",
    305                           "Fail to get camera info");
    306         return;
    307     }
    308     env->SetIntField(info_obj, fields.facing, cameraInfo.facing);
    309     env->SetIntField(info_obj, fields.orientation, cameraInfo.orientation);
    310 }
    311 
    312 // connect to camera service
    313 static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    314     jobject weak_this, jint cameraId)
    315 {
    316     sp<Camera> camera = Camera::connect(cameraId);
    317 
    318     if (camera == NULL) {
    319         jniThrowException(env, "java/lang/RuntimeException",
    320                           "Fail to connect to camera service");
    321         return;
    322     }
    323 
    324     // make sure camera hardware is alive
    325     if (camera->getStatus() != NO_ERROR) {
    326         jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed");
    327         return;
    328     }
    329 
    330     jclass clazz = env->GetObjectClass(thiz);
    331     if (clazz == NULL) {
    332         jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera");
    333         return;
    334     }
    335 
    336     // We use a weak reference so the Camera object can be garbage collected.
    337     // The reference is only used as a proxy for callbacks.
    338     sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    339     context->incStrong(thiz);
    340     camera->setListener(context);
    341 
    342     // save context in opaque field
    343     env->SetIntField(thiz, fields.context, (int)context.get());
    344 }
    345 
    346 // disconnect from camera service
    347 // It's okay to call this when the native camera context is already null.
    348 // This handles the case where the user has called release() and the
    349 // finalizer is invoked later.
    350 static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
    351 {
    352     // TODO: Change to LOGV
    353     LOGV("release camera");
    354     JNICameraContext* context = NULL;
    355     sp<Camera> camera;
    356     {
    357         Mutex::Autolock _l(sLock);
    358         context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
    359 
    360         // Make sure we do not attempt to callback on a deleted Java object.
    361         env->SetIntField(thiz, fields.context, 0);
    362     }
    363 
    364     // clean up if release has not been called before
    365     if (context != NULL) {
    366         camera = context->getCamera();
    367         context->release();
    368         LOGV("native_release: context=%p camera=%p", context, camera.get());
    369 
    370         // clear callbacks
    371         if (camera != NULL) {
    372             camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
    373             camera->disconnect();
    374         }
    375 
    376         // remove context to prevent further Java access
    377         context->decStrong(thiz);
    378     }
    379 }
    380 
    381 static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
    382 {
    383     LOGV("setPreviewDisplay");
    384     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    385     if (camera == 0) return;
    386 
    387     sp<Surface> surface = NULL;
    388     if (jSurface != NULL) {
    389         surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
    390     }
    391     if (camera->setPreviewDisplay(surface) != NO_ERROR) {
    392         jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
    393     }
    394 }
    395 
    396 static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
    397 {
    398     LOGV("startPreview");
    399     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    400     if (camera == 0) return;
    401 
    402     if (camera->startPreview() != NO_ERROR) {
    403         jniThrowException(env, "java/lang/RuntimeException", "startPreview failed");
    404         return;
    405     }
    406 }
    407 
    408 static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz)
    409 {
    410     LOGV("stopPreview");
    411     sp<Camera> c = get_native_camera(env, thiz, NULL);
    412     if (c == 0) return;
    413 
    414     c->stopPreview();
    415 }
    416 
    417 static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz)
    418 {
    419     LOGV("previewEnabled");
    420     sp<Camera> c = get_native_camera(env, thiz, NULL);
    421     if (c == 0) return false;
    422 
    423     return c->previewEnabled();
    424 }
    425 
    426 static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean manualBuffer)
    427 {
    428     LOGV("setHasPreviewCallback: installed:%d, manualBuffer:%d", (int)installed, (int)manualBuffer);
    429     // Important: Only install preview_callback if the Java code has called
    430     // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
    431     // each preview frame for nothing.
    432     JNICameraContext* context;
    433     sp<Camera> camera = get_native_camera(env, thiz, &context);
    434     if (camera == 0) return;
    435 
    436     // setCallbackMode will take care of setting the context flags and calling
    437     // camera->setPreviewCallbackFlags within a mutex for us.
    438     context->setCallbackMode(env, installed, manualBuffer);
    439 }
    440 
    441 static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) {
    442     LOGV("addCallbackBuffer");
    443 
    444     JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
    445 
    446     if (context != NULL) {
    447         context->addCallbackBuffer(env, bytes);
    448     }
    449 }
    450 
    451 static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
    452 {
    453     LOGV("autoFocus");
    454     JNICameraContext* context;
    455     sp<Camera> c = get_native_camera(env, thiz, &context);
    456     if (c == 0) return;
    457 
    458     if (c->autoFocus() != NO_ERROR) {
    459         jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
    460     }
    461 }
    462 
    463 static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz)
    464 {
    465     LOGV("cancelAutoFocus");
    466     JNICameraContext* context;
    467     sp<Camera> c = get_native_camera(env, thiz, &context);
    468     if (c == 0) return;
    469 
    470     if (c->cancelAutoFocus() != NO_ERROR) {
    471         jniThrowException(env, "java/lang/RuntimeException", "cancelAutoFocus failed");
    472     }
    473 }
    474 
    475 static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
    476 {
    477     LOGV("takePicture");
    478     JNICameraContext* context;
    479     sp<Camera> camera = get_native_camera(env, thiz, &context);
    480     if (camera == 0) return;
    481 
    482     if (camera->takePicture() != NO_ERROR) {
    483         jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
    484         return;
    485     }
    486 }
    487 
    488 static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params)
    489 {
    490     LOGV("setParameters");
    491     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    492     if (camera == 0) return;
    493 
    494     const jchar* str = env->GetStringCritical(params, 0);
    495     String8 params8;
    496     if (params) {
    497         params8 = String8(str, env->GetStringLength(params));
    498         env->ReleaseStringCritical(params, str);
    499     }
    500     if (camera->setParameters(params8) != NO_ERROR) {
    501         jniThrowException(env, "java/lang/RuntimeException", "setParameters failed");
    502         return;
    503     }
    504 }
    505 
    506 static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz)
    507 {
    508     LOGV("getParameters");
    509     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    510     if (camera == 0) return 0;
    511 
    512     return env->NewStringUTF(camera->getParameters().string());
    513 }
    514 
    515 static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz)
    516 {
    517     LOGV("reconnect");
    518     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    519     if (camera == 0) return;
    520 
    521     if (camera->reconnect() != NO_ERROR) {
    522         jniThrowException(env, "java/io/IOException", "reconnect failed");
    523         return;
    524     }
    525 }
    526 
    527 static void android_hardware_Camera_lock(JNIEnv *env, jobject thiz)
    528 {
    529     LOGV("lock");
    530     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    531     if (camera == 0) return;
    532 
    533     if (camera->lock() != NO_ERROR) {
    534         jniThrowException(env, "java/lang/RuntimeException", "lock failed");
    535     }
    536 }
    537 
    538 static void android_hardware_Camera_unlock(JNIEnv *env, jobject thiz)
    539 {
    540     LOGV("unlock");
    541     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    542     if (camera == 0) return;
    543 
    544     if (camera->unlock() != NO_ERROR) {
    545         jniThrowException(env, "java/lang/RuntimeException", "unlock failed");
    546     }
    547 }
    548 
    549 static void android_hardware_Camera_startSmoothZoom(JNIEnv *env, jobject thiz, jint value)
    550 {
    551     LOGV("startSmoothZoom");
    552     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    553     if (camera == 0) return;
    554 
    555     status_t rc = camera->sendCommand(CAMERA_CMD_START_SMOOTH_ZOOM, value, 0);
    556     if (rc == BAD_VALUE) {
    557         char msg[64];
    558         sprintf(msg, "invalid zoom value=%d", value);
    559         jniThrowException(env, "java/lang/IllegalArgumentException", msg);
    560     } else if (rc != NO_ERROR) {
    561         jniThrowException(env, "java/lang/RuntimeException", "start smooth zoom failed");
    562     }
    563 }
    564 
    565 static void android_hardware_Camera_stopSmoothZoom(JNIEnv *env, jobject thiz)
    566 {
    567     LOGV("stopSmoothZoom");
    568     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    569     if (camera == 0) return;
    570 
    571     if (camera->sendCommand(CAMERA_CMD_STOP_SMOOTH_ZOOM, 0, 0) != NO_ERROR) {
    572         jniThrowException(env, "java/lang/RuntimeException", "stop smooth zoom failed");
    573     }
    574 }
    575 
    576 static void android_hardware_Camera_setDisplayOrientation(JNIEnv *env, jobject thiz,
    577         jint value)
    578 {
    579     LOGV("setDisplayOrientation");
    580     sp<Camera> camera = get_native_camera(env, thiz, NULL);
    581     if (camera == 0) return;
    582 
    583     if (camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION, value, 0) != NO_ERROR) {
    584         jniThrowException(env, "java/lang/RuntimeException", "set display orientation failed");
    585     }
    586 }
    587 
    588 //-------------------------------------------------
    589 
    590 static JNINativeMethod camMethods[] = {
    591   { "getNumberOfCameras",
    592     "()I",
    593     (void *)android_hardware_Camera_getNumberOfCameras },
    594   { "getCameraInfo",
    595     "(ILandroid/hardware/Camera$CameraInfo;)V",
    596     (void*)android_hardware_Camera_getCameraInfo },
    597   { "native_setup",
    598     "(Ljava/lang/Object;I)V",
    599     (void*)android_hardware_Camera_native_setup },
    600   { "native_release",
    601     "()V",
    602     (void*)android_hardware_Camera_release },
    603   { "setPreviewDisplay",
    604     "(Landroid/view/Surface;)V",
    605     (void *)android_hardware_Camera_setPreviewDisplay },
    606   { "startPreview",
    607     "()V",
    608     (void *)android_hardware_Camera_startPreview },
    609   { "stopPreview",
    610     "()V",
    611     (void *)android_hardware_Camera_stopPreview },
    612   { "previewEnabled",
    613     "()Z",
    614     (void *)android_hardware_Camera_previewEnabled },
    615   { "setHasPreviewCallback",
    616     "(ZZ)V",
    617     (void *)android_hardware_Camera_setHasPreviewCallback },
    618   { "addCallbackBuffer",
    619     "([B)V",
    620     (void *)android_hardware_Camera_addCallbackBuffer },
    621   { "native_autoFocus",
    622     "()V",
    623     (void *)android_hardware_Camera_autoFocus },
    624   { "native_cancelAutoFocus",
    625     "()V",
    626     (void *)android_hardware_Camera_cancelAutoFocus },
    627   { "native_takePicture",
    628     "()V",
    629     (void *)android_hardware_Camera_takePicture },
    630   { "native_setParameters",
    631     "(Ljava/lang/String;)V",
    632     (void *)android_hardware_Camera_setParameters },
    633   { "native_getParameters",
    634     "()Ljava/lang/String;",
    635     (void *)android_hardware_Camera_getParameters },
    636   { "reconnect",
    637     "()V",
    638     (void*)android_hardware_Camera_reconnect },
    639   { "lock",
    640     "()V",
    641     (void*)android_hardware_Camera_lock },
    642   { "unlock",
    643     "()V",
    644     (void*)android_hardware_Camera_unlock },
    645   { "startSmoothZoom",
    646     "(I)V",
    647     (void *)android_hardware_Camera_startSmoothZoom },
    648   { "stopSmoothZoom",
    649     "()V",
    650     (void *)android_hardware_Camera_stopSmoothZoom },
    651   { "setDisplayOrientation",
    652     "(I)V",
    653     (void *)android_hardware_Camera_setDisplayOrientation },
    654 };
    655 
    656 struct field {
    657     const char *class_name;
    658     const char *field_name;
    659     const char *field_type;
    660     jfieldID   *jfield;
    661 };
    662 
    663 static int find_fields(JNIEnv *env, field *fields, int count)
    664 {
    665     for (int i = 0; i < count; i++) {
    666         field *f = &fields[i];
    667         jclass clazz = env->FindClass(f->class_name);
    668         if (clazz == NULL) {
    669             LOGE("Can't find %s", f->class_name);
    670             return -1;
    671         }
    672 
    673         jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
    674         if (field == NULL) {
    675             LOGE("Can't find %s.%s", f->class_name, f->field_name);
    676             return -1;
    677         }
    678 
    679         *(f->jfield) = field;
    680     }
    681 
    682     return 0;
    683 }
    684 
    685 // Get all the required offsets in java class and register native functions
    686 int register_android_hardware_Camera(JNIEnv *env)
    687 {
    688     field fields_to_find[] = {
    689         { "android/hardware/Camera", "mNativeContext",   "I", &fields.context },
    690         { "android/view/Surface",    ANDROID_VIEW_SURFACE_JNI_ID, "I", &fields.surface },
    691         { "android/hardware/Camera$CameraInfo", "facing",   "I", &fields.facing },
    692         { "android/hardware/Camera$CameraInfo", "orientation",   "I", &fields.orientation },
    693     };
    694 
    695     if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
    696         return -1;
    697 
    698     jclass clazz = env->FindClass("android/hardware/Camera");
    699     fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
    700                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    701     if (fields.post_event == NULL) {
    702         LOGE("Can't find android/hardware/Camera.postEventFromNative");
    703         return -1;
    704     }
    705 
    706 
    707     // Register native functions
    708     return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
    709                                               camMethods, NELEM(camMethods));
    710 }
    711 
    712