Merge "Fix track duration calculation if the start timestamp is non-zero" into gingerbread
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index dd44aa5..76307b2 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -110,6 +110,13 @@
      */
     virtual void bootFinished() = 0;
 
+    /* Capture the specified screen. requires READ_FRAME_BUFFER permission
+     * This function will fail if there is a secure window on screen.
+     */
+    virtual status_t captureScreen(DisplayID dpy,
+            sp<IMemoryHeap>* heap,
+            uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+
     /* Signal surfaceflinger that there might be some work to do
      * This is an ASYNCHRONOUS call.
      */
@@ -133,7 +140,8 @@
         SET_ORIENTATION,
         FREEZE_DISPLAY,
         UNFREEZE_DISPLAY,
-        SIGNAL
+        SIGNAL,
+        CAPTURE_SCREEN
     };
 
     virtual status_t    onTransact( uint32_t code,
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 97d31f4..9668bde 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -24,8 +24,9 @@
 #ifndef __LIBS_ZIPFILERO_H
 #define __LIBS_ZIPFILERO_H
 
-#include "Errors.h"
-#include "FileMap.h"
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -211,6 +212,9 @@
     /* open Zip archive */
     int         mFd;
 
+    /* Lock for handling the file descriptor (seeks, etc) */
+    mutable Mutex mFdLock;
+
     /* zip file name */
     char*       mFileName;
 
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a3e117f..13c58f0 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -517,12 +517,26 @@
     }
     
     if ((flags & TF_ONE_WAY) == 0) {
+        #if 0
+        if (code == 4) { // relayout
+            LOGI(">>>>>> CALLING transaction 4");
+        } else {
+            LOGI(">>>>>> CALLING transaction %d", code);
+        }
+        #endif
         if (reply) {
             err = waitForResponse(reply);
         } else {
             Parcel fakeReply;
             err = waitForResponse(&fakeReply);
         }
+        #if 0
+        if (code == 4) { // relayout
+            LOGI("<<<<<< RETURNING transaction 4");
+        } else {
+            LOGI("<<<<<< RETURNING transaction %d", code);
+        }
+        #endif
         
         IF_LOG_TRANSACTIONS() {
             TextOutput::Bundle _b(alog);
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 5c111f6..040060e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -124,6 +124,21 @@
         remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
     }
 
+    virtual status_t captureScreen(DisplayID dpy,
+            sp<IMemoryHeap>* heap,
+            uint32_t* width, uint32_t* height, PixelFormat* format)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeInt32(dpy);
+        remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+        *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
+        *width = reply.readInt32();
+        *height = reply.readInt32();
+        *format = reply.readInt32();
+        return reply.readInt32();
+    }
+
     virtual void signal() const
     {
         Parcel data, reply;
@@ -190,6 +205,19 @@
             sp<IBinder> b = getCblk()->asBinder();
             reply->writeStrongBinder(b);
         } break;
+        case CAPTURE_SCREEN: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            DisplayID dpy = data.readInt32();
+            sp<IMemoryHeap> heap;
+            uint32_t w, h;
+            PixelFormat f;
+            status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+            reply->writeStrongBinder(heap->asBinder());
+            reply->writeInt32(w);
+            reply->writeInt32(h);
+            reply->writeInt32(f);
+            reply->writeInt32(res);
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 2d53136..9fcae72 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -22,6 +22,7 @@
 #include <utils/ZipFileRO.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
+#include <utils/threads.h>
 
 #include <zlib.h>
 
@@ -195,7 +196,7 @@
             free(scanBuf);
             return false;
         } else if (header != kLFHSignature) {
-            LOGV("Not a Zip archive (found 0x%08x)\n", val);
+            LOGV("Not a Zip archive (found 0x%08x)\n", header);
             free(scanBuf);
             return false;
         }
@@ -496,15 +497,21 @@
         }
 
         unsigned char lfhBuf[kLFHLen];
-        if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
-            LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
-            return false;
-        }
-        ssize_t actual =
-            TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
-        if (actual != sizeof(lfhBuf)) {
-            LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
-            return false;
+
+        {
+            AutoMutex _l(mFdLock);
+
+            if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+                LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+                return false;
+            }
+
+            ssize_t actual =
+                TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+            if (actual != sizeof(lfhBuf)) {
+                LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+                return false;
+            }
         }
 
         if (get4LE(lfhBuf) != kLFHSignature) {
@@ -636,7 +643,7 @@
         memcpy(buffer, ptr, uncompLen);
     } else {
         if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     if (compLen > kSequentialMin)
@@ -644,6 +651,8 @@
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
@@ -667,7 +676,7 @@
 
     getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
 
-    const FileMap* file = createEntryFileMap(entry);
+    FileMap* file = createEntryFileMap(entry);
     if (file == NULL) {
         goto bail;
     }
@@ -678,21 +687,23 @@
         ssize_t actual = write(fd, ptr, uncompLen);
         if (actual < 0) {
             LOGE("Write failed: %s\n", strerror(errno));
-            goto bail;
+            goto unmap;
         } else if ((size_t) actual != uncompLen) {
             LOGE("Partial write during uncompress (%zd of %zd)\n",
                 (size_t)actual, (size_t)uncompLen);
-            goto bail;
+            goto unmap;
         } else {
             LOGI("+++ successful write\n");
         }
     } else {
         if (!inflateBuffer(fd, ptr, uncompLen, compLen))
-            goto bail;
+            goto unmap;
     }
 
     result = true;
 
+unmap:
+    file->release();
 bail:
     return result;
 }
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
index 850866a..493122d 100644
--- a/services/surfaceflinger/GLExtensions.cpp
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -92,6 +92,10 @@
         // hack for Adreno 200
         mHaveTextureExternal = true;
     }
+
+    if (hasExtension("GL_OES_framebuffer_object")) {
+        mHaveFramebufferObject = true;
+    }
 }
 
 bool GLExtensions::hasExtension(char const* extension) const
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
index bbb284e..c86c66a 100644
--- a/services/surfaceflinger/GLExtensions.h
+++ b/services/surfaceflinger/GLExtensions.h
@@ -39,6 +39,7 @@
     bool mHaveTextureExternal   : 1;
     bool mHaveNpot              : 1;
     bool mHaveDirectTexture     : 1;
+    bool mHaveFramebufferObject : 1;
 
     String8 mVendor;
     String8 mRenderer;
@@ -66,6 +67,10 @@
         return mHaveDirectTexture;
     }
 
+    inline bool haveFramebufferObject() const {
+        return mHaveFramebufferObject;
+    }
+
     void initWithGLStrings(
             GLubyte const* vendor,
             GLubyte const* renderer,
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;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8ecfc01..f09fdbc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -193,6 +193,11 @@
     virtual status_t                    unfreezeDisplay(DisplayID dpy, uint32_t flags);
     virtual int                         setOrientation(DisplayID dpy, int orientation, uint32_t flags);
     virtual void                        signal() const;
+    virtual status_t                    captureScreen(DisplayID dpy,
+                                                      sp<IMemoryHeap>* heap,
+                                                      uint32_t* width,
+                                                      uint32_t* height,
+                                                      PixelFormat* format);
 
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
@@ -361,6 +366,7 @@
                 nsecs_t                     mBootTime;
                 Permission                  mHardwareTest;
                 Permission                  mAccessSurfaceFlinger;
+                Permission                  mReadFramebuffer;
                 Permission                  mDump;
                 
                 // Can only accessed from the main thread, these members
diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk
new file mode 100644
index 0000000..1cfb471
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	screencap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	libbinder \
+	libskia \
+    libui \
+    libsurfaceflinger_client
+
+LOCAL_MODULE:= test-screencap
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES += \
+	external/skia/include/core \
+	external/skia/include/effects \
+	external/skia/include/images \
+	external/skia/src/ports \
+	external/skia/include/utils
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
new file mode 100644
index 0000000..9e893f4
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <binder/IMemory.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkImageEncoder.h>
+#include <SkBitmap.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+    if (argc != 2) {
+        printf("usage: %s path\n", argv[0]);
+        exit(0);
+    }
+
+    const String16 name("SurfaceFlinger");
+    sp<ISurfaceComposer> composer;
+    getService(name, &composer);
+
+    sp<IMemoryHeap> heap;
+    uint32_t w, h;
+    PixelFormat f;
+    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
+        exit(0);
+    }
+
+    printf("screen capture success: w=%u, h=%u, pixels=%p\n",
+            w, h, heap->getBase());
+
+    printf("saving file as PNG in %s ...\n", argv[1]);
+
+    SkBitmap b;
+    b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    b.setPixels(heap->getBase());
+    SkImageEncoder::EncodeFile(argv[1], b,
+            SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
+
+    return 0;
+}