add support for [1974164] Be able to take a screen shot on the device

screenshots are taken using ISurfaceComposer::captureScreen() which returns
the size of the screenshot and an IMemoryHeap containing the data.
screenshots have limitations:
- they will always fail if a secure window is up on screen
- require GL_OES_framebuffer_object extension
- in some situation, video planes won't been captured

Change-Id: I741c68a2d2984fb139039301c3349e6780e2cd58
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc241d4..2b06f6f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -75,6 +75,7 @@
         mBootTime(systemTime()),
         mHardwareTest("android.permission.HARDWARE_TEST"),
         mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
+        mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
         mDeferReleaseConsole(false),
@@ -1465,8 +1466,23 @@
                         "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
                 return PERMISSION_DENIED;
             }
+            break;
+        }
+        case CAPTURE_SCREEN:
+        {
+            // codes that require permission check
+            IPCThreadState* ipc = IPCThreadState::self();
+            const int pid = ipc->getCallingPid();
+            const int uid = ipc->getCallingUid();
+            if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
+                LOGE("Permission Denial: "
+                        "can't read framebuffer pid=%d, uid=%d", pid, uid);
+                return PERMISSION_DENIED;
+            }
+            break;
         }
     }
+
     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
         CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1530,6 +1546,139 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::captureScreen(DisplayID dpy,
+        sp<IMemoryHeap>* heap,
+        uint32_t* width, uint32_t* height, PixelFormat* format)
+{
+    // only one display supported for now
+    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+        return BAD_VALUE;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    class MessageCaptureScreen : public MessageBase {
+        SurfaceFlinger* flinger;
+        DisplayID dpy;
+        sp<IMemoryHeap>* heap;
+        uint32_t* w;
+        uint32_t* h;
+        PixelFormat* f;
+        status_t result;
+    public:
+        MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
+                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+            : flinger(flinger), dpy(dpy),
+              heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+        {
+        }
+        status_t getResult() const {
+            return result;
+        }
+        virtual bool handler() {
+            Mutex::Autolock _l(flinger->mStateLock);
+
+            // if we have secure windows, never allow the screen capture
+            if (flinger->mSecureFrameBuffer)
+                return true;
+
+            // make sure to clear all GL error flags
+            while ( glGetError() != GL_NO_ERROR ) ;
+
+            // get screen geometry
+            const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
+            const uint32_t sw = hw.getWidth();
+            const uint32_t sh = hw.getHeight();
+            const Region screenBounds(hw.bounds());
+            const size_t size = sw * sh * 4;
+
+            // create a FBO
+            GLuint name, tname;
+            glGenRenderbuffersOES(1, &tname);
+            glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+            glGenFramebuffersOES(1, &name);
+            glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+                    GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+            GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+            if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+                // invert everything, b/c glReadPixel() below will invert the FB
+                glMatrixMode(GL_PROJECTION);
+                glPushMatrix();
+                glLoadIdentity();
+                glOrthof(0, sw, 0, sh, 0, 1);
+                glMatrixMode(GL_MODELVIEW);
+
+                // redraw the screen entirely...
+                glClearColor(0,0,0,1);
+                glClear(GL_COLOR_BUFFER_BIT);
+                const Vector< sp<LayerBase> >& layers(
+                        flinger->mVisibleLayersSortedByZ);
+                const size_t count = layers.size();
+                for (size_t i=0 ; i<count ; ++i) {
+                    const sp<LayerBase>& layer(layers[i]);
+                    if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
+                        // we cannot render LayerBuffer because it doens't
+                        // use OpenGL, and won't show-up in the FBO.
+                        continue;
+                    }
+                    layer->draw(screenBounds);
+                }
+
+                glMatrixMode(GL_PROJECTION);
+                glPopMatrix();
+                glMatrixMode(GL_MODELVIEW);
+
+                // check for errors and return screen capture
+                if (glGetError() != GL_NO_ERROR) {
+                    // error while rendering
+                    result = INVALID_OPERATION;
+                } else {
+                    // allocate shared memory large enough to hold the
+                    // screen capture
+                    sp<MemoryHeapBase> base(
+                            new MemoryHeapBase(size, 0, "screen-capture") );
+                    void* const ptr = base->getBase();
+                    if (ptr) {
+                        // capture the screen with glReadPixels()
+                        glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+                        if (glGetError() == GL_NO_ERROR) {
+                            *heap = base;
+                            *w = sw;
+                            *h = sh;
+                            *f = PIXEL_FORMAT_RGBA_8888;
+                            result = NO_ERROR;
+                        }
+                    } else {
+                        result = NO_MEMORY;
+                    }
+                }
+            } else {
+                result = BAD_VALUE;
+            }
+
+            // release FBO resources
+            glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+            glDeleteRenderbuffersOES(1, &tname);
+            glDeleteFramebuffersOES(1, &name);
+            return true;
+        }
+    };
+
+    sp<MessageBase> msg = new MessageCaptureScreen(this,
+            dpy, heap, width, height, format);
+    status_t res = postMessageSync(msg);
+    if (res == NO_ERROR) {
+        res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
+    }
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+
 sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
 {
     sp<Layer> result;