Merge change 5158 into donut
* changes:
Use a ref-counted callback interface for Camera. This allows the camera service to hang onto the callback interface until all callbacks have been processed. This prevents problems where pending callbacks in binder worker threads are processed after the Java camera object and its associated native resources have been released. Bug 1884362
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 9b92bc5..b07ba7d 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -53,19 +53,33 @@
static fields_t fields;
static Mutex sLock;
-struct camera_context_t {
+// provides persistent context for calls from native code to Java
+class JNICameraContext: public CameraListener
+{
+public:
+ JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
+ ~JNICameraContext() { release(); }
+ virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
+ virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
+ sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
+ void release();
+
+private:
+ void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
+
jobject mCameraJObjectWeak; // weak reference to java object
jclass mCameraJClass; // strong reference to java class
sp<Camera> mCamera; // strong reference to native object
+ Mutex mLock;
};
-sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pContext)
+sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
sp<Camera> camera;
Mutex::Autolock _l(sLock);
- camera_context_t* context = reinterpret_cast<camera_context_t*>(env->GetIntField(thiz, fields.context));
+ JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
if (context != NULL) {
- camera = context->mCamera;
+ camera = context->getCamera();
}
LOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
if (camera == 0) {
@@ -76,30 +90,140 @@
return camera;
}
-static void err_callback(status_t err, void *cookie)
+JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera)
{
- camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
- if ((context == NULL) || (context->mCamera == 0)) return;
+ mCameraJObjectWeak = env->NewGlobalRef(weak_this);
+ mCameraJClass = (jclass)env->NewGlobalRef(clazz);
+ mCamera = camera;
+}
- LOGV("err_callback: context=%p, camera=%p", context, context->mCamera.get());
-
- int error;
- switch (err) {
- case DEAD_OBJECT:
- error = kCameraErrorMediaServer;
- break;
- default:
- error = kCameraErrorUnknown;
- break;
- }
-
+void JNICameraContext::release()
+{
+ LOGV("release");
+ Mutex::Autolock _l(mLock);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- LOGE("err_callback on dead VM");
+
+ if (mCameraJObjectWeak != NULL) {
+ env->DeleteGlobalRef(mCameraJObjectWeak);
+ mCameraJObjectWeak = NULL;
+ }
+ if (mCameraJClass != NULL) {
+ env->DeleteGlobalRef(mCameraJClass);
+ mCameraJClass = NULL;
+ }
+ mCamera.clear();
+}
+
+void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2)
+{
+ LOGV("notify");
+
+ // VM pointer will be NULL if object is released
+ Mutex::Autolock _l(mLock);
+ if (mCameraJObjectWeak == NULL) {
+ LOGW("callback on dead camera object");
return;
}
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kErrorCallback, error, 0, NULL);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ // parse message
+ switch (msgType) {
+ case CAMERA_MSG_ERROR:
+ LOGV("errorCallback");
+ int error;
+ switch (ext1) {
+ case DEAD_OBJECT:
+ error = kCameraErrorMediaServer;
+ break;
+ default:
+ error = kCameraErrorUnknown;
+ break;
+ }
+ env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+ mCameraJObjectWeak, kErrorCallback, error, 0, NULL);
+ break;
+ case CAMERA_MSG_FOCUS:
+ LOGV("autoFocusCallback");
+ env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+ mCameraJObjectWeak, kAutoFocusCallback, ext1, 0, NULL);
+ break;
+ case CAMERA_MSG_SHUTTER:
+ LOGV("shutterCallback");
+ env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+ mCameraJObjectWeak, kShutterCallback, 0, 0, NULL);
+ break;
+ default:
+ LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2);
+ break;
+ }
+}
+
+void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
+{
+ jbyteArray obj = NULL;
+
+ // allocate Java byte array and copy data
+ if (dataPtr != NULL) {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);
+ LOGV("postData: off=%d, size=%d", offset, size);
+ uint8_t *heapBase = (uint8_t*)heap->base();
+
+ if (heapBase != NULL) {
+ uint8_t *data = heapBase + offset;
+ obj = env->NewByteArray(size);
+ if (obj == NULL) {
+ LOGE("Couldn't allocate byte array for JPEG data");
+ env->ExceptionClear();
+ } else {
+ jbyte *bytes = env->GetByteArrayElements(obj, NULL);
+ memcpy(bytes, data, size);
+ env->ReleaseByteArrayElements(obj, bytes, 0);
+
+ }
+ } else {
+ LOGE("image heap is NULL");
+ }
+ }
+
+ // post image data to Java
+ env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+ mCameraJObjectWeak, msgType, 0, 0, obj);
+ if (obj) {
+ env->DeleteLocalRef(obj);
+ }
+}
+
+void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
+{
+ // VM pointer will be NULL if object is released
+ Mutex::Autolock _l(mLock);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ // return data based on callback type
+ switch(msgType) {
+ case CAMERA_MSG_PREVIEW_FRAME:
+ LOGV("previewCallback");
+ copyAndPost(env, dataPtr, kPreviewCallback);
+ break;
+ case CAMERA_MSG_VIDEO_FRAME:
+ LOGV("recordingCallback");
+ break;
+ case CAMERA_MSG_RAW_IMAGE:
+ LOGV("rawCallback");
+ env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+ mCameraJObjectWeak, kRawCallback, 0, 0, NULL);
+ break;
+ case CAMERA_MSG_COMPRESSED_IMAGE:
+ LOGV("jpegCallback");
+ copyAndPost(env, dataPtr, kJpegCallback);
+ break;
+ default:
+ LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
+ break;
+ }
+
}
// connect to camera service
@@ -127,19 +251,12 @@
// We use a weak reference so the Camera object can be garbage collected.
// The reference is only used as a proxy for callbacks.
- camera_context_t* context = new camera_context_t;
- context->mCameraJObjectWeak = env->NewGlobalRef(weak_this);
- context->mCameraJClass = (jclass)env->NewGlobalRef(clazz);
- context->mCamera = camera;
+ sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
+ context->incStrong(thiz);
+ camera->setListener(context);
// save context in opaque field
- env->SetIntField(thiz, fields.context, (int)context);
-
- LOGV("native_setup: mCameraJObjectWeak=%x, camera_obj=%x, context=%p",
- (int)context->mCameraJObjectWeak, (int)thiz, context);
-
- // set error callback
- camera->setErrorCallback(err_callback, context);
+ env->SetIntField(thiz, fields.context, (int)context.get());
}
// disconnect from camera service
@@ -148,11 +265,11 @@
// finalizer is invoked later.
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
- camera_context_t* context = NULL;
+ JNICameraContext* context = NULL;
sp<Camera> camera;
{
Mutex::Autolock _l(sLock);
- context = reinterpret_cast<camera_context_t*>(env->GetIntField(thiz, fields.context));
+ context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
// Make sure we do not attempt to callback on a deleted Java object.
env->SetIntField(thiz, fields.context, 0);
@@ -160,21 +277,18 @@
// clean up if release has not been called before
if (context != NULL) {
- camera = context->mCamera;
- context->mCamera.clear();
+ camera = context->getCamera();
+ context->release();
LOGV("native_release: context=%p camera=%p", context, camera.get());
// clear callbacks
if (camera != NULL) {
- camera->setPreviewCallback(NULL, NULL, FRAME_CALLBACK_FLAG_NOOP);
- camera->setErrorCallback(NULL, NULL);
+ camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
camera->disconnect();
- env->DeleteGlobalRef(context->mCameraJObjectWeak);
- env->DeleteGlobalRef(context->mCameraJClass);
}
// remove context to prevent further Java access
- delete context;
+ context->decStrong(thiz);
}
}
@@ -190,48 +304,6 @@
}
}
-static void preview_callback(const sp<IMemory>& mem, void *cookie)
-{
- LOGV("preview_callback");
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- LOGE("preview_callback on dead VM");
- return;
- }
- camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
- if ((context == NULL) || (context->mCamera == 0)) {
- LOGW("context or camera is NULL in preview_callback");
- return;
- }
- LOGV("native_release: context=%p camera=%p", context, context->mCamera.get());
-
- int arg1 = 0, arg2 = 0;
- jobject obj = NULL;
-
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-
- uint8_t *data = ((uint8_t *)heap->base()) + offset;
-
- jbyteArray array = env->NewByteArray(size);
- if (array == NULL) {
- LOGE("Couldn't allocate byte array for YUV data");
- env->ExceptionClear();
- return;
- }
-
- jbyte *bytes = env->GetByteArrayElements(array, NULL);
- memcpy(bytes, data, size);
- env->ReleaseByteArrayElements(array, bytes, 0);
-
- obj = array;
-
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kPreviewCallback, arg1, arg2, obj);
- env->DeleteLocalRef(array);
-}
-
static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz)
{
LOGV("startPreview");
@@ -267,7 +339,7 @@
// Important: Only install preview_callback if the Java code has called
// setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy
// each preview frame for nothing.
- camera_context_t* context;
+ JNICameraContext* context;
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
@@ -277,130 +349,32 @@
} else {
callback_flag = FRAME_CALLBACK_FLAG_NOOP;
}
- camera->setPreviewCallback(installed ? preview_callback : NULL, context, callback_flag);
-}
-
-static void autofocus_callback_impl(bool success, void *cookie)
-{
- LOGV("autoFocusCallback");
- camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
-
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- LOGE("autofocus_callback on dead VM");
- return;
- }
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kAutoFocusCallback, success, 0, NULL);
+ camera->setPreviewCallbackFlags(callback_flag);
}
static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
{
LOGV("autoFocus");
- camera_context_t* context;
+ JNICameraContext* context;
sp<Camera> c = get_native_camera(env, thiz, &context);
if (c == 0) return;
- c->setAutoFocusCallback(autofocus_callback_impl, context);
if (c->autoFocus() != NO_ERROR) {
jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed");
}
}
-static void jpeg_callback(const sp<IMemory>& mem, void *cookie)
-{
- LOGV("jpegCallback");
- camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
-
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- LOGE("jpeg`_callback on dead VM");
- return;
- }
- int arg1 = 0, arg2 = 0;
- jobject obj = NULL;
-
- if (mem == NULL) {
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, NULL);
- return;
- }
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- LOGV("jpeg_callback: mem off=%d, size=%d", offset, size);
-
- uint8_t *heap_base = (uint8_t *)heap->base();
- if (heap_base == NULL) {
- LOGE("YUV heap is NULL");
- return;
- }
-
- uint8_t *data = heap_base + offset;
-
- jbyteArray array = env->NewByteArray(size);
- if (array == NULL) {
- LOGE("Couldn't allocate byte array for JPEG data");
- env->ExceptionClear();
- return;
- }
-
- jbyte *bytes = env->GetByteArrayElements(array, NULL);
- memcpy(bytes, data, size);
- env->ReleaseByteArrayElements(array, bytes, 0);
-
- obj = array;
-
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, obj);
- env->DeleteLocalRef(array);
-}
-
-static void shutter_callback_impl(void *cookie)
-{
- LOGV("shutterCallback");
- camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
-
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- LOGE("shutter_callback on dead VM");
- return;
- }
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kShutterCallback, 0, 0, NULL);
-}
-
-static void raw_callback(const sp<IMemory>& mem __attribute__((unused)),
- void *cookie)
-{
- LOGV("rawCallback");
- camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie);
-
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- LOGE("raw_callback on dead VM");
- return;
- }
- env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event,
- context->mCameraJObjectWeak, kRawCallback, 0, 0, NULL);
-}
-
static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz)
{
LOGV("takePicture");
- camera_context_t* context;
+ JNICameraContext* context;
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
- camera->setShutterCallback(shutter_callback_impl, context);
- camera->setRawCallback(raw_callback, context);
- camera->setJpegCallback(jpeg_callback, context);
if (camera->takePicture() != NO_ERROR) {
jniThrowException(env, "java/lang/RuntimeException", "takePicture failed");
return;
}
-
- return;
}
static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params)
diff --git a/include/ui/Camera.h b/include/ui/Camera.h
index 048bdd5..bd4855c 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -86,6 +86,14 @@
class Mutex;
class String8;
+// ref-counted object for callbacks
+class CameraListener: virtual public RefBase
+{
+public:
+ virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
+ virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr) = 0;
+};
+
typedef void (*shutter_callback)(void *cookie);
typedef void (*frame_callback)(const sp<IMemory>& mem, void *cookie);
typedef void (*autofocus_callback)(bool focused, void *cookie);
@@ -152,6 +160,9 @@
void setErrorCallback(error_callback cb, void *cookie);
void setAutoFocusCallback(autofocus_callback cb, void *cookie);
+ void setListener(const sp<CameraListener>& listener);
+ void setPreviewCallbackFlags(int preview_callback_flag);
+
// ICameraClient interface
virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
virtual void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr);
@@ -194,6 +205,8 @@
autofocus_callback mAutoFocusCallback;
void *mAutoFocusCallbackCookie;
+ sp<CameraListener> mListener;
+
friend class DeathNotifier;
static Mutex mLock;
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index 6613700..1aa52452 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -337,9 +337,32 @@
mErrorCallbackCookie = cookie;
}
+void Camera::setListener(const sp<CameraListener>& listener)
+{
+ Mutex::Autolock _l(mLock);
+ mListener = listener;
+}
+
+void Camera::setPreviewCallbackFlags(int flag)
+{
+ LOGV("setPreviewCallbackFlags");
+ sp <ICamera> c = mCamera;
+ if (c == 0) return;
+ mCamera->setPreviewCallbackFlag(flag);
+}
+
// callback from camera service
void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
{
+ sp<CameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ listener->notify(msgType, ext1, ext2);
+ }
+
switch(msgType) {
case CAMERA_MSG_ERROR:
LOGV("errorCallback");
@@ -368,6 +391,15 @@
// callback from camera service when frame or image is ready
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)
{
+ sp<CameraListener> listener;
+ {
+ Mutex::Autolock _l(mLock);
+ listener = mListener;
+ }
+ if (listener != NULL) {
+ listener->postData(msgType, dataPtr);
+ }
+
switch(msgType) {
case CAMERA_MSG_PREVIEW_FRAME:
LOGV("previewCallback");
@@ -401,6 +433,7 @@
void Camera::binderDied(const wp<IBinder>& who) {
LOGW("ICamera died");
+ notifyCallback(CAMERA_MSG_ERROR, DEAD_OBJECT, 0);
if (mErrorCallback) {
mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie);
}
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 7bfeb83..0273a5a 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -41,7 +41,7 @@
// ----------------------------------------------------------------------------
// helper function to extract a native Camera object from a Camera Java object
-extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct camera_context_t** context);
+extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);
struct fields_t {
jfieldID context;