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