Fix remote GraphicBuffer allocation in SurfaceFlinger.

This change fixes a horrible hack that I did to allow application
processes to create GraphicBuffer objects by making a binder call to
SurfaceFlinger.  This change introduces a new binder interface
specifically for doing this, and does it in such a way that
SurfaceFlinger will maintain a reference to the buffers until the app is
done with them.

Change-Id: Icb240397c6c206d7f69124c1497a829f051cb49b
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 1389ed6..447de76 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -29,6 +29,7 @@
 
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
+#include <surfaceflinger/IGraphicBufferAlloc.h>
 
 #include <utils/Log.h>
 
@@ -83,6 +84,8 @@
         mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
         mSlots[i].mOwnedByClient = false;
     }
+    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+    mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
 }
 
 SurfaceTexture::~SurfaceTexture() {
@@ -110,9 +113,8 @@
         return 0;
     }
     usage |= GraphicBuffer::USAGE_HW_TEXTURE;
-    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
-    sp<GraphicBuffer> graphicBuffer(composer->createGraphicBuffer(w, h,
-            format, usage));
+    sp<GraphicBuffer> graphicBuffer(
+            mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
     if (graphicBuffer == 0) {
         LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
     } else {
@@ -122,6 +124,7 @@
             mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
             mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
         }
+        mAllocdBuffers.add(graphicBuffer);
     }
     return graphicBuffer;
 }
@@ -204,27 +207,28 @@
     // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
     // so this check will fail until a buffer gets queued.
     if (mCurrentTexture != mLastQueued) {
-        // Update the SurfaceTexture state.
-        mCurrentTexture = mLastQueued;
-        mCurrentCrop = mLastQueuedCrop;
-        mCurrentTransform = mLastQueuedTransform;
-
         // Update the GL texture object.
-        EGLImageKHR image = mSlots[mCurrentTexture].mEglImage;
+        EGLImageKHR image = mSlots[mLastQueued].mEglImage;
         if (image == EGL_NO_IMAGE_KHR) {
             EGLDisplay dpy = eglGetCurrentDisplay();
-            sp<GraphicBuffer> graphicBuffer = mSlots[mCurrentTexture].mGraphicBuffer;
+            sp<GraphicBuffer> graphicBuffer = mSlots[mLastQueued].mGraphicBuffer;
             image = createImage(dpy, graphicBuffer);
-            mSlots[mCurrentTexture].mEglImage = image;
-            mSlots[mCurrentTexture].mEglDisplay = dpy;
+            mSlots[mLastQueued].mEglImage = image;
+            mSlots[mLastQueued].mEglDisplay = dpy;
         }
         glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
         GLint error = glGetError();
         if (error != GL_NO_ERROR) {
             LOGE("error binding external texture image %p (slot %d): %#04x",
-                    image, mCurrentTexture, error);
+                    image, mLastQueued, error);
             return -EINVAL;
         }
+
+        // Update the SurfaceTexture state.
+        mCurrentTexture = mLastQueued;
+        mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
+        mCurrentCrop = mLastQueuedCrop;
+        mCurrentTransform = mLastQueuedTransform;
     }
     return OK;
 }
@@ -282,6 +286,19 @@
             mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
         }
     }
+
+    int exceptBuf = -1;
+    for (size_t i = 0; i < mAllocdBuffers.size(); i++) {
+        if (mAllocdBuffers[i] == mCurrentTextureBuf) {
+            exceptBuf = i;
+            break;
+        }
+    }
+    mAllocdBuffers.clear();
+    if (exceptBuf >= 0) {
+        mAllocdBuffers.add(mCurrentTextureBuf);
+    }
+    mGraphicBufferAlloc->freeAllGraphicBuffersExcept(exceptBuf);
 }
 
 EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk
index ce3c71a..4a0faf0 100644
--- a/libs/surfaceflinger_client/Android.mk
+++ b/libs/surfaceflinger_client/Android.mk
@@ -5,6 +5,7 @@
 	ISurfaceComposer.cpp \
 	ISurface.cpp \
 	ISurfaceComposerClient.cpp \
+	IGraphicBufferAlloc.cpp \
 	LayerState.cpp \
 	SharedBufferStack.cpp \
 	Surface.cpp \
diff --git a/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp b/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp
new file mode 100644
index 0000000..e05da72
--- /dev/null
+++ b/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// tag as surfaceflinger
+#define LOG_TAG "SurfaceFlinger"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <surfaceflinger/IGraphicBufferAlloc.h>
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+enum {
+    CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+    FREE_ALL_GRAPHIC_BUFFERS_EXCEPT,
+};
+
+class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc>
+{
+public:
+    BpGraphicBufferAlloc(const sp<IBinder>& impl)
+        : BpInterface<IGraphicBufferAlloc>(impl)
+    {
+    }
+
+    virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
+            PixelFormat format, uint32_t usage) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                IGraphicBufferAlloc::getInterfaceDescriptor());
+        data.writeInt32(w);
+        data.writeInt32(h);
+        data.writeInt32(format);
+        data.writeInt32(usage);
+        remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply);
+        sp<GraphicBuffer> graphicBuffer;
+        bool nonNull = (bool)reply.readInt32();
+        if (nonNull) {
+            graphicBuffer = new GraphicBuffer();
+            reply.read(*graphicBuffer);
+        }
+        return graphicBuffer;
+    }
+
+    virtual void freeAllGraphicBuffersExcept(int bufIdx) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                IGraphicBufferAlloc::getInterfaceDescriptor());
+        data.writeInt32(bufIdx);
+        remote()->transact(FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, data, &reply);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc");
+
+// ----------------------------------------------------------------------
+
+status_t BnGraphicBufferAlloc::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    // codes that don't require permission check
+
+    switch(code) {
+        case CREATE_GRAPHIC_BUFFER: {
+            CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
+            uint32_t w = data.readInt32();
+            uint32_t h = data.readInt32();
+            PixelFormat format = data.readInt32();
+            uint32_t usage = data.readInt32();
+            sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage));
+            reply->writeInt32(result != 0);
+            if (result != 0) {
+                reply->write(*result);
+            }
+            return NO_ERROR;
+        } break;
+        case FREE_ALL_GRAPHIC_BUFFERS_EXCEPT: {
+            CHECK_INTERFACE(IGraphicBufferAlloc, data, reply);
+            int bufIdx = data.readInt32();
+            freeAllGraphicBuffersExcept(bufIdx);
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}; // namespace android
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index a42b49d..2216824 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -26,7 +26,6 @@
 #include <binder/IServiceManager.h>
 
 #include <ui/DisplayInfo.h>
-#include <ui/GraphicBuffer.h>
 
 #include <surfaceflinger/ISurfaceComposer.h>
 
@@ -65,6 +64,15 @@
         return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
     }
 
+    virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc()
+    {
+        uint32_t n;
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply);
+        return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder());
+    }
+
     virtual sp<IMemoryHeap> getCblk() const
     {
         Parcel data, reply;
@@ -170,25 +178,6 @@
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY);
     }
-
-    virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
-            PixelFormat format, uint32_t usage) const {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        data.writeInt32(w);
-        data.writeInt32(h);
-        data.writeInt32(format);
-        data.writeInt32(usage);
-        remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER, data,
-                &reply);
-        sp<GraphicBuffer> graphicBuffer;
-        bool nonNull = (bool)reply.readInt32();
-        if (nonNull) {
-            graphicBuffer = new GraphicBuffer();
-            reply.read(*graphicBuffer);
-        }
-        return graphicBuffer;
-    }
 };
 
 IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer");
@@ -209,6 +198,11 @@
             sp<IBinder> b = createClientConnection()->asBinder();
             reply->writeStrongBinder(b);
         } break;
+        case CREATE_GRAPHIC_BUFFER_ALLOC: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> b = createGraphicBufferAlloc()->asBinder();
+            reply->writeStrongBinder(b);
+        } break;
         case OPEN_GLOBAL_TRANSACTION: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             openGlobalTransaction();
@@ -267,18 +261,6 @@
             reply->writeInt32(f);
             reply->writeInt32(res);
         } break;
-        case CREATE_GRAPHIC_BUFFER: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            uint32_t w = data.readInt32();
-            uint32_t h = data.readInt32();
-            PixelFormat format = data.readInt32();
-            uint32_t usage = data.readInt32();
-            sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage));
-            reply->writeInt32(result != 0);
-            if (result != 0) {
-                reply->write(*result);
-            }
-        } break;
         case TURN_ELECTRON_BEAM_OFF: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             int32_t mode = data.readInt32();