android_hardware_Camera.cpp revision 36f68b8f24df906c969581b0b8e1a47f95dc03cb
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 <ui/Surface.h>
27#include <ui/Camera.h>
28#include <binder/IMemory.h>
29
30using namespace android;
31
32struct fields_t {
33    jfieldID    context;
34    jfieldID    surface;
35    jmethodID   post_event;
36};
37
38static fields_t fields;
39static Mutex sLock;
40
41// provides persistent context for calls from native code to Java
42class JNICameraContext: public CameraListener
43{
44public:
45    JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
46    ~JNICameraContext() { release(); }
47    virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
48    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
49    virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
50    sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
51    void release();
52
53private:
54    void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
55
56    jobject     mCameraJObjectWeak;     // weak reference to java object
57    jclass      mCameraJClass;          // strong reference to java class
58    sp<Camera>  mCamera;                // strong reference to native object
59    Mutex       mLock;
60};
61
62sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
63{
64    sp<Camera> camera;
65    Mutex::Autolock _l(sLock);
66    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
67    if (context != NULL) {
68        camera = context->getCamera();
69    }
70    LOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
71    if (camera == 0) {
72        jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
73    }
74
75    if (pContext != NULL) *pContext = context;
76    return camera;
77}
78
79JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
80{
81    mCameraJObjectWeak = env->NewGlobalRef(weak_this);
82    mCameraJClass = (jclass)env->NewGlobalRef(clazz);
83    mCamera = camera;
84}
85
86void JNICameraContext::release()
87{
88    LOGV("release");
89    Mutex::Autolock _l(mLock);
90    JNIEnv *env = AndroidRuntime::getJNIEnv();
91
92    if (mCameraJObjectWeak != NULL) {
93        env->DeleteGlobalRef(mCameraJObjectWeak);
94        mCameraJObjectWeak = NULL;
95    }
96    if (mCameraJClass != NULL) {
97        env->DeleteGlobalRef(mCameraJClass);
98        mCameraJClass = NULL;
99    }
100    mCamera.clear();
101}
102
103void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2)
104{
105    LOGV("notify");
106
107    // VM pointer will be NULL if object is released
108    Mutex::Autolock _l(mLock);
109    if (mCameraJObjectWeak == NULL) {
110        LOGW("callback on dead camera object");
111        return;
112    }
113    JNIEnv *env = AndroidRuntime::getJNIEnv();
114    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
115            mCameraJObjectWeak, msgType, ext1, ext2, NULL);
116}
117
118void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
119{
120    jbyteArray obj = NULL;
121
122    // allocate Java byte array and copy data
123    if (dataPtr != NULL) {
124        ssize_t offset;
125        size_t size;
126        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
127        LOGV("postData: off=%d, size=%d", offset, size);
128        uint8_t *heapBase = (uint8_t*)heap->base();
129
130        if (heapBase != NULL) {
131            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
132            obj = env->NewByteArray(size);
133            if (obj == NULL) {
134                LOGE("Couldn't allocate byte array for JPEG data");
135                env->ExceptionClear();
136            } else {
137                env->SetByteArrayRegion(obj, 0, size, data);
138            }
139        } else {
140            LOGE("image heap is NULL");
141        }
142    }
143
144    // post image data to Java
145    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
146            mCameraJObjectWeak, msgType, 0, 0, obj);
147    if (obj) {
148        env->DeleteLocalRef(obj);
149    }
150}
151
152void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
153{
154    // VM pointer will be NULL if object is released
155    Mutex::Autolock _l(mLock);
156    JNIEnv *env = AndroidRuntime::getJNIEnv();
157    if (mCameraJObjectWeak == NULL) {
158        LOGW("callback on dead camera object");
159        return;
160    }
161
162    // return data based on callback type
163    switch(msgType) {
164    case CAMERA_MSG_VIDEO_FRAME:
165        // should never happen
166        break;
167    // don't return raw data to Java
168    case CAMERA_MSG_RAW_IMAGE:
169        LOGV("rawCallback");
170        env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
171                mCameraJObjectWeak, msgType, 0, 0, NULL);
172        break;
173    default:
174        // TODO: Change to LOGV
175        LOGD("dataCallback(%d, %p)", msgType, dataPtr.get());
176        copyAndPost(env, dataPtr, msgType);
177        break;
178    }
179}
180
181void JNICameraContext::postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
182{
183    // TODO: plumb up to Java. For now, just drop the timestamp
184    postData(msgType, dataPtr);
185}
186
187// connect to camera service
188static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
189{
190    sp<Camera> camera = Camera::connect();
191
192    if (camera == NULL) {
193        jniThrowException(env, "java/lang/RuntimeException",
194                          "Fail to connect to camera service");
195        return;
196    }
197
198    // make sure camera hardware is alive
199    if (camera->getStatus() != NO_ERROR) {
200        jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed");
201        return;
202    }
203
204    jclass clazz = env->GetObjectClass(thiz);
205    if (clazz == NULL) {
206        jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera");
207        return;
208    }
209
210    // We use a weak reference so the Camera object can be garbage collected.
211    // The reference is only used as a proxy for callbacks.
212    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
213    context->incStrong(thiz);
214    camera->setListener(context);
215
216    // save context in opaque field
217    env->SetIntField(thiz, fields.context, (int)context.get());
218}
219
220// disconnect from camera service
221// It's okay to call this when the native camera context is already null.
222// This handles the case where the user has called release() and the
223// finalizer is invoked later.
224static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
225{
226    // TODO: Change to LOGV
227    LOGD("release camera");
228    JNICameraContext* context = NULL;
229    sp<Camera> camera;
230    {
231        Mutex::Autolock _l(sLock);
232        context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
233
234        // Make sure we do not attempt to callback on a deleted Java object.
235        env->SetIntField(thiz, fields.context, 0);
236    }
237
238    // clean up if release has not been called before
239    if (context != NULL) {
240        camera = context->getCamera();
241        context->release();
242        LOGV("native_release: context=%p camera=%p", context, camera.get());
243
244        // clear callbacks
245        if (camera != NULL) {
246            camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
247            camera->disconnect();
248        }
249
250        // remove context to prevent further Java access
251        context->decStrong(thiz);
252    }
253}
254
255static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
256{
257    LOGV("setPreviewDisplay");
258    sp<Camera> camera = get_native_camera(env, thiz, NULL);
259    if (camera == 0) return;
260
261    sp<Surface> surface = NULL;
262    if (jSurface != NULL) {
263        surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
264    }
265    if (camera->setPreviewDisplay(surface) != NO_ERROR) {
266        jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
267    }
268}
269
270static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
271{
272    LOGV("startPreview");
273    sp<Camera> camera = get_native_camera(env, thiz, NULL);
274    if (camera == 0) return;
275
276    if (camera->startPreview() != NO_ERROR) {
277        jniThrowException(env, "java/lang/RuntimeException", "startPreview failed");
278        return;
279    }
280}
281
282static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz)
283{
284    LOGV("stopPreview");
285    sp<Camera> c = get_native_camera(env, thiz, NULL);
286    if (c == 0) return;
287
288    c->stopPreview();
289}
290
291static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz)
292{
293    LOGV("previewEnabled");
294    sp<Camera> c = get_native_camera(env, thiz, NULL);
295    if (c == 0) return false;
296
297    return c->previewEnabled();
298}
299
300static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot)
301{
302    // Important: Only install preview_callback if the Java code has called
303    // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
304    // each preview frame for nothing.
305    JNICameraContext* context;
306    sp<Camera> camera = get_native_camera(env, thiz, &context);
307    if (camera == 0) return;
308
309    int callback_flag;
310    if (installed) {
311        callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA;
312    } else {
313        callback_flag = FRAME_CALLBACK_FLAG_NOOP;
314    }
315    camera->setPreviewCallbackFlags(callback_flag);
316}
317
318static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
319{
320    LOGV("autoFocus");
321    JNICameraContext* context;
322    sp<Camera> c = get_native_camera(env, thiz, &context);
323    if (c == 0) return;
324
325    if (c->autoFocus() != NO_ERROR) {
326        jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
327    }
328}
329
330static void android_hardware_Camera_cancelAutoFocus(JNIEnv *env, jobject thiz)
331{
332    LOGV("cancelAutoFocus");
333    JNICameraContext* context;
334    sp<Camera> c = get_native_camera(env, thiz, &context);
335    if (c == 0) return;
336
337    if (c->cancelAutoFocus() != NO_ERROR) {
338        jniThrowException(env, "java/lang/RuntimeException", "cancelAutoFocus failed");
339    }
340}
341
342static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
343{
344    LOGV("takePicture");
345    JNICameraContext* context;
346    sp<Camera> camera = get_native_camera(env, thiz, &context);
347    if (camera == 0) return;
348
349    if (camera->takePicture() != NO_ERROR) {
350        jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
351        return;
352    }
353}
354
355static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params)
356{
357    LOGV("setParameters");
358    sp<Camera> camera = get_native_camera(env, thiz, NULL);
359    if (camera == 0) return;
360
361    const jchar* str = env->GetStringCritical(params, 0);
362    String8 params8;
363    if (params) {
364        params8 = String8(str, env->GetStringLength(params));
365        env->ReleaseStringCritical(params, str);
366    }
367    if (camera->setParameters(params8) != NO_ERROR) {
368        jniThrowException(env, "java/lang/RuntimeException", "setParameters failed");
369        return;
370    }
371}
372
373static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz)
374{
375    LOGV("getParameters");
376    sp<Camera> camera = get_native_camera(env, thiz, NULL);
377    if (camera == 0) return 0;
378
379    return env->NewStringUTF(camera->getParameters().string());
380}
381
382static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz)
383{
384    LOGV("reconnect");
385    sp<Camera> camera = get_native_camera(env, thiz, NULL);
386    if (camera == 0) return;
387
388    if (camera->reconnect() != NO_ERROR) {
389        jniThrowException(env, "java/io/IOException", "reconnect failed");
390        return;
391    }
392}
393
394static void android_hardware_Camera_lock(JNIEnv *env, jobject thiz)
395{
396    LOGV("lock");
397    sp<Camera> camera = get_native_camera(env, thiz, NULL);
398    if (camera == 0) return;
399
400    if (camera->lock() != NO_ERROR) {
401        jniThrowException(env, "java/lang/RuntimeException", "lock failed");
402    }
403}
404
405static void android_hardware_Camera_unlock(JNIEnv *env, jobject thiz)
406{
407    LOGV("unlock");
408    sp<Camera> camera = get_native_camera(env, thiz, NULL);
409    if (camera == 0) return;
410
411    if (camera->unlock() != NO_ERROR) {
412        jniThrowException(env, "java/lang/RuntimeException", "unlock failed");
413    }
414}
415
416static void android_hardware_Camera_startSmoothZoom(JNIEnv *env, jobject thiz, jint value)
417{
418    LOGD("startSmoothZoom");
419    sp<Camera> camera = get_native_camera(env, thiz, NULL);
420    if (camera == 0) return;
421
422    if (camera->sendCommand(CAMERA_CMD_START_SMOOTH_ZOOM, value, 0) != NO_ERROR) {
423        jniThrowException(env, "java/lang/RuntimeException", "start smooth zoom failed");
424    }
425}
426
427static void android_hardware_Camera_stopSmoothZoom(JNIEnv *env, jobject thiz)
428{
429    LOGD("stopSmoothZoom");
430    sp<Camera> camera = get_native_camera(env, thiz, NULL);
431    if (camera == 0) return;
432
433    if (camera->sendCommand(CAMERA_CMD_STOP_SMOOTH_ZOOM, 0, 0) != NO_ERROR) {
434        jniThrowException(env, "java/lang/RuntimeException", "stop smooth zoom failed");
435    }
436}
437
438//-------------------------------------------------
439
440static JNINativeMethod camMethods[] = {
441  { "native_setup",
442    "(Ljava/lang/Object;)V",
443    (void*)android_hardware_Camera_native_setup },
444  { "native_release",
445    "()V",
446    (void*)android_hardware_Camera_release },
447  { "setPreviewDisplay",
448    "(Landroid/view/Surface;)V",
449    (void *)android_hardware_Camera_setPreviewDisplay },
450  { "startPreview",
451    "()V",
452    (void *)android_hardware_Camera_startPreview },
453  { "stopPreview",
454    "()V",
455    (void *)android_hardware_Camera_stopPreview },
456  { "previewEnabled",
457    "()Z",
458    (void *)android_hardware_Camera_previewEnabled },
459  { "setHasPreviewCallback",
460    "(ZZ)V",
461    (void *)android_hardware_Camera_setHasPreviewCallback },
462  { "native_autoFocus",
463    "()V",
464    (void *)android_hardware_Camera_autoFocus },
465  { "native_cancelAutoFocus",
466    "()V",
467    (void *)android_hardware_Camera_cancelAutoFocus },
468  { "native_takePicture",
469    "()V",
470    (void *)android_hardware_Camera_takePicture },
471  { "native_setParameters",
472    "(Ljava/lang/String;)V",
473    (void *)android_hardware_Camera_setParameters },
474  { "native_getParameters",
475    "()Ljava/lang/String;",
476    (void *)android_hardware_Camera_getParameters },
477  { "reconnect",
478    "()V",
479    (void*)android_hardware_Camera_reconnect },
480  { "lock",
481    "()V",
482    (void*)android_hardware_Camera_lock },
483  { "unlock",
484    "()V",
485    (void*)android_hardware_Camera_unlock },
486  { "startSmoothZoom",
487    "(I)V",
488    (void *)android_hardware_Camera_startSmoothZoom },
489  { "stopSmoothZoom",
490    "()V",
491    (void *)android_hardware_Camera_stopSmoothZoom },
492};
493
494struct field {
495    const char *class_name;
496    const char *field_name;
497    const char *field_type;
498    jfieldID   *jfield;
499};
500
501static int find_fields(JNIEnv *env, field *fields, int count)
502{
503    for (int i = 0; i < count; i++) {
504        field *f = &fields[i];
505        jclass clazz = env->FindClass(f->class_name);
506        if (clazz == NULL) {
507            LOGE("Can't find %s", f->class_name);
508            return -1;
509        }
510
511        jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
512        if (field == NULL) {
513            LOGE("Can't find %s.%s", f->class_name, f->field_name);
514            return -1;
515        }
516
517        *(f->jfield) = field;
518    }
519
520    return 0;
521}
522
523// Get all the required offsets in java class and register native functions
524int register_android_hardware_Camera(JNIEnv *env)
525{
526    field fields_to_find[] = {
527        { "android/hardware/Camera", "mNativeContext",   "I", &fields.context },
528        { "android/view/Surface",    "mSurface",         "I", &fields.surface }
529    };
530
531    if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
532        return -1;
533
534    jclass clazz = env->FindClass("android/hardware/Camera");
535    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
536                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
537    if (fields.post_event == NULL) {
538        LOGE("Can't find android/hardware/Camera.postEventFromNative");
539        return -1;
540    }
541
542
543    // Register native functions
544    return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
545                                              camMethods, NELEM(camMethods));
546}
547
548