am 78a50aa1: Merge change If3c30fc9 into eclair

Merge commit '78a50aa1db6572ba7d9f9d91c6eb16f993c09f40' into eclair-mr2

* commit '78a50aa1db6572ba7d9f9d91c6eb16f993c09f40':
  Patching in hidden API to allow app managed preview frame buffers.
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0c1c7ec..4b733ef 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -81,6 +81,7 @@
     private ZoomCallback mZoomCallback;
     private ErrorCallback mErrorCallback;
     private boolean mOneShot;
+    private boolean mWithBuffer;
 
     /**
      * Returns a new Camera object.
@@ -229,9 +230,10 @@
     public final void setPreviewCallback(PreviewCallback cb) {
         mPreviewCallback = cb;
         mOneShot = false;
+        mWithBuffer = false;
         // Always use one-shot mode. We fake camera preview mode by
         // doing one-shot preview continuously.
-        setHasPreviewCallback(cb != null, true);
+        setHasPreviewCallback(cb != null, false);
     }
 
     /**
@@ -241,14 +243,48 @@
      * @param cb A callback object that receives a copy of the preview frame.
      */
     public final void setOneShotPreviewCallback(PreviewCallback cb) {
-        if (cb != null) {
-            mPreviewCallback = cb;
-            mOneShot = true;
-            setHasPreviewCallback(true, true);
-        }
+        mPreviewCallback = cb;
+        mOneShot = true;
+        mWithBuffer = false;
+        setHasPreviewCallback(cb != null, false);
     }
 
-    private native final void setHasPreviewCallback(boolean installed, boolean oneshot);
+    private native final void setHasPreviewCallback(boolean installed, boolean manualBuffer);
+
+    /**
+     * Installs a callback which will get called as long as there are buffers in the
+     * preview buffer queue, which minimizes dynamic allocation of preview buffers.
+     *
+     * Apps must call addCallbackBuffer to explicitly register the buffers to use, or no callbacks
+     * will be received. addCallbackBuffer may be safely called before or after
+     * a call to setPreviewCallbackWithBuffer with a non-null callback parameter.
+     *
+     * The buffer queue will be cleared upon any calls to setOneShotPreviewCallback,
+     * setPreviewCallback, or to this method with a null callback parameter.
+     *
+     * @param cb A callback object that receives a copy of the preview frame.  A null value will clear the queue.
+     * @hide
+     */
+    public final void setPreviewCallbackWithBuffer(PreviewCallback cb) {
+        mPreviewCallback = cb;
+        mOneShot = false;
+        mWithBuffer = true;
+        setHasPreviewCallback(cb != null, true);
+    }
+
+    /**
+     * Adds a pre-allocated buffer to the callback buffer queue.
+     * Preview width and height can be determined from getPreviewSize, and bitsPerPixel can be
+     * found from from  {@link android.hardware.Camera.Parameters#getPreviewFormat()} and
+     * {@link android.graphics.PixelFormat#getPixelFormatInfo(int, PixelFormat)}
+     *
+     * Alternatively, a buffer from a previous callback may be passed in or used
+     * to determine the size of new preview frame buffers.
+     *
+     * @param callbackBuffer The buffer to register. Size should be width * height * bitsPerPixel / 8.
+     * @hide
+     */
+    public native final void addCallbackBuffer(byte[] callbackBuffer);
 
     private class EventHandler extends Handler
     {
@@ -288,11 +324,11 @@
                         // in case the app calls setPreviewCallback from
                         // the callback function
                         mPreviewCallback = null;
-                    } else {
+                    } else if (!mWithBuffer) {
                         // We're faking the camera preview mode to prevent
                         // the app from being flooded with preview frames.
                         // Set to oneshot mode again.
-                        setHasPreviewCallback(true, true);
+                        setHasPreviewCallback(true, false);
                     }
                     cb.onPreviewFrame((byte[])msg.obj, mCamera);
                 }
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 5b6bd35..d57e526 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -23,6 +23,8 @@
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
 
+#include <utils/Vector.h>
+
 #include <ui/Surface.h>
 #include <ui/Camera.h>
 #include <binder/IMemory.h>
@@ -47,16 +49,23 @@
     virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
     virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
     virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
+    void addCallbackBuffer(JNIEnv *env, jbyteArray cbb);
+    void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
     sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
     void release();
 
 private:
     void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType);
+    void clearCallbackBuffers_l(JNIEnv *env);
 
     jobject     mCameraJObjectWeak;     // weak reference to java object
     jclass      mCameraJClass;          // strong reference to java class
     sp<Camera>  mCamera;                // strong reference to native object
     Mutex       mLock;
+
+    Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[]
+    bool mManualBufferMode;              // Whether to use application managed buffers.
+    bool mManualCameraCallbackSet;       // Whether the callback has been set, used to reduce unnecessary calls to set the callback.
 };
 
 sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
@@ -81,6 +90,9 @@
     mCameraJObjectWeak = env->NewGlobalRef(weak_this);
     mCameraJClass = (jclass)env->NewGlobalRef(clazz);
     mCamera = camera;
+
+    mManualBufferMode = false;
+    mManualCameraCallbackSet = false;
 }
 
 void JNICameraContext::release()
@@ -97,6 +109,7 @@
         env->DeleteGlobalRef(mCameraJClass);
         mCameraJClass = NULL;
     }
+    clearCallbackBuffers_l(env);
     mCamera.clear();
 }
 
@@ -129,7 +142,42 @@
 
         if (heapBase != NULL) {
             const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
-            obj = env->NewByteArray(size);
+
+            if (!mManualBufferMode) {
+                LOGV("Allocating callback buffer");
+                obj = env->NewByteArray(size);
+            } else {
+                // Vector access should be protected by lock in postData()
+                if(!mCallbackBuffers.isEmpty()) {
+                    LOGV("Using callback buffer from queue of length %d", mCallbackBuffers.size());
+                    jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);
+                    mCallbackBuffers.removeAt(0);
+
+                    obj = (jbyteArray)env->NewLocalRef(globalBuffer);
+                    env->DeleteGlobalRef(globalBuffer);
+
+                    if (obj != NULL) {
+                        jsize bufferLength = env->GetArrayLength(obj);
+                        if ((int)bufferLength < (int)size) {
+                            LOGE("Manually set buffer was too small! Expected %d bytes, but got %d!",
+                                 size, bufferLength);
+                            env->DeleteLocalRef(obj);
+                            return;
+                        }
+                    }
+                }
+
+                if(mCallbackBuffers.isEmpty()) {
+                    LOGW("Out of buffers, clearing callback!");
+                    mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+                    mManualCameraCallbackSet = false;
+
+                    if (obj == NULL) {
+                        return;
+                    }
+                }
+            }
+
             if (obj == NULL) {
                 LOGE("Couldn't allocate byte array for JPEG data");
                 env->ExceptionClear();
@@ -184,6 +232,62 @@
     postData(msgType, dataPtr);
 }
 
+void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualMode)
+{
+    Mutex::Autolock _l(mLock);
+    mManualBufferMode = manualMode;
+    mManualCameraCallbackSet = false;
+
+    // In order to limit the over usage of binder threads, all non-manual buffer
+    // callbacks use FRAME_CALLBACK_FLAG_BARCODE_SCANNER mode now.
+    //
+    // Continuous callbacks will have the callback re-registered from handleMessage.
+    // Manual buffer mode will operate as fast as possible, relying on the finite supply
+    // of buffers for throttling.
+
+    if (!installed) {
+        mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
+        clearCallbackBuffers_l(env);
+    } else if (mManualBufferMode) {
+        if (!mCallbackBuffers.isEmpty()) {
+            mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
+            mManualCameraCallbackSet = true;
+        }
+    } else {
+        mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_BARCODE_SCANNER);
+        clearCallbackBuffers_l(env);
+    }
+}
+
+void JNICameraContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb)
+{
+    if (cbb != NULL) {
+        Mutex::Autolock _l(mLock);
+        jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb);
+        mCallbackBuffers.push(cbb);
+
+        LOGV("Adding callback buffer to queue, %d total", mCallbackBuffers.size());
+
+        // We want to make sure the camera knows we're ready for the next frame.
+        // This may have come unset had we not had a callbackbuffer ready for it last time.
+        if (mManualBufferMode && !mManualCameraCallbackSet) {
+            mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_CAMERA);
+            mManualCameraCallbackSet = true;
+        }
+    } else {
+       LOGE("Null byte array!");
+    }
+}
+
+void JNICameraContext::clearCallbackBuffers_l(JNIEnv *env)
+{
+    LOGV("Clearing callback buffers, %d remained", mCallbackBuffers.size());
+    while(!mCallbackBuffers.isEmpty()) {
+        env->DeleteGlobalRef(mCallbackBuffers.top());
+        mCallbackBuffers.pop();
+    }
+}
+
 // connect to camera service
 static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
 {
@@ -297,8 +401,9 @@
     return c->previewEnabled();
 }
 
-static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean oneshot)
+static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject thiz, jboolean installed, jboolean manualBuffer)
 {
+    LOGV("setHasPreviewCallback: installed:%d, manualBuffer:%d", (int)installed, (int)manualBuffer);
     // 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.
@@ -306,13 +411,19 @@
     sp<Camera> camera = get_native_camera(env, thiz, &context);
     if (camera == 0) return;
 
-    int callback_flag;
-    if (installed) {
-        callback_flag = oneshot ? FRAME_CALLBACK_FLAG_BARCODE_SCANNER : FRAME_CALLBACK_FLAG_CAMERA;
-    } else {
-        callback_flag = FRAME_CALLBACK_FLAG_NOOP;
+    // setCallbackMode will take care of setting the context flags and calling
+    // camera->setPreviewCallbackFlags within a mutex for us.
+    context->setCallbackMode(env, installed, manualBuffer);
+}
+
+static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) {
+    LOGV("addCallbackBuffer");
+
+    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
+
+    if (context != NULL) {
+        context->addCallbackBuffer(env, bytes);
     }
-    camera->setPreviewCallbackFlags(callback_flag);
 }
 
 static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz)
@@ -459,6 +570,9 @@
   { "setHasPreviewCallback",
     "(ZZ)V",
     (void *)android_hardware_Camera_setHasPreviewCallback },
+  { "addCallbackBuffer",
+    "([B)V",
+    (void *)android_hardware_Camera_addCallbackBuffer },
   { "native_autoFocus",
     "()V",
     (void *)android_hardware_Camera_autoFocus },