Add support for creating a Surface from a a SurfaceTexture.

The Surface is already using SurfaceTexture internally and it is parcelable. This
is intended to replace and phase out ParcelSurfaceTexture in favor of creating a
new Surface.java object from an existing SurfaceTexture.

Change-Id: I8e2dd86614523da6abed6403e1d705a68fa19fdf
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index c913bb3..836867b 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -161,6 +161,9 @@
      */
     public static final int FLAGS_ORIENTATION_ANIMATION_DISABLE = 0x000000001;
 
+    // The mSurfaceControl will only be present for Surfaces used by the window
+    // server or system processes. When this class is parceled we defer to the
+    // mSurfaceControl to do the parceling. Otherwise we parcel the mNativeSurface.
     @SuppressWarnings("unused")
     private int mSurfaceControl;
     @SuppressWarnings("unused")
@@ -202,6 +205,19 @@
     native private static void nativeClassInit();
     static { nativeClassInit(); }
 
+    /**
+     * Create Surface from a SurfaceTexture.
+     *
+     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this Surface.
+     * @hide
+     */
+    public Surface(SurfaceTexture surfaceTexture) {
+        if (DEBUG_RELEASE) {
+            mCreationStack = new Exception();
+        }
+        mCanvas = new CompatibleCanvas();
+        initFromSurfaceTexture(surfaceTexture);
+    }
     
     /**
      * create a surface
@@ -505,5 +521,7 @@
 
     private native void init(Parcel source);
 
+    private native void initFromSurfaceTexture(SurfaceTexture surfaceTexture);
+
     private native int getIdentity();
 }
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 0dc9293..4c1ca31 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -22,6 +22,7 @@
 #include "android/graphics/GraphicsJNI.h"
 
 #include <binder/IMemory.h>
+#include <gui/SurfaceTexture.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <surfaceflinger/Surface.h>
 #include <ui/Region.h>
@@ -38,6 +39,7 @@
 #include "JNIHelp.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_graphics_SurfaceTexture.h>
 #include <utils/misc.h>
 
 
@@ -244,6 +246,19 @@
     setSurfaceControl(env, clazz, surface);
 }
 
+static void Surface_initFromSurfaceTexture(
+        JNIEnv* env, jobject clazz, jobject jst)
+{
+    sp<ISurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, jst));
+    sp<Surface> surface(new Surface(st));
+    if (surface == NULL) {
+        jniThrowException(env, OutOfResourcesException, NULL);
+        return;
+    }
+    setSurfaceControl(env, clazz, NULL);
+    setSurface(env, clazz, surface);
+}
+
 static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel)
 {
     Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel);
@@ -761,10 +776,26 @@
         return;
     }
 
+    // The Java instance may have a SurfaceControl (in the case of the
+    // WindowManager or a system app). In that case, we defer to the
+    // SurfaceControl to send its ISurface. Otherwise, if the Surface is
+    // available we let it parcel itself. Finally, if the Surface is also
+    // NULL we fall back to using the SurfaceControl path which sends an
+    // empty surface; this matches legacy behavior.
     const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
-    SurfaceControl::writeSurfaceToParcel(control, parcel);
+    if (control != NULL) {
+        SurfaceControl::writeSurfaceToParcel(control, parcel);
+    } else {
+        sp<Surface> surface(Surface_getSurface(env, clazz));
+        if (surface != NULL) {
+            Surface::writeToParcel(surface, parcel);
+        } else {
+            SurfaceControl::writeSurfaceToParcel(NULL, parcel);
+        }
+    }
     if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
-        setSurfaceControl(env, clazz, 0);
+        setSurfaceControl(env, clazz, NULL);
+        setSurface(env, clazz, NULL);
     }
 }
 
@@ -784,6 +815,7 @@
     {"nativeClassInit",     "()V",  (void*)nativeClassInit },
     {"init",                "(Landroid/view/SurfaceSession;ILjava/lang/String;IIIII)V",  (void*)Surface_init },
     {"init",                "(Landroid/os/Parcel;)V",  (void*)Surface_initParcel },
+    {"initFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V", (void*)Surface_initFromSurfaceTexture },
     {"getIdentity",         "()I",  (void*)Surface_getIdentity },
     {"destroy",             "()V",  (void*)Surface_destroy },
     {"release",             "()V",  (void*)Surface_release },
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 9c352ad..0460bbd 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -122,7 +122,10 @@
         uint32_t    reserved[2];
     };
 
+    explicit Surface(const sp<ISurfaceTexture>& st);
+
     static status_t writeToParcel(const sp<Surface>& control, Parcel* parcel);
+
     static sp<Surface> readFromParcel(const Parcel& data);
     static bool isValid(const sp<Surface>& surface) {
         return (surface != 0) && surface->isValid();
@@ -147,14 +150,14 @@
     Surface& operator = (Surface& rhs);
     Surface(const Surface& rhs);
 
-    Surface(const sp<SurfaceControl>& control);
+    explicit Surface(const sp<SurfaceControl>& control);
     Surface(const Parcel& data, const sp<IBinder>& ref);
     ~Surface();
 
     /*
      *  private stuff...
      */
-    void init();
+    void init(const sp<ISurfaceTexture>& surfaceTexture);
 
     static void cleanCachedSurfacesLocked();
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c4f9e53..ccf98e5 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -184,6 +184,7 @@
         identity = control->mIdentity;
     }
     parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+    parcel->writeStrongBinder(NULL);  // NULL ISurfaceTexture in this case.
     parcel->writeInt32(identity);
     return NO_ERROR;
 }
@@ -192,7 +193,8 @@
 {
     Mutex::Autolock _l(mLock);
     if (mSurfaceData == 0) {
-        mSurfaceData = new Surface(const_cast<SurfaceControl*>(this));
+        sp<SurfaceControl> surface_control(const_cast<SurfaceControl*>(this));
+        mSurfaceData = new Surface(surface_control);
     }
     return mSurfaceData;
 }
@@ -208,31 +210,58 @@
       mSurface(surface->mSurface),
       mIdentity(surface->mIdentity)
 {
-    init();
+    sp<ISurfaceTexture> st;
+    if (mSurface != NULL) {
+        st = mSurface->getSurfaceTexture();
+    }
+    init(st);
 }
 
 Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
     : SurfaceTextureClient()
 {
-    mSurface    = interface_cast<ISurface>(ref);
+    mSurface = interface_cast<ISurface>(ref);
+    sp<IBinder> st_binder(parcel.readStrongBinder());
+    sp<ISurfaceTexture> st;
+    if (st_binder != NULL) {
+        st = interface_cast<ISurfaceTexture>(st_binder);
+    } else if (mSurface != NULL) {
+        st = mSurface->getSurfaceTexture();
+    }
+
     mIdentity   = parcel.readInt32();
-    init();
+    init(st);
+}
+
+Surface::Surface(const sp<ISurfaceTexture>& st)
+    : SurfaceTextureClient(),
+      mSurface(NULL),
+      mIdentity(0)
+{
+    init(st);
 }
 
 status_t Surface::writeToParcel(
         const sp<Surface>& surface, Parcel* parcel)
 {
     sp<ISurface> sur;
+    sp<ISurfaceTexture> st;
     uint32_t identity = 0;
     if (Surface::isValid(surface)) {
         sur      = surface->mSurface;
+        st       = surface->getISurfaceTexture();
         identity = surface->mIdentity;
-    } else if (surface != 0 && surface->mSurface != 0) {
-        LOGW("Parceling invalid surface with non-NULL ISurface as NULL: "
-             "mSurface = %p, mIdentity = %d",
-             surface->mSurface.get(), surface->mIdentity);
+    } else if (surface != 0 &&
+            (surface->mSurface != NULL ||
+             surface->getISurfaceTexture() != NULL)) {
+        LOGE("Parceling invalid surface with non-NULL ISurface/ISurfaceTexture as NULL: "
+             "mSurface = %p, surfaceTexture = %p, mIdentity = %d, ",
+             surface->mSurface.get(), surface->getISurfaceTexture().get(),
+             surface->mIdentity);
     }
-    parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+
+    parcel->writeStrongBinder(sur != NULL ? sur->asBinder() : NULL);
+    parcel->writeStrongBinder(st != NULL ? st->asBinder() : NULL);
     parcel->writeInt32(identity);
     return NO_ERROR;
 
@@ -249,8 +278,8 @@
        surface = new Surface(data, binder);
        sCachedSurfaces.add(binder, surface);
     }
-    if (surface->mSurface == 0) {
-      surface = 0;
+    if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
+        surface = 0;
     }
     cleanCachedSurfacesLocked();
     return surface;
@@ -267,10 +296,9 @@
     }
 }
 
-void Surface::init()
+void Surface::init(const sp<ISurfaceTexture>& surfaceTexture)
 {
-    if (mSurface != NULL) {
-        sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture());
+    if (mSurface != NULL || surfaceTexture != NULL) {
         LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface");
         if (surfaceTexture != NULL) {
             setISurfaceTexture(surfaceTexture);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 482b437..95671bc 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -611,7 +611,7 @@
      * needed.  Not calling this method when playing back a video will
      * result in only the audio track being played.
      *
-     * Either a surface or surface texture must be set if a display or video sink
+     * Either a surface holder or surface must be set if a display or video sink
      * is needed.  Not calling this method or {@link #setTexture(SurfaceTexture)}
      * when playing back a video will result in only the audio track being played.
      *
@@ -630,6 +630,27 @@
     }
 
     /**
+     * Sets the {@link Surface} to be used as the sink for the video portion of
+     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but does not
+     * support {@link #setScreenOnWhilePlaying(boolean)} or {@link #updateSurfaceScreenOn()}.
+     * Setting a Surface will un-set any Surface or SurfaceHolder that was previously set.
+     *
+     * @param surface The {@link Surface} to be used for the video portion of the media.
+     *
+     * @hide Pending review by API council.
+     */
+    public void setSurface(Surface surface) {
+        if (mScreenOnWhilePlaying && surface != null && mSurface != null) {
+            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
+        }
+        mSurfaceHolder = null;
+        mSurface = surface;
+        mParcelSurfaceTexture = null;  // TODO(tedbo): Remove.
+        _setVideoSurfaceOrSurfaceTexture();
+        updateSurfaceScreenOn();
+    }
+
+    /**
      * Sets the {@link SurfaceTexture} to be used as the sink for the
      * video portion of the media. Either a surface or surface texture
      * must be set if a video sink is needed.  The same surface texture
@@ -665,7 +686,7 @@
      * @param pst The {@link ParcelSurfaceTexture} to be used as the sink for
      * the video portion of the media.
      *
-     * @hide Pending review by API council.
+     * @hide Pending removal when there are no more callers.
      */
     public void setParcelSurfaceTexture(ParcelSurfaceTexture pst) {
         if (mScreenOnWhilePlaying && pst != null && mParcelSurfaceTexture == null) {
@@ -1000,8 +1021,8 @@
      */
     public void setScreenOnWhilePlaying(boolean screenOn) {
         if (mScreenOnWhilePlaying != screenOn) {
-            if (screenOn && mParcelSurfaceTexture != null) {
-                Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture");
+            if (screenOn && mSurfaceHolder == null) {
+                Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
             }
             mScreenOnWhilePlaying = screenOn;
             updateSurfaceScreenOn();