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