blob: b07ba7d4241d9adea23eeee244641c5744669b3c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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 <utils/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
Dave Sparks5e271152009-06-23 17:30:11 -070056// 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 jobject mCameraJObjectWeak; // weak reference to java object
71 jclass mCameraJClass; // strong reference to java class
72 sp<Camera> mCamera; // strong reference to native object
Dave Sparks5e271152009-06-23 17:30:11 -070073 Mutex mLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074};
75
Dave Sparks5e271152009-06-23 17:30:11 -070076sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077{
78 sp<Camera> camera;
79 Mutex::Autolock _l(sLock);
Dave Sparks5e271152009-06-23 17:30:11 -070080 JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 if (context != NULL) {
Dave Sparks5e271152009-06-23 17:30:11 -070082 camera = context->getCamera();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 }
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
Dave Sparks5e271152009-06-23 17:30:11 -070093JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094{
Dave Sparks5e271152009-06-23 17:30:11 -070095 mCameraJObjectWeak = env->NewGlobalRef(weak_this);
96 mCameraJClass = (jclass)env->NewGlobalRef(clazz);
97 mCamera = camera;
98}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
Dave Sparks5e271152009-06-23 17:30:11 -0700100void JNICameraContext::release()
101{
102 LOGV("release");
103 Mutex::Autolock _l(mLock);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 JNIEnv *env = AndroidRuntime::getJNIEnv();
Dave Sparks5e271152009-06-23 17:30:11 -0700105
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");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 return;
126 }
Dave Sparks5e271152009-06-23 17:30:11 -0700127 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227}
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) {
Wu-cheng Lifa3e5562009-05-04 19:38:43 +0800235 jniThrowException(env, "java/lang/RuntimeException",
236 "Fail to connect to camera service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 return;
238 }
239
240 // make sure camera hardware is alive
241 if (camera->getStatus() != NO_ERROR) {
Wu-cheng Liba55b362009-06-10 16:55:39 +0800242 jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 return;
244 }
245
246 jclass clazz = env->GetObjectClass(thiz);
247 if (clazz == NULL) {
Wu-cheng Liba55b362009-06-10 16:55:39 +0800248 jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 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.
Dave Sparks5e271152009-06-23 17:30:11 -0700254 sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
255 context->incStrong(thiz);
256 camera->setListener(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257
258 // save context in opaque field
Dave Sparks5e271152009-06-23 17:30:11 -0700259 env->SetIntField(thiz, fields.context, (int)context.get());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260}
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{
Dave Sparks5e271152009-06-23 17:30:11 -0700268 JNICameraContext* context = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 sp<Camera> camera;
270 {
271 Mutex::Autolock _l(sLock);
Dave Sparks5e271152009-06-23 17:30:11 -0700272 context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273
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) {
Dave Sparks5e271152009-06-23 17:30:11 -0700280 camera = context->getCamera();
281 context->release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 LOGV("native_release: context=%p camera=%p", context, camera.get());
283
284 // clear callbacks
285 if (camera != NULL) {
Dave Sparks5e271152009-06-23 17:30:11 -0700286 camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 camera->disconnect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 }
289
290 // remove context to prevent further Java access
Dave Sparks5e271152009-06-23 17:30:11 -0700291 context->decStrong(thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307static 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) {
Wu-cheng Liba55b362009-06-10 16:55:39 +0800314 jniThrowException(env, "java/lang/RuntimeException", "startPreview failed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 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.
Dave Sparks5e271152009-06-23 17:30:11 -0700342 JNICameraContext* context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 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 }
Dave Sparks5e271152009-06-23 17:30:11 -0700352 camera->setPreviewCallbackFlags(callback_flag);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353}
354
355static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
356{
357 LOGV("autoFocus");
Dave Sparks5e271152009-06-23 17:30:11 -0700358 JNICameraContext* context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 sp<Camera> c = get_native_camera(env, thiz, &context);
360 if (c == 0) return;
361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 if (c->autoFocus() != NO_ERROR) {
Wu-cheng Liba55b362009-06-10 16:55:39 +0800363 jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365}
366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
368{
369 LOGV("takePicture");
Dave Sparks5e271152009-06-23 17:30:11 -0700370 JNICameraContext* context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 sp<Camera> camera = get_native_camera(env, thiz, &context);
372 if (camera == 0) return;
373
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 if (camera->takePicture() != NO_ERROR) {
Wu-cheng Liba55b362009-06-10 16:55:39 +0800375 jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 return;
377 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378}
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) {
Wu-cheng Liba55b362009-06-10 16:55:39 +0800393 jniThrowException(env, "java/lang/RuntimeException", "setParameters failed");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 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