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
This commit is contained in:
Android (Google) Code Review
2009-06-24 08:34:34 -07:00
4 changed files with 208 additions and 188 deletions

View File

@@ -53,19 +53,33 @@ struct fields_t {
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 @@ sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pCont
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;
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;
}
mCameraJObjectWeak = env->NewGlobalRef(weak_this);
mCameraJClass = (jclass)env->NewGlobalRef(clazz);
mCamera = camera;
}
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 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj
// 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 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj
// 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 @@ static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
// 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 android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz,
}
}
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 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t
// 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 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t
} 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)

View File

@@ -86,6 +86,14 @@ class Surface;
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 @@ public:
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 @@ private:
autofocus_callback mAutoFocusCallback;
void *mAutoFocusCallbackCookie;
sp<CameraListener> mListener;
friend class DeathNotifier;
static Mutex mLock;

View File

@@ -337,9 +337,32 @@ void Camera::setErrorCallback(error_callback cb, void *cookie)
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 @@ void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
// 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::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)
void Camera::binderDied(const wp<IBinder>& who) {
LOGW("ICamera died");
notifyCallback(CAMERA_MSG_ERROR, DEAD_OBJECT, 0);
if (mErrorCallback) {
mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie);
}

View File

@@ -41,7 +41,7 @@ using namespace android;
// ----------------------------------------------------------------------------
// 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;