android_hardware_Camera.cpp revision 0d4586d326a60052503619076d143e73e5780ac7
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);
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        LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
175        copyAndPost(env, dataPtr, msgType);
176        break;
177    }
178}
179
180void JNICameraContext::postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
181{
182    // TODO: plumb up to Java. For now, just drop the timestamp
183    postData(msgType, dataPtr);
184}
185
186// connect to camera service
187static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
188{
189    sp<Camera> camera = Camera::connect();
190
191    if (camera == NULL) {
192        jniThrowException(env, "java/lang/RuntimeException",
193                          "Fail to connect to camera service");
194        return;
195    }
196
197    // make sure camera hardware is alive
198    if (camera->getStatus() != NO_ERROR) {
199        jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed");
200        return;
201    }
202
203    jclass clazz = env->GetObjectClass(thiz);
204    if (clazz == NULL) {
205        jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera");
206        return;
207    }
208
209    // We use a weak reference so the Camera object can be garbage collected.
210    // The reference is only used as a proxy for callbacks.
211    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
212    context->incStrong(thiz);
213    camera->setListener(context);
214
215    // save context in opaque field
216    env->SetIntField(thiz, fields.context, (int)context.get());
217}
218
219// disconnect from camera service
220// It's okay to call this when the native camera context is already null.
221// This handles the case where the user has called release() and the
222// finalizer is invoked later.
223static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
224{
225    JNICameraContext* context = NULL;
226    sp<Camera> camera;
227    {
228        Mutex::Autolock _l(sLock);
229        context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
230
231        // Make sure we do not attempt to callback on a deleted Java object.
232        env->SetIntField(thiz, fields.context, 0);
233    }
234
235    // clean up if release has not been called before
236    if (context != NULL) {
237        camera = context->getCamera();
238        context->release();
239        LOGV("native_release: context=%p camera=%p", context, camera.get());
240
241        // clear callbacks
242        if (camera != NULL) {
243            camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
244            camera->disconnect();
245        }
246
247        // remove context to prevent further Java access
248        context->decStrong(thiz);
249    }
250}
251
252static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, jobject jSurface)
253{
254    LOGV("setPreviewDisplay");
255    sp<Camera> camera = get_native_camera(env, thiz, NULL);
256    if (camera == 0) return;
257
258    sp<Surface> surface = NULL;
259    if (jSurface != NULL) {
260        surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
261    }
262    if (camera->setPreviewDisplay(surface) != NO_ERROR) {
263        jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
264    }
265}
266
267static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
268{
269    LOGV("startPreview");
270    sp<Camera> camera = get_native_camera(env, thiz, NULL);
271    if (camera == 0) return;
272
273    if (camera->startPreview() != NO_ERROR) {
274        jniThrowException(env, "java/lang/RuntimeException", "startPreview failed");
275        return;
276    }
277}
278
279static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz)
280{
281    LOGV("stopPreview");
282    sp<Camera> c = get_native_camera(env, thiz, NULL);
283    if (c == 0) return;
284
285    c->stopPreview();
286}
287
288static bool android_hardware_Camera_previewEnabled(JNIEnv *env, jobject thiz)
289{
290    LOGV("previewEnabled");
291    sp<Camera> c = get_native_camera(env, thiz, NULL);
292    if (c == 0) return false;
293
294    return c->previewEnabled();
295}
296
297static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot)
298{
299    // Important: Only install preview_callback if the Java code has called
300    // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
301    // each preview frame for nothing.
302    JNICameraContext* context;
303    sp<Camera> camera = get_native_camera(env, thiz, &context);
304    if (camera == 0) return;
305
306    int callback_flag;
307    if (installed) {
308        callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA;
309    } else {
310        callback_flag = FRAME_CALLBACK_FLAG_NOOP;
311    }
312    camera->setPreviewCallbackFlags(callback_flag);
313}
314
315static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
316{
317    LOGV("autoFocus");
318    JNICameraContext* context;
319    sp<Camera> c = get_native_camera(env, thiz, &context);
320    if (c == 0) return;
321
322    if (c->autoFocus() != NO_ERROR) {
323        jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
324    }
325}
326
327static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
328{
329    LOGV("takePicture");
330    JNICameraContext* context;
331    sp<Camera> camera = get_native_camera(env, thiz, &context);
332    if (camera == 0) return;
333
334    if (camera->takePicture() != NO_ERROR) {
335        jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
336        return;
337    }
338}
339
340static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params)
341{
342    LOGV("setParameters");
343    sp<Camera> camera = get_native_camera(env, thiz, NULL);
344    if (camera == 0) return;
345
346    const jchar* str = env->GetStringCritical(params, 0);
347    String8 params8;
348    if (params) {
349        params8 = String8(str, env->GetStringLength(params));
350        env->ReleaseStringCritical(params, str);
351    }
352    if (camera->setParameters(params8) != NO_ERROR) {
353        jniThrowException(env, "java/lang/RuntimeException", "setParameters failed");
354        return;
355    }
356}
357
358static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz)
359{
360    LOGV("getParameters");
361    sp<Camera> camera = get_native_camera(env, thiz, NULL);
362    if (camera == 0) return 0;
363
364    return env->NewStringUTF(camera->getParameters().string());
365}
366
367static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz)
368{
369    LOGV("reconnect");
370    sp<Camera> camera = get_native_camera(env, thiz, NULL);
371    if (camera == 0) return;
372
373    if (camera->reconnect() != NO_ERROR) {
374        jniThrowException(env, "java/io/IOException", "reconnect failed");
375        return;
376    }
377}
378
379static jint android_hardware_Camera_lock(JNIEnv *env, jobject thiz)
380{
381    LOGV("lock");
382    sp<Camera> camera = get_native_camera(env, thiz, NULL);
383    if (camera == 0) return INVALID_OPERATION;
384    return (jint) camera->lock();
385}
386
387static jint android_hardware_Camera_unlock(JNIEnv *env, jobject thiz)
388{
389    LOGV("unlock");
390    sp<Camera> camera = get_native_camera(env, thiz, NULL);
391    if (camera == 0) return INVALID_OPERATION;
392    return (jint) camera->unlock();
393}
394
395//-------------------------------------------------
396
397static JNINativeMethod camMethods[] = {
398  { "native_setup",
399    "(Ljava/lang/Object;)V",
400    (void*)android_hardware_Camera_native_setup },
401  { "native_release",
402    "()V",
403    (void*)android_hardware_Camera_release },
404  { "setPreviewDisplay",
405    "(Landroid/view/Surface;)V",
406    (void *)android_hardware_Camera_setPreviewDisplay },
407  { "startPreview",
408    "()V",
409    (void *)android_hardware_Camera_startPreview },
410  { "stopPreview",
411    "()V",
412    (void *)android_hardware_Camera_stopPreview },
413  { "previewEnabled",
414    "()Z",
415    (void *)android_hardware_Camera_previewEnabled },
416  { "setHasPreviewCallback",
417    "(ZZ)V",
418    (void *)android_hardware_Camera_setHasPreviewCallback },
419  { "native_autoFocus",
420    "()V",
421    (void *)android_hardware_Camera_autoFocus },
422  { "native_takePicture",
423    "()V",
424    (void *)android_hardware_Camera_takePicture },
425  { "native_setParameters",
426    "(Ljava/lang/String;)V",
427    (void *)android_hardware_Camera_setParameters },
428  { "native_getParameters",
429    "()Ljava/lang/String;",
430    (void *)android_hardware_Camera_getParameters },
431  { "reconnect",
432    "()V",
433    (void*)android_hardware_Camera_reconnect },
434  { "lock",
435    "()I",
436    (void*)android_hardware_Camera_lock },
437  { "unlock",
438    "()I",
439    (void*)android_hardware_Camera_unlock },
440};
441
442struct field {
443    const char *class_name;
444    const char *field_name;
445    const char *field_type;
446    jfieldID   *jfield;
447};
448
449static int find_fields(JNIEnv *env, field *fields, int count)
450{
451    for (int i = 0; i < count; i++) {
452        field *f = &fields[i];
453        jclass clazz = env->FindClass(f->class_name);
454        if (clazz == NULL) {
455            LOGE("Can't find %s", f->class_name);
456            return -1;
457        }
458
459        jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type);
460        if (field == NULL) {
461            LOGE("Can't find %s.%s", f->class_name, f->field_name);
462            return -1;
463        }
464
465        *(f->jfield) = field;
466    }
467
468    return 0;
469}
470
471// Get all the required offsets in java class and register native functions
472int register_android_hardware_Camera(JNIEnv *env)
473{
474    field fields_to_find[] = {
475        { "android/hardware/Camera", "mNativeContext",   "I", &fields.context },
476        { "android/view/Surface",    "mSurface",         "I", &fields.surface }
477    };
478
479    if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0)
480        return -1;
481
482    jclass clazz = env->FindClass("android/hardware/Camera");
483    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
484                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
485    if (fields.post_event == NULL) {
486        LOGE("Can't find android/hardware/Camera.postEventFromNative");
487        return -1;
488    }
489
490
491    // Register native functions
492    return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
493                                              camMethods, NELEM(camMethods));
494}
495
496