Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d7b8d1a authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 5158 into donut

* changes:
  Use a ref-counted callback interface for Camera. This allows the camera service to hang onto the callback interface until all callbacks have been processed. This prevents problems where pending callbacks in binder worker threads are processed after the Java camera object and its associated native resources have been released. Bug 1884362
parents 544450b4 5e271159
Loading
Loading
Loading
Loading
+156 −182
Original line number Original line Diff line number Diff line
@@ -53,19 +53,33 @@ struct fields_t {
static fields_t fields;
static fields_t fields;
static Mutex sLock;
static Mutex sLock;


struct camera_context_t {
// provides persistent context for calls from native code to Java
class JNICameraContext: public CameraListener
{
public:
    JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
    ~JNICameraContext() { release(); }
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
    sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
    void release();

private:
    void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);

    jobject     mCameraJObjectWeak;     // weak reference to java object
    jobject     mCameraJObjectWeak;     // weak reference to java object
    jclass      mCameraJClass;          // strong reference to java class
    jclass      mCameraJClass;          // strong reference to java class
    sp<Camera>  mCamera;                // strong reference to native object 
    sp<Camera>  mCamera;                // strong reference to native object 
    Mutex       mLock;
};
};


sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pContext)
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
{
    sp<Camera> camera;
    sp<Camera> camera;
    Mutex::Autolock _l(sLock);
    Mutex::Autolock _l(sLock);
    camera_context_t* context = reinterpret_cast<camera_context_t*>(env->GetIntField(thiz, fields.context));
    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
    if (context != NULL) {
    if (context != NULL) {
        camera = context->mCamera;
        camera = context->getCamera();
    }
    }
    LOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
    LOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
    if (camera == 0) {
    if (camera == 0) {
@@ -76,15 +90,48 @@ sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pCont
    return camera;
    return camera;
}
}


static void err_callback(status_t err, void *cookie)
JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
{
{
    camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
    mCameraJObjectWeak = env->NewGlobalRef(weak_this);
    if ((context == NULL) || (context->mCamera == 0)) return;
    mCameraJClass = (jclass)env->NewGlobalRef(clazz);
    mCamera = camera;
}


    LOGV("err_callback: context=%p, camera=%p", context, context->mCamera.get());
void JNICameraContext::release()
{
    LOGV("release");
    Mutex::Autolock _l(mLock);
    JNIEnv *env = AndroidRuntime::getJNIEnv();


    if (mCameraJObjectWeak != NULL) {
        env->DeleteGlobalRef(mCameraJObjectWeak);
        mCameraJObjectWeak = NULL;
    }
    if (mCameraJClass != NULL) {
        env->DeleteGlobalRef(mCameraJClass);
        mCameraJClass = NULL;
    }
    mCamera.clear();
}

void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2)
{
    LOGV("notify");

    // VM pointer will be NULL if object is released
    Mutex::Autolock _l(mLock);
    if (mCameraJObjectWeak == NULL) {
        LOGW("callback on dead camera object");
        return;
    }
    JNIEnv *env = AndroidRuntime::getJNIEnv();

    // parse message
    switch (msgType) {
    case CAMERA_MSG_ERROR:
        LOGV("errorCallback");
        int error;
        int error;
    switch (err) {
        switch (ext1) {
            case DEAD_OBJECT:
            case DEAD_OBJECT:
                error = kCameraErrorMediaServer;
                error = kCameraErrorMediaServer;
                break;
                break;
@@ -92,14 +139,91 @@ static void err_callback(status_t err, void *cookie)
                error = kCameraErrorUnknown;
                error = kCameraErrorUnknown;
                break;
                break;
        }
        }
        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                mCameraJObjectWeak, kErrorCallback, error, 0, NULL);
        break;
    case CAMERA_MSG_FOCUS:
        LOGV("autoFocusCallback");
        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                mCameraJObjectWeak, kAutoFocusCallback, ext1, 0, NULL);
        break;
    case CAMERA_MSG_SHUTTER:
        LOGV("shutterCallback");
        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                mCameraJObjectWeak, kShutterCallback, 0, 0, NULL);
        break;
    default:
        LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2);
        break;
    }
}


void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
{
    jbyteArray obj = NULL;

    // allocate Java byte array and copy data
    if (dataPtr != NULL) {
        ssize_t offset;
        size_t size;
        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
        LOGV("postData: off=%d, size=%d", offset, size);
        uint8_t *heapBase = (uint8_t*)heap->base();

        if (heapBase != NULL) {
            uint8_t *data = heapBase + offset;
            obj = env->NewByteArray(size);
            if (obj == NULL) {
                LOGE("Couldn't allocate byte array for JPEG data");
                env->ExceptionClear();
            } else {
                jbyte *bytes = env->GetByteArrayElements(obj, NULL);
                memcpy(bytes, data, size);
                env->ReleaseByteArrayElements(obj, bytes, 0);

            }
        } else {
            LOGE("image heap is NULL");
        }
    }

    // post image data to Java
    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
            mCameraJObjectWeak, msgType, 0, 0, obj);
    if (obj) {
        env->DeleteLocalRef(obj);
    }
}

void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
{
    // VM pointer will be NULL if object is released
    Mutex::Autolock _l(mLock);
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {

        LOGE("err_callback on dead VM");
    // return data based on callback type
        return;
    switch(msgType) {
    case CAMERA_MSG_PREVIEW_FRAME:
        LOGV("previewCallback");
        copyAndPost(env, dataPtr, kPreviewCallback);
        break;
    case CAMERA_MSG_VIDEO_FRAME:
        LOGV("recordingCallback");
        break;
    case CAMERA_MSG_RAW_IMAGE:
        LOGV("rawCallback");
        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
                mCameraJObjectWeak, kRawCallback, 0, 0, NULL);
        break;
    case CAMERA_MSG_COMPRESSED_IMAGE:
        LOGV("jpegCallback");
        copyAndPost(env, dataPtr, kJpegCallback);
        break;
    default:
        LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
        break;
    }
    }
    env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,

            context->mCameraJObjectWeak, kErrorCallback, error, 0, NULL);
}
}


// connect to camera service
// connect to camera service
@@ -127,19 +251,12 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj


    // We use a weak reference so the Camera object can be garbage collected.
    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    // The reference is only used as a proxy for callbacks.
    camera_context_t* context = new camera_context_t;
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->mCameraJObjectWeak = env->NewGlobalRef(weak_this);
    context->incStrong(thiz);
    context->mCameraJClass = (jclass)env->NewGlobalRef(clazz);
    camera->setListener(context);
    context->mCamera = camera;


    // save context in opaque field
    // save context in opaque field
    env->SetIntField(thiz, fields.context, (int)context);
    env->SetIntField(thiz, fields.context, (int)context.get());

    LOGV("native_setup: mCameraJObjectWeak=%x, camera_obj=%x, context=%p",
            (int)context->mCameraJObjectWeak, (int)thiz, context);

    // set error callback
    camera->setErrorCallback(err_callback, context);
}
}


// disconnect from camera service
// disconnect from camera service
@@ -148,11 +265,11 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj
// finalizer is invoked later.
// finalizer is invoked later.
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
{
    camera_context_t* context = NULL;
    JNICameraContext* context = NULL;
    sp<Camera> camera;
    sp<Camera> camera;
    {
    {
        Mutex::Autolock _l(sLock);
        Mutex::Autolock _l(sLock);
        context = reinterpret_cast<camera_context_t*>(env->GetIntField(thiz, fields.context));
        context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));


        // Make sure we do not attempt to callback on a deleted Java object.
        // Make sure we do not attempt to callback on a deleted Java object.
        env->SetIntField(thiz, fields.context, 0);
        env->SetIntField(thiz, fields.context, 0);
@@ -160,21 +277,18 @@ static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)


    // clean up if release has not been called before
    // clean up if release has not been called before
    if (context != NULL) {
    if (context != NULL) {
        camera = context->mCamera;
        camera = context->getCamera();
        context->mCamera.clear();
        context->release();
        LOGV("native_release: context=%p camera=%p", context, camera.get());
        LOGV("native_release: context=%p camera=%p", context, camera.get());


        // clear callbacks
        // clear callbacks
        if (camera != NULL) {
        if (camera != NULL) {
            camera->setPreviewCallback(NULL, NULL, FRAME_CALLBACK_FLAG_NOOP);
            camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
            camera->setErrorCallback(NULL, NULL);
            camera->disconnect();
            camera->disconnect();
            env->DeleteGlobalRef(context->mCameraJObjectWeak);
            env->DeleteGlobalRef(context->mCameraJClass);
        }
        }


        // remove context to prevent further Java access
        // remove context to prevent further Java access
        delete context;
        context->decStrong(thiz);
    }
    }
}
}


@@ -190,48 +304,6 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz,
    }
    }
}
}


static void preview_callback(const sp<IMemory>& mem, void *cookie)
{
    LOGV("preview_callback");
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        LOGE("preview_callback on dead VM");
        return;
    }
    camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
    if ((context == NULL) || (context->mCamera == 0)) {
        LOGW("context or camera is NULL in preview_callback");
        return;
    }
    LOGV("native_release: context=%p camera=%p", context, context->mCamera.get());

    int arg1 = 0, arg2 = 0;
    jobject obj = NULL;

    ssize_t offset;
    size_t size;
    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);

    uint8_t *data = ((uint8_t *)heap->base()) + offset;

    jbyteArray array = env->NewByteArray(size);
    if (array == NULL) {
        LOGE("Couldn't allocate byte array for YUV data");
        env->ExceptionClear();
        return;
    }

    jbyte *bytes = env->GetByteArrayElements(array, NULL);
    memcpy(bytes, data, size);
    env->ReleaseByteArrayElements(array, bytes, 0);

    obj = array;

    env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
            context->mCameraJObjectWeak, kPreviewCallback, arg1, arg2, obj);
    env->DeleteLocalRef(array);
}

static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
{
    LOGV("startPreview");
    LOGV("startPreview");
@@ -267,7 +339,7 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t
    // Important: Only install preview_callback if the Java code has called
    // Important: Only install preview_callback if the Java code has called
    // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
    // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
    // each preview frame for nothing.
    // each preview frame for nothing.
    camera_context_t* context;
    JNICameraContext* context;
    sp<Camera> camera = get_native_camera(env, thiz, &context);
    sp<Camera> camera = get_native_camera(env, thiz, &context);
    if (camera == 0) return;
    if (camera == 0) return;


@@ -277,130 +349,32 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t
    } else {
    } else {
        callback_flag = FRAME_CALLBACK_FLAG_NOOP;
        callback_flag = FRAME_CALLBACK_FLAG_NOOP;
    }
    }
    camera->setPreviewCallback(installed ? preview_callback : NULL, context, callback_flag);
    camera->setPreviewCallbackFlags(callback_flag);
}

static void autofocus_callback_impl(bool success, void *cookie)
{
    LOGV("autoFocusCallback");
    camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        LOGE("autofocus_callback on dead VM");
        return;
    }
    env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
            context->mCameraJObjectWeak, kAutoFocusCallback, success, 0, NULL);
}
}


static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
{
{
    LOGV("autoFocus");
    LOGV("autoFocus");
    camera_context_t* context;
    JNICameraContext* context;
    sp<Camera> c = get_native_camera(env, thiz, &context);
    sp<Camera> c = get_native_camera(env, thiz, &context);
    if (c == 0) return;
    if (c == 0) return;


    c->setAutoFocusCallback(autofocus_callback_impl, context);
    if (c->autoFocus() != NO_ERROR) {
    if (c->autoFocus() != NO_ERROR) {
        jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
        jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
    }
    }
}
}


static void jpeg_callback(const sp<IMemory>& mem, void *cookie)
{
    LOGV("jpegCallback");
    camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        LOGE("jpeg`_callback on dead VM");
        return;
    }
    int arg1 = 0, arg2 = 0;
    jobject obj = NULL;

    if (mem == NULL) {
        env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
                                  context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, NULL);
        return;
    }
    ssize_t offset;
    size_t size;
    sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
    LOGV("jpeg_callback: mem off=%d, size=%d", offset, size);

    uint8_t *heap_base = (uint8_t *)heap->base();
    if (heap_base == NULL) {
        LOGE("YUV heap is NULL");
        return;
    }

    uint8_t *data = heap_base + offset;

    jbyteArray array = env->NewByteArray(size);
    if (array == NULL) {
        LOGE("Couldn't allocate byte array for JPEG data");
        env->ExceptionClear();
        return;
    }

    jbyte *bytes = env->GetByteArrayElements(array, NULL);
    memcpy(bytes, data, size);
    env->ReleaseByteArrayElements(array, bytes, 0);

    obj = array;

    env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
                              context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, obj);
    env->DeleteLocalRef(array);
}

static void shutter_callback_impl(void *cookie)
{
    LOGV("shutterCallback");
    camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        LOGE("shutter_callback on dead VM");
        return;
    }
    env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
                              context->mCameraJObjectWeak, kShutterCallback, 0, 0, NULL);
}

static void raw_callback(const sp<IMemory>& mem __attribute__((unused)),
                         void *cookie)
{
    LOGV("rawCallback");
    camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        LOGE("raw_callback on dead VM");
        return;
    }
    env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
                              context->mCameraJObjectWeak, kRawCallback, 0, 0, NULL);
}

static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
{
{
    LOGV("takePicture");
    LOGV("takePicture");
    camera_context_t* context;
    JNICameraContext* context;
    sp<Camera> camera = get_native_camera(env, thiz, &context);
    sp<Camera> camera = get_native_camera(env, thiz, &context);
    if (camera == 0) return;
    if (camera == 0) return;


    camera->setShutterCallback(shutter_callback_impl, context);
    camera->setRawCallback(raw_callback, context);
    camera->setJpegCallback(jpeg_callback, context);
    if (camera->takePicture() != NO_ERROR) {
    if (camera->takePicture() != NO_ERROR) {
        jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
        jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
        return;
        return;
    }
    }

    return;
}
}


static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params)
static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params)
+13 −0
Original line number Original line Diff line number Diff line
@@ -86,6 +86,14 @@ class Surface;
class Mutex;
class Mutex;
class String8;
class String8;


// ref-counted object for callbacks
class CameraListener: virtual public RefBase
{
public:
    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr) = 0;
};

typedef void (*shutter_callback)(void *cookie);
typedef void (*shutter_callback)(void *cookie);
typedef void (*frame_callback)(const sp<IMemory>& mem, void *cookie);
typedef void (*frame_callback)(const sp<IMemory>& mem, void *cookie);
typedef void (*autofocus_callback)(bool focused, void *cookie);
typedef void (*autofocus_callback)(bool focused, void *cookie);
@@ -152,6 +160,9 @@ public:
            void        setErrorCallback(error_callback cb, void *cookie);
            void        setErrorCallback(error_callback cb, void *cookie);
            void        setAutoFocusCallback(autofocus_callback cb, void *cookie);
            void        setAutoFocusCallback(autofocus_callback cb, void *cookie);


            void        setListener(const sp<CameraListener>& listener);
            void        setPreviewCallbackFlags(int preview_callback_flag);

    // ICameraClient interface
    // ICameraClient interface
    virtual void        notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
    virtual void        notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
    virtual void        dataCallback(int32_t msgType, const sp<IMemory>& dataPtr);
    virtual void        dataCallback(int32_t msgType, const sp<IMemory>& dataPtr);
@@ -194,6 +205,8 @@ private:
            autofocus_callback  mAutoFocusCallback;
            autofocus_callback  mAutoFocusCallback;
            void                *mAutoFocusCallbackCookie;
            void                *mAutoFocusCallbackCookie;


            sp<CameraListener>  mListener;

            friend class DeathNotifier;
            friend class DeathNotifier;


            static  Mutex               mLock;
            static  Mutex               mLock;
+33 −0
Original line number Original line Diff line number Diff line
@@ -337,9 +337,32 @@ void Camera::setErrorCallback(error_callback cb, void *cookie)
    mErrorCallbackCookie = cookie;
    mErrorCallbackCookie = cookie;
}
}


void Camera::setListener(const sp<CameraListener>& listener)
{
    Mutex::Autolock _l(mLock);
    mListener = listener;
}

void Camera::setPreviewCallbackFlags(int flag)
{
    LOGV("setPreviewCallbackFlags");
    sp <ICamera> c = mCamera;
    if (c == 0) return;
    mCamera->setPreviewCallbackFlag(flag);
}

// callback from camera service
// callback from camera service
void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
{
{
    sp<CameraListener> listener;
    {
        Mutex::Autolock _l(mLock);
        listener = mListener;
    }
    if (listener != NULL) {
        listener->notify(msgType, ext1, ext2);
    }

    switch(msgType) {
    switch(msgType) {
    case CAMERA_MSG_ERROR:
    case CAMERA_MSG_ERROR:
        LOGV("errorCallback");
        LOGV("errorCallback");
@@ -368,6 +391,15 @@ void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
// callback from camera service when frame or image is ready
// callback from camera service when frame or image is ready
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)
{
{
    sp<CameraListener> listener;
    {
        Mutex::Autolock _l(mLock);
        listener = mListener;
    }
    if (listener != NULL) {
        listener->postData(msgType, dataPtr);
    }

    switch(msgType) {
    switch(msgType) {
    case CAMERA_MSG_PREVIEW_FRAME:
    case CAMERA_MSG_PREVIEW_FRAME:
        LOGV("previewCallback");
        LOGV("previewCallback");
@@ -401,6 +433,7 @@ void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)


void Camera::binderDied(const wp<IBinder>& who) {
void Camera::binderDied(const wp<IBinder>& who) {
    LOGW("ICamera died");
    LOGW("ICamera died");
    notifyCallback(CAMERA_MSG_ERROR, DEAD_OBJECT, 0);
    if (mErrorCallback) {
    if (mErrorCallback) {
        mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie);
        mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie);
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -41,7 +41,7 @@ using namespace android;
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------


// helper function to extract a native Camera object from a Camera Java object
// helper function to extract a native Camera object from a Camera Java object
extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct camera_context_t** context);
extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);


struct fields_t {
struct fields_t {
    jfieldID    context;
    jfieldID    context;