Merge "Import translations. DO NOT MERGE" into mnc-dev
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d22cfda..8220a74 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -664,11 +664,11 @@
         if (!mHasSurface) {
             throw new IllegalStateException("codec was not configured for an output surface");
         }
-
-        // TODO implement this
-        throw new IllegalArgumentException("codec does not support this surface");
+        native_setSurface(surface);
     }
 
+    private native void native_setSurface(@NonNull Surface surface);
+
     /**
      * Create a persistent input surface that can be used with codecs that normally have an input
      * surface, such as video encoders. A persistent input can be reused by subsequent
@@ -681,12 +681,20 @@
      */
     @NonNull
     public static Surface createPersistentInputSurface() {
-        // TODO implement this
-        return new PersistentSurface();
+        return native_createPersistentInputSurface();
     }
 
     static class PersistentSurface extends Surface {
-        PersistentSurface() {}
+        @SuppressWarnings("unused")
+        PersistentSurface() {} // used by native
+
+        @Override
+        public void release() {
+            native_releasePersistentInputSurface(this);
+            super.release();
+        }
+
+        private long mPersistentObject;
     };
 
     /**
@@ -700,9 +708,17 @@
      *           {@link #createPersistentInputSurface}.
      */
     public void usePersistentInputSurface(@NonNull Surface surface) {
-        throw new IllegalArgumentException("not implemented");
+        if (!(surface instanceof PersistentSurface)) {
+            throw new IllegalArgumentException("not a PersistentSurface");
+        }
+        native_usePersistentInputSurface(surface);
     }
 
+    @NonNull
+    private static native final PersistentSurface native_createPersistentInputSurface();
+    private static native final void native_releasePersistentInputSurface(@NonNull Surface surface);
+    private native final void native_usePersistentInputSurface(@NonNull Surface surface);
+
     private native final void native_setCallback(@Nullable Callback cb);
 
     private native final void native_configure(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 1b054cc..a2f596b 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.Application;
@@ -142,22 +143,28 @@
 
     /**
      * Configures the recorder to use a persistent surface when using SURFACE video source.
-     * <p> May only be called after {@link #prepare} in lieu of {@link #getSurface}.
-     * Frames rendered to the Surface before {@link #start} will be discarded.</p>
+     * <p> May only be called before {@link #prepare}. If called, {@link #getSurface} should
+     * not be used and will throw IllegalStateException. Frames rendered to the Surface
+     * before {@link #start} will be discarded.</p>
 
      * @param surface a persistent input surface created by
      *           {@link MediaCodec#createPersistentInputSurface}
-     * @throws IllegalStateException if it is called before {@link #prepare}, after
-     * {@link #stop}, or is called when VideoSource is not set to SURFACE.
+     * @throws IllegalStateException if it is called after {@link #prepare} and before
+     * {@link #stop}.
      * @throws IllegalArgumentException if the surface was not created by
      *           {@link MediaCodec#createPersistentInputSurface}.
      * @see MediaCodec#createPersistentInputSurface
      * @see MediaRecorder.VideoSource
      */
-    public void usePersistentSurface(Surface surface) {
-        throw new IllegalArgumentException("not implemented");
+    public void usePersistentSurface(@NonNull Surface surface) {
+        if (!(surface instanceof MediaCodec.PersistentSurface)) {
+            throw new IllegalArgumentException("not a PersistentSurface");
+        }
+        native_usePersistentSurface(surface);
     }
 
+    private native final void native_usePersistentSurface(@NonNull Surface surface);
+
     /**
      * Sets a Surface to show a preview of recorded media (video). Calls this
      * before prepare() to make sure that the desirable preview display is
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index e34f9ed..f808c0d1 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -39,7 +39,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/PersistentSurface.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include <system/window.h>
@@ -75,6 +75,14 @@
     jint reasonReclaimed;
 } gExceptionReason;
 
+static struct {
+    jclass clazz;
+    jfieldID mLock;
+    jfieldID mPersistentObject;
+    jmethodID ctor;
+    jmethodID setNativeObjectLocked;
+} gPersistentSurfaceClassInfo;
+
 struct fields_t {
     jfieldID context;
     jmethodID postEventFromNativeID;
@@ -87,6 +95,7 @@
 };
 
 static fields_t gFields;
+static const void *sRefBaseOwner;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -245,11 +254,29 @@
     return mCodec->configure(format, mSurfaceTextureClient, crypto, flags);
 }
 
+status_t JMediaCodec::setSurface(
+        const sp<IGraphicBufferProducer> &bufferProducer) {
+    sp<Surface> client;
+    if (bufferProducer != NULL) {
+        client = new Surface(bufferProducer, true /* controlledByApp */);
+    }
+    status_t err = mCodec->setSurface(client);
+    if (err == OK) {
+        mSurfaceTextureClient = client;
+    }
+    return err;
+}
+
 status_t JMediaCodec::createInputSurface(
         sp<IGraphicBufferProducer>* bufferProducer) {
     return mCodec->createInputSurface(bufferProducer);
 }
 
+status_t JMediaCodec::usePersistentInputSurface(
+        const sp<PersistentSurface> &surface) {
+    return mCodec->usePersistentInputSurface(surface);
+}
+
 status_t JMediaCodec::start() {
     return mCodec->start();
 }
@@ -797,6 +824,10 @@
             jniThrowException(env, "java/lang/IllegalStateException", msg);
             return 0;
 
+        case BAD_VALUE:
+            jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+            return 0;
+
         default:
             if (isCryptoError(err)) {
                 throwCryptoException(env, err, msg);
@@ -869,6 +900,149 @@
     throwExceptionAsNecessary(env, err);
 }
 
+static void android_media_MediaCodec_native_setSurface(
+        JNIEnv *env,
+        jobject thiz,
+        jobject jsurface) {
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    sp<IGraphicBufferProducer> bufferProducer;
+    if (jsurface != NULL) {
+        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
+        if (surface != NULL) {
+            bufferProducer = surface->getIGraphicBufferProducer();
+        } else {
+            jniThrowException(
+                    env,
+                    "java/lang/IllegalArgumentException",
+                    "The surface has been released");
+            return;
+        }
+    }
+
+    status_t err = codec->setSurface(bufferProducer);
+    throwExceptionAsNecessary(env, err);
+}
+
+sp<PersistentSurface> android_media_MediaCodec_getPersistentInputSurface(
+        JNIEnv* env, jobject object) {
+    sp<PersistentSurface> persistentSurface;
+
+    jobject lock = env->GetObjectField(
+            object, gPersistentSurfaceClassInfo.mLock);
+    if (env->MonitorEnter(lock) == JNI_OK) {
+        persistentSurface = reinterpret_cast<PersistentSurface *>(
+                env->GetLongField(object,
+                        gPersistentSurfaceClassInfo.mPersistentObject));
+        env->MonitorExit(lock);
+    }
+    env->DeleteLocalRef(lock);
+
+    return persistentSurface;
+}
+
+static jobject android_media_MediaCodec_createPersistentInputSurface(
+        JNIEnv* env, jclass /* clazz */) {
+    ALOGV("android_media_MediaCodec_createPersistentInputSurface");
+    sp<PersistentSurface> persistentSurface =
+        MediaCodec::CreatePersistentInputSurface();
+
+    if (persistentSurface == NULL) {
+        return NULL;
+    }
+
+    sp<Surface> surface = new Surface(
+            persistentSurface->getBufferProducer(), true);
+    if (surface == NULL) {
+        return NULL;
+    }
+
+    jobject object = env->NewObject(
+            gPersistentSurfaceClassInfo.clazz,
+            gPersistentSurfaceClassInfo.ctor);
+
+    if (object == NULL) {
+        if (env->ExceptionCheck()) {
+            ALOGE("Could not create PersistentSurface.");
+            env->ExceptionClear();
+        }
+        return NULL;
+    }
+
+    jobject lock = env->GetObjectField(
+            object, gPersistentSurfaceClassInfo.mLock);
+    if (env->MonitorEnter(lock) == JNI_OK) {
+        env->CallVoidMethod(
+                object,
+                gPersistentSurfaceClassInfo.setNativeObjectLocked,
+                (jlong)surface.get());
+        env->SetLongField(
+                object,
+                gPersistentSurfaceClassInfo.mPersistentObject,
+                (jlong)persistentSurface.get());
+        env->MonitorExit(lock);
+    } else {
+        env->DeleteLocalRef(object);
+        object = NULL;
+    }
+    env->DeleteLocalRef(lock);
+
+    if (object != NULL) {
+        surface->incStrong(&sRefBaseOwner);
+        persistentSurface->incStrong(&sRefBaseOwner);
+    }
+
+    return object;
+}
+
+static void android_media_MediaCodec_releasePersistentInputSurface(
+        JNIEnv* env, jclass /* clazz */, jobject object) {
+    sp<PersistentSurface> persistentSurface;
+
+    jobject lock = env->GetObjectField(
+            object, gPersistentSurfaceClassInfo.mLock);
+    if (env->MonitorEnter(lock) == JNI_OK) {
+        persistentSurface = reinterpret_cast<PersistentSurface *>(
+            env->GetLongField(
+                    object, gPersistentSurfaceClassInfo.mPersistentObject));
+        env->SetLongField(
+                object,
+                gPersistentSurfaceClassInfo.mPersistentObject,
+                (jlong)0);
+        env->MonitorExit(lock);
+    }
+    env->DeleteLocalRef(lock);
+
+    if (persistentSurface != NULL) {
+        persistentSurface->decStrong(&sRefBaseOwner);
+    }
+    // no need to release surface as it will be released by Surface's jni
+}
+
+static void android_media_MediaCodec_usePersistentInputSurface(
+        JNIEnv* env, jobject thiz, jobject object) {
+    ALOGV("android_media_MediaCodec_usePersistentInputSurface");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+    if (codec == NULL) {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    sp<PersistentSurface> persistentSurface =
+        android_media_MediaCodec_getPersistentInputSurface(env, object);
+
+    status_t err = codec->usePersistentInputSurface(persistentSurface);
+    if (err != NO_ERROR) {
+        throwExceptionAsNecessary(env, err);
+    }
+}
+
 static jobject android_media_MediaCodec_createInputSurface(JNIEnv* env,
         jobject thiz) {
     ALOGV("android_media_MediaCodec_createInputSurface");
@@ -1471,6 +1645,29 @@
     CHECK(field != NULL);
     gExceptionReason.reasonReclaimed =
         env->GetStaticIntField(clazz.get(), field);
+
+    clazz.reset(env->FindClass("android/view/Surface"));
+    CHECK(clazz.get() != NULL);
+
+    field = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+    CHECK(field != NULL);
+    gPersistentSurfaceClassInfo.mLock = field;
+
+    jmethodID method = env->GetMethodID(clazz.get(), "setNativeObjectLocked", "(J)V");
+    CHECK(method != NULL);
+    gPersistentSurfaceClassInfo.setNativeObjectLocked = method;
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$PersistentSurface"));
+    CHECK(clazz.get() != NULL);
+    gPersistentSurfaceClassInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+    method = env->GetMethodID(clazz.get(), "<init>", "()V");
+    CHECK(method != NULL);
+    gPersistentSurfaceClassInfo.ctor = method;
+
+    field = env->GetFieldID(clazz.get(), "mPersistentObject", "J");
+    CHECK(field != NULL);
+    gPersistentSurfaceClassInfo.mPersistentObject = field;
 }
 
 static void android_media_MediaCodec_native_setup(
@@ -1521,6 +1718,17 @@
 
     { "native_reset", "()V", (void *)android_media_MediaCodec_reset },
 
+    { "native_releasePersistentInputSurface",
+      "(Landroid/view/Surface;)V",
+       (void *)android_media_MediaCodec_releasePersistentInputSurface},
+
+    { "native_createPersistentInputSurface",
+      "()Landroid/media/MediaCodec$PersistentSurface;",
+      (void *)android_media_MediaCodec_createPersistentInputSurface },
+
+    { "native_usePersistentInputSurface", "(Landroid/view/Surface;)V",
+      (void *)android_media_MediaCodec_usePersistentInputSurface },
+
     { "native_setCallback",
       "(Landroid/media/MediaCodec$Callback;)V",
       (void *)android_media_MediaCodec_native_setCallback },
@@ -1530,6 +1738,10 @@
       "Landroid/media/MediaCrypto;I)V",
       (void *)android_media_MediaCodec_native_configure },
 
+    { "native_setSurface",
+      "(Landroid/view/Surface;)V",
+      (void *)android_media_MediaCodec_native_setSurface },
+
     { "createInputSurface", "()Landroid/view/Surface;",
       (void *)android_media_MediaCodec_createInputSurface },
 
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 9f2785a..bf61f42 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -33,6 +33,7 @@
 struct ICrypto;
 struct IGraphicBufferProducer;
 struct MediaCodec;
+struct PersistentSurface;
 class Surface;
 
 struct JMediaCodec : public AHandler {
@@ -53,7 +54,11 @@
             const sp<ICrypto> &crypto,
             int flags);
 
+    status_t setSurface(
+            const sp<IGraphicBufferProducer> &surface);
+
     status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer);
+    status_t usePersistentInputSurface(const sp<PersistentSurface> &surface);
 
     status_t start();
     status_t stop();
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 02297fc..0044bed 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -29,6 +29,7 @@
 #include <camera/ICameraService.h>
 #include <camera/Camera.h>
 #include <media/mediarecorder.h>
+#include <media/stagefright/PersistentSurface.h>
 #include <utils/threads.h>
 
 #include <ScopedUtfChars.h>
@@ -48,6 +49,8 @@
 
 // helper function to extract a native Camera object from a Camera Java object
 extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context);
+extern sp<PersistentSurface>
+android_media_MediaCodec_getPersistentInputSurface(JNIEnv* env, jobject object);
 
 struct fields_t {
     jfieldID    context;
@@ -115,6 +118,12 @@
     return android_view_Surface_getSurface(env, clazz);
 }
 
+static sp<PersistentSurface> get_persistentSurface(JNIEnv* env, jobject object)
+{
+    ALOGV("get_persistentSurface");
+    return android_media_MediaCodec_getPersistentInputSurface(env, object);
+}
+
 // Returns true if it throws an exception.
 static bool process_media_recorder_call(JNIEnv *env, status_t opStatus, const char* exception, const char* message)
 {
@@ -487,6 +496,18 @@
     android_media_MediaRecorder_release(env, thiz);
 }
 
+void android_media_MediaRecorder_usePersistentSurface(
+        JNIEnv* env, jobject thiz, jobject object) {
+    ALOGV("android_media_MediaRecorder_usePersistentSurface");
+
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+
+    sp<PersistentSurface> persistentSurface = get_persistentSurface(env, object);
+
+    process_media_recorder_call(env, mr->usePersistentSurface(persistentSurface),
+            "java/lang/IllegalArgumentException", "native_usePersistentSurface failed.");
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -513,6 +534,7 @@
     {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
                                                                 (void *)android_media_MediaRecorder_native_setup},
     {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
+    {"native_usePersistentSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_usePersistentSurface },
 };
 
 // This function only registers the native methods, and is called from
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
index e730329..563b0f3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediarecorder/MediaRecorderTest.java
@@ -27,6 +27,7 @@
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.hardware.Camera;
+import android.media.MediaCodec;
 import android.media.MediaMetadataRetriever;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
@@ -225,10 +226,12 @@
 
     private boolean recordVideoFromSurface(
             int frameRate, int captureRate, int width, int height,
-            int videoFormat, int outFormat, String outFile, boolean videoOnly) {
+            int videoFormat, int outFormat, String outFile, boolean videoOnly,
+            Surface persistentSurface) {
         Log.v(TAG,"recordVideoFromSurface");
         MediaRecorder recorder = new MediaRecorder();
         int sleepTime = 33; // normal capture at 33ms / frame
+        Surface surface = null;
         try {
             if (!videoOnly) {
                 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
@@ -246,8 +249,15 @@
             if (!videoOnly) {
                 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
             }
+            if (persistentSurface != null) {
+                Log.v(TAG, "using persistent surface");
+                surface = persistentSurface;
+                recorder.usePersistentSurface(surface);
+            }
             recorder.prepare();
-            Surface surface = recorder.getSurface();
+            if (persistentSurface == null) {
+                surface = recorder.getSurface();
+            }
 
             Paint paint = new Paint();
             paint.setTextSize(16);
@@ -283,11 +293,15 @@
 
             Log.v(TAG, "stop");
             recorder.stop();
-            recorder.release();
         } catch (Exception e) {
-            Log.v("record video failed ", e.toString());
-            recorder.release();
+            Log.v(TAG, "record video failed: " + e.toString());
             return false;
+        } finally {
+            recorder.release();
+            // release surface if not using persistent surface
+            if (persistentSurface == null && surface != null) {
+                surface.release();
+            }
         }
         return true;
     }
@@ -550,7 +564,7 @@
 
                 success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
                         MediaRecorder.OutputFormat.THREE_GPP, filename,
-                        k == 0 ? true : false /* videoOnly */);
+                        k == 0 ? true : false /* videoOnly */, null);
                 if (success) {
                     success = validateVideo(filename, 352, 288);
                 }
@@ -564,6 +578,40 @@
         assertTrue("testSurfaceRecording", noOfFailure == 0);
     }
 
+    public void testPersistentSurfaceRecording() {
+        boolean success = false;
+        int noOfFailure = 0;
+        Surface surface = null;
+        try {
+            int codec = MediaRecorder.VideoEncoder.H264;
+            int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
+            surface = MediaCodec.createPersistentInputSurface();
+            for (int k = 0; k < 2; k++) {
+                String filename = "/sdcard/surface_persistent" + k + ".3gp";
+
+                Log.v(TAG, "test persistent surface - round " + k);
+                success = recordVideoFromSurface(frameRate, 0, 352, 288, codec,
+                        MediaRecorder.OutputFormat.THREE_GPP, filename,
+                        true /* videoOnly */, surface);
+                if (success) {
+                    success = validateVideo(filename, 352, 288);
+                }
+                if (!success) {
+                    noOfFailure++;
+                }
+            }
+        } catch (Exception e) {
+            Log.v(TAG, e.toString());
+        } finally {
+            if (surface != null) {
+                Log.v(TAG, "releasing persistent surface");
+                surface.release();
+                surface = null;
+            }
+        }
+        assertTrue("testPersistentSurfaceRecording", noOfFailure == 0);
+    }
+
     // Test recording from surface source with/without audio
     public void testSurfaceRecordingTimeLapse() {
         boolean success = false;
@@ -583,7 +631,7 @@
                 success = recordVideoFromSurface(
                         frameRate, captureRate, 352, 288, codec,
                         MediaRecorder.OutputFormat.THREE_GPP,
-                        filename, false /* videoOnly */);
+                        filename, false /* videoOnly */, null);
                 if (success) {
                     success = validateVideo(filename, 352, 288);
                     if (success) {