Merge "remove support for console in SurfaceFlinger"
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 340daaf..96828c6 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -127,11 +127,28 @@
     // be called from the client.
     status_t setDefaultBufferSize(uint32_t w, uint32_t h);
 
-private:
+    // getCurrentBuffer returns the buffer associated with the current image.
+    sp<GraphicBuffer> getCurrentBuffer() const;
+
+    // getCurrentTextureTarget returns the texture target of the current
+    // texture as returned by updateTexImage().
+    GLenum getCurrentTextureTarget() const;
+
+    // getCurrentCrop returns the cropping rectangle of the current buffer
+    Rect getCurrentCrop() const;
+
+    // getCurrentTransform returns the transform of the current buffer
+    uint32_t getCurrentTransform() const;
+
+protected:
 
     // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
     // all slots.
     void freeAllBuffers();
+    static bool isExternalFormat(uint32_t format);
+    static GLenum getTextureTarget(uint32_t format);
+
+private:
 
     // createImage creates a new EGLImage from a GraphicBuffer.
     EGLImageKHR createImage(EGLDisplay dpy,
@@ -194,6 +211,10 @@
     // reset mCurrentTexture to INVALID_BUFFER_SLOT.
     int mCurrentTexture;
 
+    // mCurrentTextureTarget is the GLES texture target to be used with the
+    // current texture.
+    GLenum mCurrentTextureTarget;
+
     // mCurrentTextureBuf is the graphic buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to properly use
@@ -256,7 +277,7 @@
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
-    Mutex mMutex;
+    mutable Mutex mMutex;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index df82bf2..fe9b049 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -27,6 +27,8 @@
 
 namespace android {
 
+class Surface;
+
 class SurfaceTextureClient
     : public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase>
 {
@@ -36,6 +38,7 @@
     sp<ISurfaceTexture> getISurfaceTexture() const;
 
 private:
+    friend class Surface;
 
     // can't be copied
     SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs);
@@ -78,6 +81,8 @@
 
     void freeAllBuffers();
 
+    int getConnectedApi() const;
+
     enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
     enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
     enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
@@ -121,10 +126,25 @@
     // a timestamp is auto-generated when queueBuffer is called.
     int64_t mTimestamp;
 
+    // mConnectedApi holds the currently connected API to this surface
+    int mConnectedApi;
+
+    // mQueryWidth is the width returned by query(). It is set to width
+    // of the last dequeued buffer or to mReqWidth if no buffer was dequeued.
+    uint32_t mQueryWidth;
+
+    // mQueryHeight is the height returned by query(). It is set to height
+    // of the last dequeued buffer or to mReqHeight if no buffer was dequeued.
+    uint32_t mQueryHeight;
+
+    // mQueryFormat is the format returned by query(). It is set to the last
+    // dequeued format or to mReqFormat if no buffer was dequeued.
+    uint32_t mQueryFormat;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
-    Mutex mMutex;
+    mutable Mutex mMutex;
 };
 
 }; // namespace android
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 0dc29c8..9b92c73 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -620,6 +620,11 @@
     // Oldest sample to consider when calculating the velocity.
     static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
 
+    // When the total duration of the window of samples being averaged is less
+    // than the window size, the resulting velocity is scaled to reduce the impact
+    // of overestimation in short traces.
+    static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms
+
     // The minimum duration between samples when estimating velocity.
     static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
 
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index e2346f0..39418f0 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -27,6 +27,8 @@
 
 #include <gui/SurfaceTexture.h>
 
+#include <hardware/hardware.h>
+
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <surfaceflinger/IGraphicBufferAlloc.h>
@@ -82,6 +84,7 @@
     mUseDefaultSize(true),
     mBufferCount(MIN_BUFFER_SLOTS),
     mCurrentTexture(INVALID_BUFFER_SLOT),
+    mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
     mCurrentTransform(0),
     mCurrentTimestamp(0),
     mLastQueued(INVALID_BUFFER_SLOT),
@@ -197,6 +200,7 @@
     if (buffer == NULL) {
         return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
+
     if ((mUseDefaultSize) &&
         ((uint32_t(buffer->width) != mDefaultWidth) ||
          (uint32_t(buffer->height) != mDefaultHeight))) {
@@ -263,9 +267,6 @@
     LOGV("SurfaceTexture::updateTexImage");
     Mutex::Autolock lock(mMutex);
 
-    // We always bind the texture even if we don't update its contents.
-    glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName);
-
     // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
     // so this check will fail until a buffer gets queued.
     if (mCurrentTexture != mLastQueued) {
@@ -283,7 +284,15 @@
         while ((error = glGetError()) != GL_NO_ERROR) {
             LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
         }
-        glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
+
+        GLenum target = getTextureTarget(
+                mSlots[mLastQueued].mGraphicBuffer->format);
+        if (target != mCurrentTextureTarget) {
+            glDeleteTextures(1, &mTexName);
+        }
+        glBindTexture(target, mTexName);
+        glEGLImageTargetTexture2DOES(target, (GLeglImageOES)image);
+
         bool failed = false;
         while ((error = glGetError()) != GL_NO_ERROR) {
             LOGE("error binding external texture image %p (slot %d): %#04x",
@@ -296,14 +305,53 @@
 
         // Update the SurfaceTexture state.
         mCurrentTexture = mLastQueued;
+        mCurrentTextureTarget = target;
         mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
         mCurrentCrop = mLastQueuedCrop;
         mCurrentTransform = mLastQueuedTransform;
         mCurrentTimestamp = mLastQueuedTimestamp;
+    } else {
+        // We always bind the texture even if we don't update its contents.
+        glBindTexture(mCurrentTextureTarget, mTexName);
     }
     return OK;
 }
 
+bool SurfaceTexture::isExternalFormat(uint32_t format)
+{
+    switch (format) {
+    // supported YUV formats
+    case HAL_PIXEL_FORMAT_YV12:
+    // Legacy/deprecated YUV formats
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+        return true;
+    }
+
+    // Any OEM format needs to be considered
+    if (format>=0x100 && format<=0x1FF)
+        return true;
+
+    return false;
+}
+
+GLenum SurfaceTexture::getTextureTarget(uint32_t format)
+{
+    GLenum target = GL_TEXTURE_2D;
+#if defined(GL_OES_EGL_image_external)
+    if (isExternalFormat(format)) {
+        target = GL_TEXTURE_EXTERNAL_OES;
+    }
+#endif
+    return target;
+}
+
+GLenum SurfaceTexture::getCurrentTextureTarget() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTextureTarget;
+}
+
 void SurfaceTexture::getTransformMatrix(float mtx[16]) {
     LOGV("SurfaceTexture::getTransformMatrix");
     Mutex::Autolock lock(mMutex);
@@ -445,6 +493,22 @@
     return image;
 }
 
+sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTextureBuf;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTransform;
+}
+
+
 static void mtxMul(float out[16], const float a[16], const float b[16]) {
     out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
     out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 29fc4d3..f4b2416 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -26,8 +26,10 @@
 SurfaceTextureClient::SurfaceTextureClient(
         const sp<ISurfaceTexture>& surfaceTexture):
         mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
-        mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
-        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
+        mReqHeight(0), mReqFormat(0), mReqUsage(0),
+        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0),
+        mQueryWidth(0), mQueryHeight(0), mQueryFormat(0),
+        mMutex() {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = setSwapInterval;
     ANativeWindow::dequeueBuffer    = dequeueBuffer;
@@ -101,9 +103,10 @@
     }
     sp<GraphicBuffer>& gbuf(mSlots[buf]);
     if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
-        gbuf == 0 || gbuf->getWidth() != mReqWidth ||
-        gbuf->getHeight() != mReqHeight ||
-        uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
+        gbuf == 0 ||
+        (mReqWidth && gbuf->getWidth() != mReqWidth) ||
+        (mReqHeight && gbuf->getHeight() != mReqHeight) ||
+        (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) ||
         (gbuf->getUsage() & mReqUsage) != mReqUsage) {
         gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
                 mReqFormat, mReqUsage);
@@ -111,6 +114,9 @@
             LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
             return NO_MEMORY;
         }
+        mQueryWidth  = gbuf->width;
+        mQueryHeight = gbuf->height;
+        mQueryFormat = gbuf->format;
     }
     *buffer = gbuf.get();
     return OK;
@@ -159,13 +165,13 @@
     Mutex::Autolock lock(mMutex);
     switch (what) {
     case NATIVE_WINDOW_WIDTH:
+        *value = mQueryWidth ? mQueryWidth : mReqWidth;
+        return NO_ERROR;
     case NATIVE_WINDOW_HEIGHT:
-        // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't
-        // override the size?
-        *value = 0;
+        *value = mQueryHeight ? mQueryHeight : mReqHeight;
         return NO_ERROR;
     case NATIVE_WINDOW_FORMAT:
-        *value = DEFAULT_FORMAT;
+        *value = mQueryFormat ? mQueryFormat : mReqFormat;
         return NO_ERROR;
     case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
         *value = MIN_UNDEQUEUED_BUFFERS;
@@ -260,16 +266,49 @@
 
 int SurfaceTextureClient::connect(int api) {
     LOGV("SurfaceTextureClient::connect");
-    // XXX: Implement this!
-    return INVALID_OPERATION;
+    Mutex::Autolock lock(mMutex);
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+            if (mConnectedApi) {
+                err = -EINVAL;
+            } else {
+                mConnectedApi = api;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
 }
 
 int SurfaceTextureClient::disconnect(int api) {
     LOGV("SurfaceTextureClient::disconnect");
-    // XXX: Implement this!
-    return INVALID_OPERATION;
+    Mutex::Autolock lock(mMutex);
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+            if (mConnectedApi == api) {
+                mConnectedApi = 0;
+            } else {
+                err = -EINVAL;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
 }
 
+int SurfaceTextureClient::getConnectedApi() const
+{
+    Mutex::Autolock lock(mMutex);
+    return mConnectedApi;
+}
+
+
 int SurfaceTextureClient::setUsage(uint32_t reqUsage)
 {
     LOGV("SurfaceTextureClient::setUsage");
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index bbe579e..a95f432 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -832,6 +832,7 @@
         const Position& oldestPosition =
                 oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
         nsecs_t lastDuration = 0;
+
         while (numTouches-- > 1) {
             if (++index == HISTORY_SIZE) {
                 index = 0;
@@ -858,6 +859,14 @@
 
         // Make sure we used at least one sample.
         if (samplesUsed != 0) {
+            // Scale the velocity linearly if the window of samples is small.
+            nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime;
+            if (totalDuration < MIN_WINDOW) {
+                float scale = float(totalDuration) / float(MIN_WINDOW);
+                accumVx *= scale;
+                accumVy *= scale;
+            }
+
             *outVx = accumVx;
             *outVy = accumVy;
             return true;
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index d5dd126..b54fb9d 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -662,7 +662,8 @@
 #endif
 
 void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
-    sendMessageAtTime(LLONG_MIN, handler, message);
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sendMessageAtTime(now, handler, message);
 }
 
 void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 7d72729..0747efb 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -13,8 +13,8 @@
 	EGL/hooks.cpp 	       \
 	EGL/Loader.cpp 	       \
 #
-LOCAL_STATIC_LIBRARIES += libGLESv2_dbg libprotobuf-cpp-2.3.0-lite liblzf
-LOCAL_SHARED_LIBRARIES += libcutils libutils libstlport
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libGLESv2_dbg
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libEGL
 LOCAL_LDFLAGS += -Wl,--exclude-libs=ALL
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 6474c87..9cf7223 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -46,6 +46,7 @@
 #include "egl_impl.h"
 #include "Loader.h"
 #include "glesv2dbg.h"
+#include "egl_tls.h"
 
 #define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
 
@@ -58,7 +59,7 @@
 static char const * const gVendorString     = "Android";
 static char const * const gVersionString    = "1.4 Android META-EGL";
 static char const * const gClientApiString  = "OpenGL ES";
-static char const * const gExtensionString  = 
+static char const * const gExtensionString  =
         "EGL_KHR_image "
         "EGL_KHR_image_base "
         "EGL_KHR_image_pixmap "
@@ -221,18 +222,15 @@
 struct egl_context_t : public egl_object_t
 {
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
-    
+
     egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
-            int impl, egl_connection_t const* cnx, int version) 
-    : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0), 
-      impl(impl), cnx(cnx), version(version), dbg(NULL)
+            int impl, egl_connection_t const* cnx, int version)
+    : egl_object_t(dpy), dpy(dpy), context(context), config(config), read(0), draw(0),
+      impl(impl), cnx(cnx), version(version)
     {
     }
     ~egl_context_t()
     {
-        if (dbg)
-            DestroyDbgContext(dbg);
-        dbg = NULL;
     }
     EGLDisplay                  dpy;
     EGLContext                  context;
@@ -242,7 +240,6 @@
     int                         impl;
     egl_connection_t const*     cnx;
     int                         version;
-    DbgContext *                dbg;
 };
 
 struct egl_image_t : public egl_object_t
@@ -277,15 +274,6 @@
 typedef egl_image_t::Ref    ImageRef;
 typedef egl_sync_t::Ref     SyncRef;
 
-struct tls_t
-{
-    tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { }
-    EGLint      error;
-    EGLContext  ctx;
-    EGLBoolean  logCallWithNoContext;
-};
-
-
 // ----------------------------------------------------------------------------
 
 static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
@@ -323,7 +311,7 @@
     int propertyLevel = atoi(value);
     int applicationLevel = gEGLApplicationTraceLevel;
     gEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel;
-    
+
     property_get("debug.egl.debug_proc", value, "");
     long pid = getpid();
     char procPath[128] = {};
@@ -336,14 +324,20 @@
         {
             if (!strcmp(value, cmdline))
                 gEGLDebugLevel = 1;
-        }    
+        }
         fclose(file);
     }
-    
+
     if (gEGLDebugLevel > 0)
     {
         property_get("debug.egl.debug_port", value, "5039");
-        StartDebugServer(atoi(value));
+        const unsigned short port = (unsigned short)atoi(value);
+        property_get("debug.egl.debug_forceUseFile", value, "0");
+        const bool forceUseFile = (bool)atoi(value);
+        property_get("debug.egl.debug_maxFileSize", value, "8");
+        const unsigned int maxFileSize = atoi(value) << 20;
+        property_get("debug.egl.debug_filePath", value, "/data/local/tmp/dump.gles2dbg");
+        StartDebugServer(port, forceUseFile, maxFileSize, value);
     }
 }
 
@@ -586,7 +580,7 @@
 }
 
 static inline
-egl_surface_t* get_surface(EGLSurface surface) {   
+egl_surface_t* get_surface(EGLSurface surface) {
     return egl_to_native_cast<egl_surface_t>(surface);
 }
 
@@ -595,11 +589,6 @@
     return egl_to_native_cast<egl_context_t>(context);
 }
 
-DbgContext * getDbgContextThreadSpecific()
-{
-    return get_context(getContext())->dbg;
-}
-
 static inline
 egl_image_t* get_image(EGLImageKHR image) {
     return egl_to_native_cast<egl_image_t>(image);
@@ -1442,10 +1431,12 @@
         loseCurrent(cur_c);
 
         if (ctx != EGL_NO_CONTEXT) {
-            if (!c->dbg && gEGLDebugLevel > 0)
-                c->dbg = CreateDbgContext(c->version, c->cnx->hooks[c->version]);
             setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
             setContext(ctx);
+            tls_t * const tls = getTLS();
+            if (!tls->dbg && gEGLDebugLevel > 0)
+                tls->dbg = CreateDbgContext(gEGLThreadLocalStorageKey, c->version,
+                                            c->cnx->hooks[c->version]);
             _c.acquire();
             _r.acquire();
             _d.acquire();
diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk
index fc40799..9f6e68c 100644
--- a/opengl/libs/GLES2_dbg/Android.mk
+++ b/opengl/libs/GLES2_dbg/Android.mk
@@ -11,7 +11,7 @@
     src/server.cpp \
     src/vertex.cpp
 
-LOCAL_C_INCLUDES :=	\
+LOCAL_C_INCLUDES := \
     $(LOCAL_PATH) \
     $(LOCAL_PATH)/../ \
     external/stlport/stlport \
@@ -21,7 +21,8 @@
 
 #LOCAL_CFLAGS += -O0 -g -DDEBUG -UNDEBUG
 LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI
-
+LOCAL_STATIC_LIBRARIES := libprotobuf-cpp-2.3.0-lite liblzf
+LOCAL_SHARED_LIBRARIES := libcutils libutils libstlport
 ifeq ($(TARGET_ARCH),arm)
 	LOCAL_CFLAGS += -fstrict-aliasing
 endif
@@ -43,4 +44,6 @@
 LOCAL_MODULE:= libGLESv2_dbg
 LOCAL_MODULE_TAGS := optional
 
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_SHARED_LIBRARY)
+
+include $(LOCAL_PATH)/test/Android.mk
diff --git a/opengl/libs/GLES2_dbg/generate_api_cpp.py b/opengl/libs/GLES2_dbg/generate_api_cpp.py
index 66c110f..aeba213 100755
--- a/opengl/libs/GLES2_dbg/generate_api_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_api_cpp.py
@@ -26,36 +26,36 @@
         return line.replace(annotation, "*")
     else:
         return line
-    
+
 def generate_api(lines):
     externs = []
     i = 0
     # these have been hand written
-    skipFunctions = ["glReadPixels", "glDrawArrays", "glDrawElements"]
-    
+    skipFunctions = ["glDrawArrays", "glDrawElements"]
+
     # these have an EXTEND_Debug_* macro for getting data
-    extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glShaderSource",
-"glTexImage2D", "glTexSubImage2D"]
-    
+    extendFunctions = ["glCopyTexImage2D", "glCopyTexSubImage2D", "glReadPixels",
+"glShaderSource", "glTexImage2D", "glTexSubImage2D"]
+
     # these also needs to be forwarded to DbgContext
     contextFunctions = ["glUseProgram", "glEnableVertexAttribArray", "glDisableVertexAttribArray", 
 "glVertexAttribPointer", "glBindBuffer", "glBufferData", "glBufferSubData", "glDeleteBuffers",]
-    
+
     for line in lines:
         if line.find("API_ENTRY(") >= 0: # a function prototype
             returnType = line[0: line.find(" API_ENTRY(")]
             functionName = line[line.find("(") + 1: line.find(")")] #extract GL function name
             parameterList = line[line.find(")(") + 2: line.find(") {")]
-            
+
             #if line.find("*") >= 0:
             #    extern = "%s Debug_%s(%s);" % (returnType, functionName, parameterList)
             #    externs.append(extern)
             #    continue
-            
+
             if functionName in skipFunctions:
                 sys.stderr.write("!\n! skipping function '%s'\n!\n" % (functionName))
                 continue
-                
+
             parameters = parameterList.split(',')
             paramIndex = 0
             if line.find("*") >= 0 and (line.find("*") < line.find(":") or line.find("*") > line.rfind(":")): # unannotated pointer
@@ -65,21 +65,21 @@
                     sys.stderr.write("%s should be hand written\n" % (extern))
                     print "// FIXME: this function has pointers, it should be hand written"
                     externs.append(extern)
-                
+
             print "%s Debug_%s(%s)\n{" % (returnType, functionName, RemoveAnnotation(parameterList))
             print "    glesv2debugger::Message msg;"
-    
+
             if parameterList == "void":
                 parameters = []
             arguments = ""
             paramNames = []
             inout = ""
             getData = ""
-            
+
             callerMembers = ""
             setCallerMembers = ""
             setMsgParameters = ""
-            
+
             for parameter in parameters:
                 const = parameter.find("const")
                 parameter = parameter.replace("const", "")
@@ -107,7 +107,7 @@
                         annotation = "strlen(%s)" % (paramName)
                     else:
                         count = int(annotation)
-            
+
                     setMsgParameters += "    msg.set_arg%d(ToInt(%s));\n" % (paramIndex, paramName)
                     if paramType.find("void") >= 0:
                         getData += "    msg.mutable_data()->assign(reinterpret_cast<const char *>(%s), %s * sizeof(char));" % (paramName, annotation)
@@ -127,7 +127,7 @@
                 paramIndex += 1
                 callerMembers += "        %s %s;\n" % (paramType, paramName)
                 setCallerMembers += "    caller.%s = %s;\n" % (paramName, paramName)
-            
+
             print "    struct : public FunctionCall {"
             print callerMembers
             print "        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {"
@@ -141,6 +141,11 @@
             if inout in ["out", "inout"]:
                 print "            msg.set_time((systemTime(timeMode) - c0) * 1e-6f);"
                 print "        " + getData
+            if functionName in extendFunctions:
+                print "\
+#ifdef EXTEND_AFTER_CALL_Debug_%s\n\
+            EXTEND_AFTER_CALL_Debug_%s;\n\
+#endif" % (functionName, functionName)
             if functionName in contextFunctions:
                 print "            getDbgContextThreadSpecific()->%s(%s);" % (functionName, arguments)
             if returnType == "void":
@@ -157,7 +162,10 @@
             if inout in ["in", "inout"]:
                 print getData
             if functionName in extendFunctions:
-                print "    EXTEND_Debug_%s;" % (functionName) 
+                print "\
+#ifdef EXTEND_Debug_%s\n\
+    EXTEND_Debug_%s;\n\
+#endif" % (functionName, functionName)
             print "    int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_%s);"\
                 % (functionName)
             if returnType != "void":
@@ -166,8 +174,8 @@
                 else:
                     print "    return reinterpret_cast<%s>(ret);" % (returnType)
             print "}\n"
-                        
-            
+
+
     print "// FIXME: the following functions should be written by hand"
     for extern in externs:
         print extern
@@ -189,18 +197,23 @@
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
- 
+
 // auto generated by generate_api_cpp.py
 
+#include <utils/Debug.h>
+
 #include "src/header.h"
 #include "src/api.h"
 
-template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
-template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
-"""    
+template<typename T> static int ToInt(const T & t)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int));
+    return (int &)t;
+}
+"""
     lines = open("gl2_api_annotated.in").readlines()
     generate_api(lines)
     #lines = open("gl2ext_api.in").readlines()
     #generate_api(lines)
-            
+
 
diff --git a/opengl/libs/GLES2_dbg/generate_caller_cpp.py b/opengl/libs/GLES2_dbg/generate_caller_cpp.py
index eac2292..ee4208d 100755
--- a/opengl/libs/GLES2_dbg/generate_caller_cpp.py
+++ b/opengl/libs/GLES2_dbg/generate_caller_cpp.py
@@ -177,7 +177,6 @@
 {
     LOGD("GenerateCall function=%u", cmd.function());
     const int * ret = prevRet; // only some functions have return value
-    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
     nsecs_t c0 = systemTime(timeMode);
     switch (cmd.function()) {""")
     
diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
index 466c447..57e008c 100755
--- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
+++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
@@ -70,41 +70,43 @@
 """)
 
     i = 0;
-    
+
     lines = open("gl2_api_annotated.in").readlines()
     i = generate_gl_entries(output, lines, i)
     output.write("        // end of GL functions\n")
-    
+
     #lines = open("gl2ext_api.in").readlines()
     #i = generate_gl_entries(output, lines, i)
     #output.write("        // end of GL EXT functions\n")
-    
+
     lines = open("../EGL/egl_entries.in").readlines()
     i = generate_egl_entries(output, lines, i)
     output.write("        // end of GL EXT functions\n")
-    
+
     output.write("        ACK = %d;\n" % (i))
     i += 1
-    
+
     output.write("        NEG = %d;\n" % (i))
     i += 1
-    
+
     output.write("        CONTINUE = %d;\n" % (i))
     i += 1
-    
+
     output.write("        SKIP = %d;\n" % (i))
     i += 1
-    
+
     output.write("        SETPROP = %d;\n" % (i))
     i += 1
-    
+
     output.write("""    }
     required Function function = 2 [default = NEG]; // type/function of message
     enum Type
     {
         BeforeCall = 0;
         AfterCall = 1;
-        Response = 2; // currently used for misc messages
+        AfterGeneratedCall = 2;
+        Response = 3; // currently used for misc messages
+        CompleteCall = 4; // BeforeCall and AfterCall merged
     }
     required Type type = 3;
     required bool expect_response = 4;
@@ -125,16 +127,22 @@
         ReferencedImage = 0; // for image sourced from ReadPixels
         NonreferencedImage = 1; // for image sourced from ReadPixels
     };
-    optional DataType data_type = 23; // most data types can be inferred from function
-    optional int32 pixel_format = 24; // used for image data if format and type 
-    optional int32 pixel_type = 25;   //     cannot be determined from arg 
-    
+    // most data types can be inferred from function
+    optional DataType data_type = 23;
+    // these are used for image data when they cannot be determined from args
+    optional int32 pixel_format = 24;
+    optional int32 pixel_type = 25;
+    optional int32 image_width = 26;
+    optional int32 image_height = 27;
+
     optional float time = 11; // duration of previous GL call (ms)
     enum Prop
     {
-        Capture = 0; // arg0 = true | false
+        CaptureDraw = 0; // arg0 = number of glDrawArrays/Elements to glReadPixels
         TimeMode = 1; // arg0 = SYSTEM_TIME_* in utils/Timers.h
         ExpectResponse = 2; // arg0 = enum Function, arg1 = true/false
+        CaptureSwap = 3; // arg0 = number of eglSwapBuffers to glReadPixels
+        GLConstant = 4; // arg0 = GLenum, arg1 = constant; send GL impl. constants
     };
     optional Prop prop = 21; // used with SETPROP, value in arg0
     optional float clock = 22; // wall clock in seconds
@@ -142,6 +150,6 @@
 """)
 
     output.close()
-    
+
     os.system("aprotoc --cpp_out=src --java_out=../../../../../development/tools/glesv2debugger/src debugger_message.proto")
     os.system('mv -f "src/debugger_message.pb.cc" "src/debugger_message.pb.cpp"')
diff --git a/opengl/libs/GLES2_dbg/src/api.cpp b/opengl/libs/GLES2_dbg/src/api.cpp
index 130ca7e..c483547 100644
--- a/opengl/libs/GLES2_dbg/src/api.cpp
+++ b/opengl/libs/GLES2_dbg/src/api.cpp
@@ -16,11 +16,16 @@
  
 // auto generated by generate_api_cpp.py
 
+#include <utils/Debug.h>
+
 #include "src/header.h"
 #include "src/api.h"
 
-template<typename T> static int ToInt(const T & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (int &)t; }
-template<typename T> static T FromInt(const int & t) { STATIC_ASSERT(sizeof(T) == sizeof(int), bitcast); return (T &)t; }
+template<typename T> static int ToInt(const T & t)
+{
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(T) == sizeof(int));
+    return (int &)t;
+}
 
 void Debug_glActiveTexture(GLenum texture)
 {
@@ -592,6 +597,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexImage2D
+            EXTEND_AFTER_CALL_Debug_glCopyTexImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -613,7 +621,9 @@
     msg.set_arg6(height);
     msg.set_arg7(border);
 
+#ifdef EXTEND_Debug_glCopyTexImage2D
     EXTEND_Debug_glCopyTexImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexImage2D);
 }
 
@@ -632,6 +642,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+#ifdef EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D
+            EXTEND_AFTER_CALL_Debug_glCopyTexSubImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -653,7 +666,9 @@
     msg.set_arg6(width);
     msg.set_arg7(height);
 
+#ifdef EXTEND_Debug_glCopyTexSubImage2D
     EXTEND_Debug_glCopyTexSubImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glCopyTexSubImage2D);
 }
 
@@ -2164,6 +2179,49 @@
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glPolygonOffset);
 }
 
+void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
+{
+    glesv2debugger::Message msg;
+    struct : public FunctionCall {
+        GLint x;
+        GLint y;
+        GLsizei width;
+        GLsizei height;
+        GLenum format;
+        GLenum type;
+        GLvoid* pixels;
+
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            _c->glReadPixels(x, y, width, height, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glReadPixels
+            EXTEND_AFTER_CALL_Debug_glReadPixels;
+#endif
+            return 0;
+        }
+    } caller;
+    caller.x = x;
+    caller.y = y;
+    caller.width = width;
+    caller.height = height;
+    caller.format = format;
+    caller.type = type;
+    caller.pixels = pixels;
+
+    msg.set_arg0(x);
+    msg.set_arg1(y);
+    msg.set_arg2(width);
+    msg.set_arg3(height);
+    msg.set_arg4(format);
+    msg.set_arg5(type);
+    msg.set_arg6(ToInt(pixels));
+
+    // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glReadPixels
+    EXTEND_Debug_glReadPixels;
+#endif
+    int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glReadPixels);
+}
+
 void Debug_glReleaseShaderCompiler(void)
 {
     glesv2debugger::Message msg;
@@ -2297,6 +2355,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glShaderSource(shader, count, string, length);
+#ifdef EXTEND_AFTER_CALL_Debug_glShaderSource
+            EXTEND_AFTER_CALL_Debug_glShaderSource;
+#endif
             return 0;
         }
     } caller;
@@ -2311,7 +2372,9 @@
     msg.set_arg3(ToInt(length));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glShaderSource
     EXTEND_Debug_glShaderSource;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glShaderSource);
 }
 
@@ -2472,6 +2535,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glTexImage2D
+            EXTEND_AFTER_CALL_Debug_glTexImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -2496,7 +2562,9 @@
     msg.set_arg8(ToInt(pixels));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glTexImage2D
     EXTEND_Debug_glTexImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexImage2D);
 }
 
@@ -2616,6 +2684,9 @@
 
         const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
             _c->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+#ifdef EXTEND_AFTER_CALL_Debug_glTexSubImage2D
+            EXTEND_AFTER_CALL_Debug_glTexSubImage2D;
+#endif
             return 0;
         }
     } caller;
@@ -2640,7 +2711,9 @@
     msg.set_arg8(ToInt(pixels));
 
     // FIXME: check for pointer usage
+#ifdef EXTEND_Debug_glTexSubImage2D
     EXTEND_Debug_glTexSubImage2D;
+#endif
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_glTexSubImage2D);
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/api.h b/opengl/libs/GLES2_dbg/src/api.h
index b9fc341..0b227bc 100644
--- a/opengl/libs/GLES2_dbg/src/api.h
+++ b/opengl/libs/GLES2_dbg/src/api.h
@@ -16,19 +16,29 @@
 
 #define EXTEND_Debug_glCopyTexImage2D \
     DbgContext * const dbg = getDbgContextThreadSpecific(); \
-    GLint readFormat, readType; \
-    dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat); \
-    dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType); \
-    unsigned readSize = GetBytesPerPixel(readFormat, readType) * width * height; \
-    void * readData = dbg->GetReadPixelsBuffer(readSize); \
-    dbg->hooks->gl.glReadPixels(x, y, width, height, readFormat, readType, readData); \
+    void * readData = dbg->GetReadPixelsBuffer(4 * width * height); \
+    /* pick easy format for client to convert */ \
+    dbg->hooks->gl.glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, readData); \
     dbg->CompressReadPixelBuffer(msg.mutable_data()); \
     msg.set_data_type(msg.ReferencedImage); \
-    msg.set_pixel_format(readFormat); \
-    msg.set_pixel_type(readType);
+    msg.set_pixel_format(GL_RGBA); \
+    msg.set_pixel_type(GL_UNSIGNED_BYTE);
 
 #define EXTEND_Debug_glCopyTexSubImage2D EXTEND_Debug_glCopyTexImage2D
 
+#define EXTEND_AFTER_CALL_Debug_glReadPixels \
+    { \
+        DbgContext * const dbg = getDbgContextThreadSpecific(); \
+        if (dbg->IsReadPixelBuffer(pixels)) { \
+            dbg->CompressReadPixelBuffer(msg.mutable_data()); \
+            msg.set_data_type(msg.ReferencedImage); \
+        } else { \
+            const unsigned int size = width * height * GetBytesPerPixel(format, type); \
+            dbg->Compress(pixels, size, msg.mutable_data()); \
+            msg.set_data_type(msg.NonreferencedImage); \
+        } \
+    }
+
 #define EXTEND_Debug_glShaderSource \
     std::string * const data = msg.mutable_data(); \
     for (unsigned i = 0; i < count; i++) \
diff --git a/opengl/libs/GLES2_dbg/src/caller.cpp b/opengl/libs/GLES2_dbg/src/caller.cpp
index 9992f05..6b72751 100644
--- a/opengl/libs/GLES2_dbg/src/caller.cpp
+++ b/opengl/libs/GLES2_dbg/src/caller.cpp
@@ -105,7 +105,6 @@
 {
     LOGD("GenerateCall function=%u", cmd.function());
     const int * ret = prevRet; // only some functions have return value
-    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;
     nsecs_t c0 = systemTime(timeMode);
     switch (cmd.function()) {    case glesv2debugger::Message_Function_glActiveTexture:
         dbg->hooks->gl.glActiveTexture(
@@ -772,7 +771,7 @@
     msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_function(cmd.function());
-    msg.set_type(glesv2debugger::Message_Type_AfterCall);
+    msg.set_type(glesv2debugger::Message_Type_AfterGeneratedCall);
     return ret;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/caller.h b/opengl/libs/GLES2_dbg/src/caller.h
index 5447757..e8111b3 100644
--- a/opengl/libs/GLES2_dbg/src/caller.h
+++ b/opengl/libs/GLES2_dbg/src/caller.h
@@ -138,7 +138,9 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    GLint params = -1;
+    dbg->hooks->gl.glGetProgramiv(cmd.arg0(), cmd.arg1(), &params);
+    msg.mutable_data()->append(reinterpret_cast<char *>(&params), sizeof(params));
     return prevRet;
 }
 
@@ -146,7 +148,10 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize());
+    GLsizei length = -1;
+    dbg->hooks->gl.glGetProgramInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer());
+    msg.mutable_data()->append(dbg->GetBuffer(), length);
     return prevRet;
 }
 
@@ -162,7 +167,9 @@
                                         const glesv2debugger::Message & cmd,
                                         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    GLint params = -1;
+    dbg->hooks->gl.glGetShaderiv(cmd.arg0(), cmd.arg1(), &params);
+    msg.mutable_data()->append(reinterpret_cast<char *>(&params), sizeof(params));
     return prevRet;
 }
 
@@ -170,7 +177,10 @@
         const glesv2debugger::Message & cmd,
         glesv2debugger::Message & msg, const int * const prevRet)
 {
-    assert(0);
+    const GLsizei bufSize = static_cast<GLsizei>(dbg->GetBufferSize());
+    GLsizei length = -1;
+    dbg->hooks->gl.glGetShaderInfoLog(cmd.arg0(), bufSize, &length, dbg->GetBuffer());
+    msg.mutable_data()->append(dbg->GetBuffer(), length);
     return prevRet;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index cc7336c..7f5b27b 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "header.h"
+#include "egl_tls.h"
 
 extern "C"
 {
@@ -24,11 +25,23 @@
 namespace android
 {
 
+pthread_key_t dbgEGLThreadLocalStorageKey = -1;
+
+DbgContext * getDbgContextThreadSpecific()
+{
+    tls_t* tls = (tls_t*)pthread_getspecific(dbgEGLThreadLocalStorageKey);
+    return tls->dbg;
+}
+
 DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-                       const unsigned MAX_VERTEX_ATTRIBS)
+                       const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
+                       const GLenum readType)
         : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0)
         , version(version), hooks(hooks)
         , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
+        , readFormat(readFormat), readType(readType)
+        , readBytesPerPixel(GetBytesPerPixel(readFormat, readType))
+        , captureSwap(0), captureDraw(0)
         , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
         , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
         , program(0), maxAttrib(0)
@@ -47,13 +60,35 @@
     free(lzf_ref[1]);
 }
 
-DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks)
+DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
+                              const unsigned version, const gl_hooks_t * const hooks)
 {
+    dbgEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
     assert(version < 2);
     assert(GL_NO_ERROR == hooks->gl.glGetError());
     GLint MAX_VERTEX_ATTRIBS = 0;
     hooks->gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &MAX_VERTEX_ATTRIBS);
-    return new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
+    GLint readFormat, readType;
+    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+    DbgContext * const dbg = new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS, readFormat, readType);
+
+    glesv2debugger::Message msg, cmd;
+    msg.set_context_id(reinterpret_cast<int>(dbg));
+    msg.set_expect_response(false);
+    msg.set_type(msg.Response);
+    msg.set_function(msg.SETPROP);
+    msg.set_prop(msg.GLConstant);
+    msg.set_arg0(GL_MAX_VERTEX_ATTRIBS);
+    msg.set_arg1(MAX_VERTEX_ATTRIBS);
+    Send(msg, cmd);
+
+    GLint MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0;
+    hooks->gl.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+    msg.set_arg0(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+    msg.set_arg1(MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+    Send(msg, cmd);
+    return dbg;
 }
 
 void DestroyDbgContext(DbgContext * const dbg)
@@ -113,6 +148,7 @@
 {
     if (!lzf_buf)
         lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
     const uint32_t totalDecompSize = in_len;
     outStr->append((const char *)&totalDecompSize, sizeof(totalDecompSize));
     for (unsigned int i = 0; i < in_len; i += LZF_CHUNK_SIZE) {
@@ -130,13 +166,46 @@
     }
 }
 
+unsigned char * DbgContext::Decompress(const void * in, const unsigned int inLen,
+                                       unsigned int * const outLen)
+{
+    assert(inLen > 4 * 3);
+    if (inLen < 4 * 3)
+        return NULL;
+    *outLen = *(uint32_t *)in;
+    unsigned char * const out = (unsigned char *)malloc(*outLen);
+    unsigned int outPos = 0;
+    const unsigned char * const end = (const unsigned char *)in + inLen;
+    for (const unsigned char * inData = (const unsigned char *)in + 4; inData < end; ) {
+        const uint32_t chunkOut = *(uint32_t *)inData;
+        inData += 4;
+        const uint32_t chunkIn = *(uint32_t *)inData;
+        inData += 4;
+        if (chunkIn > 0) {
+            assert(inData + chunkIn <= end);
+            assert(outPos + chunkOut <= *outLen);
+            outPos += lzf_decompress(inData, chunkIn, out + outPos, chunkOut);
+            inData += chunkIn;
+        } else {
+            assert(inData + chunkOut <= end);
+            assert(outPos + chunkOut <= *outLen);
+            memcpy(out + outPos, inData, chunkOut);
+            inData += chunkOut;
+            outPos += chunkOut;
+        }
+    }
+    return out;
+}
+
 void * DbgContext::GetReadPixelsBuffer(const unsigned size)
 {
     if (lzf_refBufSize < size + 8) {
         lzf_refBufSize = size + 8;
         lzf_ref[0] = (unsigned *)realloc(lzf_ref[0], lzf_refBufSize);
+        assert(lzf_ref[0]);
         memset(lzf_ref[0], 0, lzf_refBufSize);
         lzf_ref[1] = (unsigned *)realloc(lzf_ref[1], lzf_refBufSize);
+        assert(lzf_ref[1]);
         memset(lzf_ref[1], 0, lzf_refBufSize);
     }
     if (lzf_refSize != size) // need to clear unused ref to maintain consistency
@@ -151,6 +220,7 @@
 
 void DbgContext::CompressReadPixelBuffer(std::string * const outStr)
 {
+    assert(lzf_ref[0] && lzf_ref[1]);
     unsigned * const ref = lzf_ref[lzf_readIndex ^ 1];
     unsigned * const src = lzf_ref[lzf_readIndex];
     for (unsigned i = 0; i < lzf_refSize / sizeof(*ref) + 1; i++)
@@ -158,13 +228,34 @@
     Compress(ref, lzf_refSize, outStr);
 }
 
+char * DbgContext::GetBuffer()
+{
+    if (!lzf_buf)
+        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
+    return lzf_buf;
+}
+
+unsigned int DbgContext::GetBufferSize()
+{
+    if (!lzf_buf)
+        lzf_buf = (char *)malloc(LZF_CHUNK_SIZE);
+    assert(lzf_buf);
+    if (lzf_buf)
+        return LZF_CHUNK_SIZE;
+    else
+        return 0;
+}
+
 void DbgContext::glUseProgram(GLuint program)
 {
     while (GLenum error = hooks->gl.glGetError())
-        LOGD("DbgContext::glUseProgram: before glGetError() = 0x%.4X", error);
-
+        LOGD("DbgContext::glUseProgram(%u): before glGetError() = 0x%.4X",
+             program, error);
     this->program = program;
-
+    maxAttrib = 0;
+    if (program == 0)
+        return;
     GLint activeAttributes = 0;
     hooks->gl.glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);
     maxAttrib = 0;
@@ -202,9 +293,9 @@
             maxAttrib = slot;
     }
     delete name;
-
     while (GLenum error = hooks->gl.glGetError())
-        LOGD("DbgContext::glUseProgram: after glGetError() = 0x%.4X", error);
+        LOGD("DbgContext::glUseProgram(%u): after glGetError() = 0x%.4X",
+             program, error);
 }
 
 static bool HasNonVBOAttribs(const DbgContext * const ctx)
@@ -254,14 +345,16 @@
 
 void DbgContext::glEnableVertexAttribArray(GLuint index)
 {
-    assert(index < MAX_VERTEX_ATTRIBS);
+    if (index >= MAX_VERTEX_ATTRIBS)
+        return;
     vertexAttribs[index].enabled = true;
     hasNonVBOAttribs = HasNonVBOAttribs(this);
 }
 
 void DbgContext::glDisableVertexAttribArray(GLuint index)
 {
-    assert(index < MAX_VERTEX_ATTRIBS);
+    if (index >= MAX_VERTEX_ATTRIBS)
+        return;
     vertexAttribs[index].enabled = false;
     hasNonVBOAttribs = HasNonVBOAttribs(this);
 }
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
index 046c954..50f70f7 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
@@ -436,6 +436,8 @@
     case 0:
     case 1:
     case 2:
+    case 3:
+    case 4:
       return true;
     default:
       return false;
@@ -445,7 +447,9 @@
 #ifndef _MSC_VER
 const Message_Type Message::BeforeCall;
 const Message_Type Message::AfterCall;
+const Message_Type Message::AfterGeneratedCall;
 const Message_Type Message::Response;
+const Message_Type Message::CompleteCall;
 const Message_Type Message::Type_MIN;
 const Message_Type Message::Type_MAX;
 const int Message::Type_ARRAYSIZE;
@@ -472,6 +476,8 @@
     case 0:
     case 1:
     case 2:
+    case 3:
+    case 4:
       return true;
     default:
       return false;
@@ -479,9 +485,11 @@
 }
 
 #ifndef _MSC_VER
-const Message_Prop Message::Capture;
+const Message_Prop Message::CaptureDraw;
 const Message_Prop Message::TimeMode;
 const Message_Prop Message::ExpectResponse;
+const Message_Prop Message::CaptureSwap;
+const Message_Prop Message::GLConstant;
 const Message_Prop Message::Prop_MIN;
 const Message_Prop Message::Prop_MAX;
 const int Message::Prop_ARRAYSIZE;
@@ -506,6 +514,8 @@
 const int Message::kDataTypeFieldNumber;
 const int Message::kPixelFormatFieldNumber;
 const int Message::kPixelTypeFieldNumber;
+const int Message::kImageWidthFieldNumber;
+const int Message::kImageHeightFieldNumber;
 const int Message::kTimeFieldNumber;
 const int Message::kPropFieldNumber;
 const int Message::kClockFieldNumber;
@@ -545,6 +555,8 @@
   data_type_ = 0;
   pixel_format_ = 0;
   pixel_type_ = 0;
+  image_width_ = 0;
+  image_height_ = 0;
   time_ = 0;
   prop_ = 0;
   clock_ = 0;
@@ -606,6 +618,8 @@
   if (_has_bits_[16 / 32] & (0xffu << (16 % 32))) {
     pixel_format_ = 0;
     pixel_type_ = 0;
+    image_width_ = 0;
+    image_height_ = 0;
     time_ = 0;
     prop_ = 0;
     clock_ = 0;
@@ -790,7 +804,7 @@
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
                  input, &time_)));
-          _set_bit(18);
+          _set_bit(20);
         } else {
           goto handle_uninterpreted;
         }
@@ -905,7 +919,7 @@
           DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
                    float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
                  input, &clock_)));
-          _set_bit(20);
+          _set_bit(22);
         } else {
           goto handle_uninterpreted;
         }
@@ -960,6 +974,38 @@
         } else {
           goto handle_uninterpreted;
         }
+        if (input->ExpectTag(208)) goto parse_image_width;
+        break;
+      }
+      
+      // optional int32 image_width = 26;
+      case 26: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_image_width:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &image_width_)));
+          _set_bit(18);
+        } else {
+          goto handle_uninterpreted;
+        }
+        if (input->ExpectTag(216)) goto parse_image_height;
+        break;
+      }
+      
+      // optional int32 image_height = 27;
+      case 27: {
+        if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+            ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+         parse_image_height:
+          DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+                   ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+                 input, &image_height_)));
+          _set_bit(19);
+        } else {
+          goto handle_uninterpreted;
+        }
         if (input->ExpectAtEnd()) return true;
         break;
       }
@@ -1035,7 +1081,7 @@
   }
   
   // optional float time = 11;
-  if (_has_bit(18)) {
+  if (_has_bit(20)) {
     ::google::protobuf::internal::WireFormatLite::WriteFloat(11, this->time(), output);
   }
   
@@ -1065,13 +1111,13 @@
   }
   
   // optional .com.android.glesv2debugger.Message.Prop prop = 21;
-  if (_has_bit(19)) {
+  if (_has_bit(21)) {
     ::google::protobuf::internal::WireFormatLite::WriteEnum(
       21, this->prop(), output);
   }
   
   // optional float clock = 22;
-  if (_has_bit(20)) {
+  if (_has_bit(22)) {
     ::google::protobuf::internal::WireFormatLite::WriteFloat(22, this->clock(), output);
   }
   
@@ -1091,6 +1137,16 @@
     ::google::protobuf::internal::WireFormatLite::WriteInt32(25, this->pixel_type(), output);
   }
   
+  // optional int32 image_width = 26;
+  if (_has_bit(18)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(26, this->image_width(), output);
+  }
+  
+  // optional int32 image_height = 27;
+  if (_has_bit(19)) {
+    ::google::protobuf::internal::WireFormatLite::WriteInt32(27, this->image_height(), output);
+  }
+  
 }
 
 int Message::ByteSize() const {
@@ -1222,6 +1278,20 @@
           this->pixel_type());
     }
     
+    // optional int32 image_width = 26;
+    if (has_image_width()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->image_width());
+    }
+    
+    // optional int32 image_height = 27;
+    if (has_image_height()) {
+      total_size += 2 +
+        ::google::protobuf::internal::WireFormatLite::Int32Size(
+          this->image_height());
+    }
+    
     // optional float time = 11;
     if (has_time()) {
       total_size += 1 + 4;
@@ -1312,12 +1382,18 @@
       set_pixel_type(from.pixel_type());
     }
     if (from._has_bit(18)) {
-      set_time(from.time());
+      set_image_width(from.image_width());
     }
     if (from._has_bit(19)) {
-      set_prop(from.prop());
+      set_image_height(from.image_height());
     }
     if (from._has_bit(20)) {
+      set_time(from.time());
+    }
+    if (from._has_bit(21)) {
+      set_prop(from.prop());
+    }
+    if (from._has_bit(22)) {
       set_clock(from.clock());
     }
   }
@@ -1355,6 +1431,8 @@
     std::swap(data_type_, other->data_type_);
     std::swap(pixel_format_, other->pixel_format_);
     std::swap(pixel_type_, other->pixel_type_);
+    std::swap(image_width_, other->image_width_);
+    std::swap(image_height_, other->image_height_);
     std::swap(time_, other->time_);
     std::swap(prop_, other->prop_);
     std::swap(clock_, other->clock_);
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
index b2ec5a0..5c94664 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
@@ -236,11 +236,13 @@
 enum Message_Type {
   Message_Type_BeforeCall = 0,
   Message_Type_AfterCall = 1,
-  Message_Type_Response = 2
+  Message_Type_AfterGeneratedCall = 2,
+  Message_Type_Response = 3,
+  Message_Type_CompleteCall = 4
 };
 bool Message_Type_IsValid(int value);
 const Message_Type Message_Type_Type_MIN = Message_Type_BeforeCall;
-const Message_Type Message_Type_Type_MAX = Message_Type_Response;
+const Message_Type Message_Type_Type_MAX = Message_Type_CompleteCall;
 const int Message_Type_Type_ARRAYSIZE = Message_Type_Type_MAX + 1;
 
 enum Message_DataType {
@@ -253,13 +255,15 @@
 const int Message_DataType_DataType_ARRAYSIZE = Message_DataType_DataType_MAX + 1;
 
 enum Message_Prop {
-  Message_Prop_Capture = 0,
+  Message_Prop_CaptureDraw = 0,
   Message_Prop_TimeMode = 1,
-  Message_Prop_ExpectResponse = 2
+  Message_Prop_ExpectResponse = 2,
+  Message_Prop_CaptureSwap = 3,
+  Message_Prop_GLConstant = 4
 };
 bool Message_Prop_IsValid(int value);
-const Message_Prop Message_Prop_Prop_MIN = Message_Prop_Capture;
-const Message_Prop Message_Prop_Prop_MAX = Message_Prop_ExpectResponse;
+const Message_Prop Message_Prop_Prop_MIN = Message_Prop_CaptureDraw;
+const Message_Prop Message_Prop_Prop_MAX = Message_Prop_GLConstant;
 const int Message_Prop_Prop_ARRAYSIZE = Message_Prop_Prop_MAX + 1;
 
 // ===================================================================
@@ -510,7 +514,9 @@
   typedef Message_Type Type;
   static const Type BeforeCall = Message_Type_BeforeCall;
   static const Type AfterCall = Message_Type_AfterCall;
+  static const Type AfterGeneratedCall = Message_Type_AfterGeneratedCall;
   static const Type Response = Message_Type_Response;
+  static const Type CompleteCall = Message_Type_CompleteCall;
   static inline bool Type_IsValid(int value) {
     return Message_Type_IsValid(value);
   }
@@ -535,9 +541,11 @@
     Message_DataType_DataType_ARRAYSIZE;
   
   typedef Message_Prop Prop;
-  static const Prop Capture = Message_Prop_Capture;
+  static const Prop CaptureDraw = Message_Prop_CaptureDraw;
   static const Prop TimeMode = Message_Prop_TimeMode;
   static const Prop ExpectResponse = Message_Prop_ExpectResponse;
+  static const Prop CaptureSwap = Message_Prop_CaptureSwap;
+  static const Prop GLConstant = Message_Prop_GLConstant;
   static inline bool Prop_IsValid(int value) {
     return Message_Prop_IsValid(value);
   }
@@ -679,6 +687,20 @@
   inline ::google::protobuf::int32 pixel_type() const;
   inline void set_pixel_type(::google::protobuf::int32 value);
   
+  // optional int32 image_width = 26;
+  inline bool has_image_width() const;
+  inline void clear_image_width();
+  static const int kImageWidthFieldNumber = 26;
+  inline ::google::protobuf::int32 image_width() const;
+  inline void set_image_width(::google::protobuf::int32 value);
+  
+  // optional int32 image_height = 27;
+  inline bool has_image_height() const;
+  inline void clear_image_height();
+  static const int kImageHeightFieldNumber = 27;
+  inline ::google::protobuf::int32 image_height() const;
+  inline void set_image_height(::google::protobuf::int32 value);
+  
   // optional float time = 11;
   inline bool has_time() const;
   inline void clear_time();
@@ -723,6 +745,8 @@
   int data_type_;
   ::google::protobuf::int32 pixel_format_;
   ::google::protobuf::int32 pixel_type_;
+  ::google::protobuf::int32 image_width_;
+  ::google::protobuf::int32 image_height_;
   float time_;
   int prop_;
   float clock_;
@@ -730,7 +754,7 @@
   friend void protobuf_AssignDesc_debugger_5fmessage_2eproto();
   friend void protobuf_ShutdownFile_debugger_5fmessage_2eproto();
   
-  ::google::protobuf::uint32 _has_bits_[(21 + 31) / 32];
+  ::google::protobuf::uint32 _has_bits_[(23 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
   inline bool _has_bit(int index) const {
@@ -1070,52 +1094,84 @@
   pixel_type_ = value;
 }
 
+// optional int32 image_width = 26;
+inline bool Message::has_image_width() const {
+  return _has_bit(18);
+}
+inline void Message::clear_image_width() {
+  image_width_ = 0;
+  _clear_bit(18);
+}
+inline ::google::protobuf::int32 Message::image_width() const {
+  return image_width_;
+}
+inline void Message::set_image_width(::google::protobuf::int32 value) {
+  _set_bit(18);
+  image_width_ = value;
+}
+
+// optional int32 image_height = 27;
+inline bool Message::has_image_height() const {
+  return _has_bit(19);
+}
+inline void Message::clear_image_height() {
+  image_height_ = 0;
+  _clear_bit(19);
+}
+inline ::google::protobuf::int32 Message::image_height() const {
+  return image_height_;
+}
+inline void Message::set_image_height(::google::protobuf::int32 value) {
+  _set_bit(19);
+  image_height_ = value;
+}
+
 // optional float time = 11;
 inline bool Message::has_time() const {
-  return _has_bit(18);
+  return _has_bit(20);
 }
 inline void Message::clear_time() {
   time_ = 0;
-  _clear_bit(18);
+  _clear_bit(20);
 }
 inline float Message::time() const {
   return time_;
 }
 inline void Message::set_time(float value) {
-  _set_bit(18);
+  _set_bit(20);
   time_ = value;
 }
 
 // optional .com.android.glesv2debugger.Message.Prop prop = 21;
 inline bool Message::has_prop() const {
-  return _has_bit(19);
+  return _has_bit(21);
 }
 inline void Message::clear_prop() {
   prop_ = 0;
-  _clear_bit(19);
+  _clear_bit(21);
 }
 inline ::com::android::glesv2debugger::Message_Prop Message::prop() const {
   return static_cast< ::com::android::glesv2debugger::Message_Prop >(prop_);
 }
 inline void Message::set_prop(::com::android::glesv2debugger::Message_Prop value) {
   GOOGLE_DCHECK(::com::android::glesv2debugger::Message_Prop_IsValid(value));
-  _set_bit(19);
+  _set_bit(21);
   prop_ = value;
 }
 
 // optional float clock = 22;
 inline bool Message::has_clock() const {
-  return _has_bit(20);
+  return _has_bit(22);
 }
 inline void Message::clear_clock() {
   clock_ = 0;
-  _clear_bit(20);
+  _clear_bit(22);
 }
 inline float Message::clock() const {
   return clock_;
 }
 inline void Message::set_clock(float value) {
-  _set_bit(20);
+  _set_bit(22);
   clock_ = value;
 }
 
diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp
index 3a20e21..eb28d06 100644
--- a/opengl/libs/GLES2_dbg/src/egl.cpp
+++ b/opengl/libs/GLES2_dbg/src/egl.cpp
@@ -18,6 +18,7 @@
 
 EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
 {
+    DbgContext * const dbg = getDbgContextThreadSpecific();
     glesv2debugger::Message msg;
     struct : public FunctionCall {
         EGLDisplay dpy;
@@ -33,7 +34,21 @@
 
     msg.set_arg0(reinterpret_cast<int>(dpy));
     msg.set_arg1(reinterpret_cast<int>(draw));
-
+    if (dbg->captureSwap > 0) {
+        dbg->captureSwap--;
+        int viewport[4] = {};
+        dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+        void * pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
+                        dbg->readBytesPerPixel);
+        dbg->hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2],
+                                    viewport[3], dbg->readFormat, dbg->readType, pixels);
+        dbg->CompressReadPixelBuffer(msg.mutable_data());
+        msg.set_data_type(msg.ReferencedImage);
+        msg.set_pixel_format(dbg->readFormat);
+        msg.set_pixel_type(dbg->readType);
+        msg.set_image_width(viewport[2]);
+        msg.set_image_height(viewport[3]);
+    }
     int * ret = MessageLoop(caller, msg, glesv2debugger::Message_Function_eglSwapBuffers);
     return static_cast<EGLBoolean>(reinterpret_cast<int>(ret));
 }
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index 9218da5..f2b1fa6 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -14,6 +14,9 @@
  ** limitations under the License.
  */
 
+#ifndef ANDROID_GLES2_DBG_HEADER_H
+#define ANDROID_GLES2_DBG_HEADER_H
+
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
@@ -24,9 +27,7 @@
 
 #include <cutils/log.h>
 #include <utils/Timers.h>
-#include <../../../libcore/include/StaticAssert.h>
 
-#define EGL_TRACE 1
 #include "hooks.h"
 
 #include "glesv2dbg.h"
@@ -39,8 +40,6 @@
 using namespace android;
 using namespace com::android;
 
-#define API_ENTRY(_api) Debug_##_api
-
 #ifndef __location__
 #define __HIERALLOC_STRING_0__(s)   #s
 #define __HIERALLOC_STRING_1__(s)   __HIERALLOC_STRING_0__(s)
@@ -74,9 +73,10 @@
 };
 
 struct DbgContext {
-private:
     static const unsigned int LZF_CHUNK_SIZE = 256 * 1024;
-    char * lzf_buf; // malloc / free; for lzf chunk compression
+
+private:
+    char * lzf_buf; // malloc / free; for lzf chunk compression and other uses
 
     // used as buffer and reference frame for ReadPixels; malloc/free
     unsigned * lzf_ref [2];
@@ -84,9 +84,14 @@
     unsigned lzf_refSize, lzf_refBufSize; // bytes
 
 public:
-    const unsigned version; // 0 is GLES1, 1 is GLES2
+    const unsigned int version; // 0 is GLES1, 1 is GLES2
     const gl_hooks_t * const hooks;
-    const unsigned MAX_VERTEX_ATTRIBS;
+    const unsigned int MAX_VERTEX_ATTRIBS;
+    const GLenum readFormat, readType; // implementation supported glReadPixels
+    const unsigned int readBytesPerPixel;
+
+    unsigned int captureSwap; // number of eglSwapBuffers to glReadPixels
+    unsigned int captureDraw; // number of glDrawArrays/Elements to glReadPixels
 
     GLFunctionBitfield expectResponse;
 
@@ -119,16 +124,21 @@
     unsigned maxAttrib; // number of slots used by program
 
     DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-               const unsigned MAX_VERTEX_ATTRIBS);
+               const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
+               const GLenum readType);
     ~DbgContext();
 
     void Fetch(const unsigned index, std::string * const data) const;
     void Compress(const void * in_data, unsigned in_len, std::string * const outStr);
+    static unsigned char * Decompress(const void * in, const unsigned int inLen,
+                                      unsigned int * const outLen); // malloc/free
     void * GetReadPixelsBuffer(const unsigned size);
     bool IsReadPixelBuffer(const void * const ptr)  {
         return ptr == lzf_ref[lzf_readIndex];
     }
     void CompressReadPixelBuffer(std::string * const outStr);
+    char * GetBuffer(); // allocates lzf_buf if NULL
+    unsigned int GetBufferSize(); // allocates lzf_buf if NULL
 
     void glUseProgram(GLuint program);
     void glEnableVertexAttribArray(GLuint index);
@@ -141,9 +151,7 @@
     void glDeleteBuffers(GLsizei n, const GLuint *buffers);
 };
 
-
 DbgContext * getDbgContextThreadSpecific();
-#define DBGCONTEXT(ctx) DbgContext * const ctx = getDbgContextThreadSpecific();
 
 struct FunctionCall {
     virtual const int * operator()(gl_hooks_t::gl_t const * const _c,
@@ -152,7 +160,6 @@
 };
 
 // move these into DbgContext as static
-extern bool capture;
 extern int timeMode; // SYSTEM_TIME_
 
 extern int clientSock, serverSock;
@@ -169,3 +176,5 @@
 const int * GenerateCall(DbgContext * const dbg, const glesv2debugger::Message & cmd,
                          glesv2debugger::Message & msg, const int * const prevRet);
 }; // namespace android {
+
+#endif // #ifndef ANDROID_GLES2_DBG_HEADER_H
diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp
index 7039c84..0c711bf 100644
--- a/opengl/libs/GLES2_dbg/src/server.cpp
+++ b/opengl/libs/GLES2_dbg/src/server.cpp
@@ -28,7 +28,8 @@
 {
 
 int serverSock = -1, clientSock = -1;
-
+FILE * file = NULL;
+unsigned int MAX_FILE_SIZE = 0;
 int timeMode = SYSTEM_TIME_THREAD;
 
 static void Die(const char * msg)
@@ -38,18 +39,25 @@
     exit(1);
 }
 
-void StartDebugServer(unsigned short port)
+void StartDebugServer(const unsigned short port, const bool forceUseFile,
+                      const unsigned int maxFileSize, const char * const filePath)
 {
+    MAX_FILE_SIZE = maxFileSize;
+
     LOGD("GLESv2_dbg: StartDebugServer");
-    if (serverSock >= 0)
+    if (serverSock >= 0 || file)
         return;
 
     LOGD("GLESv2_dbg: StartDebugServer create socket");
     struct sockaddr_in server = {}, client = {};
 
     /* Create the TCP socket */
-    if ((serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
-        Die("Failed to create socket");
+    if (forceUseFile || (serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+        file = fopen(filePath, "wb");
+        if (!file)
+            Die("Failed to create socket and file");
+        else
+            return;
     }
     /* Construct the server sockaddr_in structure */
     server.sin_family = AF_INET;                  /* Internet/IP */
@@ -92,13 +100,17 @@
         close(serverSock);
         serverSock = -1;
     }
-
+    if (file) {
+        fclose(file);
+        file = NULL;
+    }
 }
 
 void Receive(glesv2debugger::Message & cmd)
 {
+    if (clientSock < 0)
+        return;
     unsigned len = 0;
-
     int received = recv(clientSock, &len, 4, MSG_WAITALL);
     if (received < 0)
         Die("Failed to receive response length");
@@ -106,7 +118,6 @@
         LOGD("received %dB: %.8X", received, len);
         Die("Received length mismatch, expected 4");
     }
-    len = ntohl(len);
     static void * buffer = NULL;
     static unsigned bufferSize = 0;
     if (bufferSize < len) {
@@ -125,6 +136,8 @@
 
 bool TryReceive(glesv2debugger::Message & cmd)
 {
+    if (clientSock < 0)
+        return false;
     fd_set readSet;
     FD_ZERO(&readSet);
     FD_SET(clientSock, &readSet);
@@ -146,14 +159,34 @@
 
 float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd)
 {
+    // TODO: use per DbgContext send/receive buffer and async socket
+    //  instead of mutex and blocking io; watch out for large messages
     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-    pthread_mutex_lock(&mutex); // TODO: this is just temporary
+    struct Autolock {
+        Autolock() {
+            pthread_mutex_lock(&mutex);
+        }
+        ~Autolock() {
+            pthread_mutex_unlock(&mutex);
+        }
+    } autolock;
 
     if (msg.function() != glesv2debugger::Message_Function_ACK)
         assert(msg.has_context_id() && msg.context_id() != 0);
     static std::string str;
     msg.SerializeToString(&str);
-    uint32_t len = htonl(str.length());
+    const uint32_t len = str.length();
+    if (clientSock < 0) {
+        if (file) {
+            fwrite(&len, sizeof(len), 1, file);
+            fwrite(str.data(), len, 1, file);
+            if (ftell(file) >= MAX_FILE_SIZE) {
+                fclose(file);
+                Die("MAX_FILE_SIZE reached");
+            }
+        }
+        return 0;
+    }
     int sent = -1;
     sent = send(clientSock, &len, sizeof(len), 0);
     if (sent != sizeof(len)) {
@@ -161,36 +194,37 @@
         Die("Failed to send message length");
     }
     nsecs_t c0 = systemTime(timeMode);
-    sent = send(clientSock, str.c_str(), str.length(), 0);
+    sent = send(clientSock, str.data(), str.length(), 0);
     float t = (float)ns2ms(systemTime(timeMode) - c0);
     if (sent != str.length()) {
         LOGD("actual sent=%d expected=%d clientSock=%d", sent, str.length(), clientSock);
         Die("Failed to send message");
     }
-
+    // TODO: factor Receive & TryReceive out and into MessageLoop, or add control argument.
+    // mean while, if server is sending a SETPROP then don't try to receive,
+    //  because server will not be processing received command
+    if (msg.function() == msg.SETPROP)
+        return t;
     // try to receive commands even though not expecting response,
-    // since client can send SETPROP commands anytime
+    //  since client can send SETPROP and other commands anytime
     if (!msg.expect_response()) {
         if (TryReceive(cmd)) {
-            LOGD("Send: TryReceived");
             if (glesv2debugger::Message_Function_SETPROP == cmd.function())
-                LOGD("Send: received SETPROP");
+                LOGD("Send: TryReceived SETPROP");
             else
-                LOGD("Send: received something else");
+                LOGD("Send: TryReceived %u", cmd.function());
         }
     } else
         Receive(cmd);
-
-    pthread_mutex_unlock(&mutex);
     return t;
 }
 
 void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd)
 {
     switch (cmd.prop()) {
-    case glesv2debugger::Message_Prop_Capture:
-        LOGD("SetProp Message_Prop_Capture %d", cmd.arg0());
-        capture = cmd.arg0();
+    case glesv2debugger::Message_Prop_CaptureDraw:
+        LOGD("SetProp Message_Prop_CaptureDraw %d", cmd.arg0());
+        dbg->captureDraw = cmd.arg0();
         break;
     case glesv2debugger::Message_Prop_TimeMode:
         LOGD("SetProp Message_Prop_TimeMode %d", cmd.arg0());
@@ -200,6 +234,10 @@
         LOGD("SetProp Message_Prop_ExpectResponse %d=%d", cmd.arg0(), cmd.arg1());
         dbg->expectResponse.Bit((glesv2debugger::Message_Function)cmd.arg0(), cmd.arg1());
         break;
+    case glesv2debugger::Message_Prop_CaptureSwap:
+        LOGD("SetProp CaptureSwap %d", cmd.arg0());
+        dbg->captureSwap = cmd.arg0();
+        break;
     default:
         assert(0);
     }
@@ -213,12 +251,17 @@
     glesv2debugger::Message cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(function);
+    bool expectResponse = dbg->expectResponse.Bit(function);
     msg.set_expect_response(expectResponse);
     msg.set_function(function);
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+
+    // when not exectResponse, set cmd to CONTINUE then SKIP
+    // cmd will be overwritten by received command
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(expectResponse);
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -233,22 +276,34 @@
             msg.set_function(function);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         case glesv2debugger::Message_Function_SKIP:
             return const_cast<int *>(ret);
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             ret = GenerateCall(dbg, cmd, msg, ret);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 471e5ad..029ee3b 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -21,74 +21,13 @@
 bool capture; // capture after each glDraw*
 }
 
-void Debug_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)
-{
-    DbgContext * const dbg = getDbgContextThreadSpecific();
-    glesv2debugger::Message msg, cmd;
-    msg.set_context_id(reinterpret_cast<int>(dbg));
-    msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glReadPixels);
-    msg.set_expect_response(expectResponse);
-    msg.set_function(glesv2debugger::Message_Function_glReadPixels);
-    msg.set_arg0(x);
-    msg.set_arg1(y);
-    msg.set_arg2(width);
-    msg.set_arg3(height);
-    msg.set_arg4(format);
-    msg.set_arg5(type);
-    msg.set_arg6(reinterpret_cast<int>(pixels));
-
-    const unsigned size = width * height * GetBytesPerPixel(format, type);
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
-    Send(msg, cmd);
-    float t = 0;
-    while (true) {
-        msg.Clear();
-        nsecs_t c0 = systemTime(timeMode);
-        switch (cmd.function()) {
-        case glesv2debugger::Message_Function_CONTINUE:
-            dbg->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels);
-            msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
-            msg.set_context_id(reinterpret_cast<int>(dbg));
-            msg.set_function(glesv2debugger::Message_Function_glReadPixels);
-            msg.set_type(glesv2debugger::Message_Type_AfterCall);
-            msg.set_expect_response(expectResponse);
-            if (dbg->IsReadPixelBuffer(pixels)) {
-                dbg->CompressReadPixelBuffer(msg.mutable_data());
-                msg.set_data_type(msg.ReferencedImage);
-            } else {
-                dbg->Compress(pixels, size, msg.mutable_data());
-                msg.set_data_type(msg.NonreferencedImage);
-            }
-            if (!expectResponse)
-                cmd.set_function(glesv2debugger::Message_Function_SKIP);
-            Send(msg, cmd);
-            break;
-        case glesv2debugger::Message_Function_SKIP:
-            return;
-        case glesv2debugger::Message_Function_SETPROP:
-            SetProp(dbg, cmd);
-            Receive(cmd);
-            break;
-        default:
-            GenerateCall(dbg, cmd, msg, NULL);
-            msg.set_expect_response(expectResponse);
-            if (!expectResponse)
-                cmd.set_function(cmd.SKIP);
-            Send(msg, cmd);
-            break;
-        }
-    }
-}
-
 void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
 {
     DbgContext * const dbg = getDbgContextThreadSpecific();
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays);
+    bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawArrays);
     msg.set_expect_response(expectResponse);
     msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
     msg.set_arg0(mode);
@@ -103,11 +42,12 @@
     }
 
     void * pixels = NULL;
-    GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(expectResponse);
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -119,33 +59,47 @@
             msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
-            if (capture) {
+            expectResponse = cmd.expect_response();
+            // TODO: pack glReadPixels data with vertex data instead of
+            //  relying on sperate call for transport, this would allow
+            //  auto generated message loop using EXTEND_Debug macro
+            if (dbg->captureDraw > 0) {
+                dbg->captureDraw--;
                 dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
 //                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
 //                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
-                                                  GetBytesPerPixel(readFormat, readType));
+                                                  dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   readFormat, readType, pixels);
+                                   dbg->readFormat, dbg->readType, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             GenerateCall(dbg, cmd, msg, NULL);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
@@ -169,7 +123,7 @@
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
-    const bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements);
+    bool expectResponse = dbg->expectResponse.Bit(glesv2debugger::Message_Function_glDrawElements);
     msg.set_expect_response(expectResponse);
     msg.set_function(glesv2debugger::Message_Function_glDrawElements);
     msg.set_arg0(mode);
@@ -195,11 +149,12 @@
         assert(0);
 
     void * pixels = NULL;
-    GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    if (!expectResponse)
-        cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    cmd.set_expect_response(expectResponse);
+    glesv2debugger::Message_Function oldCmd = cmd.function();
     Send(msg, cmd);
+    expectResponse = cmd.expect_response();
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -211,33 +166,45 @@
             msg.set_function(glesv2debugger::Message_Function_glDrawElements);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+                cmd.set_expect_response(false);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
-            if (capture) {
+            expectResponse = cmd.expect_response();
+            // TODO: pack glReadPixels data with vertex data instead of
+            //  relying on sperate call for transport, this would allow
+            //  auto generated message loop using EXTEND_Debug macro
+            if (dbg->captureDraw > 0) {
+                dbg->captureDraw--;
                 dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
-//                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
-//                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
-                                                  GetBytesPerPixel(readFormat, readType));
+                                                  dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   readFormat, readType, pixels);
+                                   dbg->readFormat, dbg->readType, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
         case glesv2debugger::Message_Function_SETPROP:
             SetProp(dbg, cmd);
-            Receive(cmd);
+            expectResponse = cmd.expect_response();
+            if (!expectResponse) // SETPROP is "out of band"
+                cmd.set_function(oldCmd);
+            else
+                Receive(cmd);
             break;
         default:
             GenerateCall(dbg, cmd, msg, NULL);
             msg.set_expect_response(expectResponse);
-            if (!expectResponse)
+            if (!expectResponse) {
                 cmd.set_function(cmd.SKIP);
+                cmd.set_expect_response(expectResponse);
+            }
+            oldCmd = cmd.function();
             Send(msg, cmd);
+            expectResponse = cmd.expect_response();
             break;
         }
     }
diff --git a/opengl/libs/GLES2_dbg/test/Android.mk b/opengl/libs/GLES2_dbg/test/Android.mk
new file mode 100644
index 0000000..14a84b4
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH) \
+    $(LOCAL_PATH)/../src \
+    $(LOCAL_PATH)/../../ \
+    external/gtest/include \
+    external/stlport/stlport \
+    external/protobuf/src \
+    bionic \
+    external \
+#
+
+LOCAL_SRC_FILES:= \
+    test_main.cpp \
+    test_server.cpp \
+    test_socket.cpp \
+#
+
+LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2_dbg libstlport
+LOCAL_STATIC_LIBRARIES := libgtest libprotobuf-cpp-2.3.0-lite liblzf
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE:= libGLESv2_dbg_test
+
+ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+    LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+endif
+ifneq ($(TARGET_SIMULATOR),true)
+    LOCAL_C_INCLUDES += bionic/libc/private
+endif
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -fvisibility=hidden
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/opengl/libs/GLES2_dbg/test/test_main.cpp b/opengl/libs/GLES2_dbg/test/test_main.cpp
new file mode 100644
index 0000000..058bea4
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/test_main.cpp
@@ -0,0 +1,234 @@
+/*
+ ** Copyright 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.
+ */
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "hooks.h"
+
+namespace
+{
+
+// The fixture for testing class Foo.
+class DbgContextTest : public ::testing::Test
+{
+protected:
+    android::DbgContext dbg;
+    gl_hooks_t hooks;
+
+    DbgContextTest()
+            : dbg(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE) {
+        // You can do set-up work for each test here.
+        hooks.gl.glGetError = GetError;
+    }
+
+    static GLenum GetError() {
+        return GL_NO_ERROR;
+    }
+
+    virtual ~DbgContextTest() {
+        // You can do clean-up work that doesn't throw exceptions here.
+    }
+
+    // If the constructor and destructor are not enough for setting up
+    // and cleaning up each test, you can define the following methods:
+
+    virtual void SetUp() {
+        // Code here will be called immediately after the constructor (right
+        // before each test).
+    }
+
+    virtual void TearDown() {
+        // Code here will be called immediately after each test (right
+        // before the destructor).
+    }
+};
+
+TEST_F(DbgContextTest, GetReadPixelBuffer)
+{
+    const unsigned int bufferSize = 512;
+    // test that it's allocating two buffers and swapping them
+    void * const buffer0 = dbg.GetReadPixelsBuffer(bufferSize);
+    ASSERT_NE((void *)NULL, buffer0);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) {
+        EXPECT_EQ(0, ((unsigned int *)buffer0)[i])
+        << "GetReadPixelsBuffer should allocate and zero";
+        ((unsigned int *)buffer0)[i] = i * 13;
+    }
+
+    void * const buffer1 = dbg.GetReadPixelsBuffer(bufferSize);
+    ASSERT_NE((void *)NULL, buffer1);
+    EXPECT_NE(buffer0, buffer1);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) {
+        EXPECT_EQ(0, ((unsigned int *)buffer1)[i])
+        << "GetReadPixelsBuffer should allocate and zero";
+        ((unsigned int *)buffer1)[i] = i * 17;
+    }
+
+    void * const buffer2 = dbg.GetReadPixelsBuffer(bufferSize);
+    EXPECT_EQ(buffer2, buffer0);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++)
+        EXPECT_EQ(i * 13, ((unsigned int *)buffer2)[i])
+        << "GetReadPixelsBuffer should swap buffers";
+
+    void * const buffer3 = dbg.GetReadPixelsBuffer(bufferSize);
+    EXPECT_EQ(buffer3, buffer1);
+    for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++)
+        EXPECT_EQ(i * 17, ((unsigned int *)buffer3)[i])
+        << "GetReadPixelsBuffer should swap buffers";
+
+    void * const buffer4 = dbg.GetReadPixelsBuffer(bufferSize);
+    EXPECT_NE(buffer3, buffer4);
+    EXPECT_EQ(buffer0, buffer2);
+    EXPECT_EQ(buffer1, buffer3);
+    EXPECT_EQ(buffer2, buffer4);
+
+    // it reallocs as necessary; 0 size may result in NULL
+    for (unsigned int i = 0; i < 42; i++) {
+        void * const buffer = dbg.GetReadPixelsBuffer(((i & 7)) << 20);
+        EXPECT_NE((void *)NULL, buffer)
+        << "should be able to get a variety of reasonable sizes";
+        EXPECT_TRUE(dbg.IsReadPixelBuffer(buffer));
+    }
+}
+
+TEST_F(DbgContextTest, CompressReadPixelBuffer)
+{
+    const unsigned int bufferSize = dbg.LZF_CHUNK_SIZE * 4 + 33;
+    std::string out;
+    unsigned char * buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        buffer[i] = i * 13;
+    dbg.CompressReadPixelBuffer(&out);
+    uint32_t decompSize = 0;
+    ASSERT_LT(12, out.length()); // at least written chunk header
+    ASSERT_EQ(bufferSize, *(uint32_t *)out.data())
+    << "total decompressed size should be as requested in GetReadPixelsBuffer";
+    for (unsigned int i = 4; i < out.length();) {
+        const uint32_t outSize = *(uint32_t *)(out.data() + i);
+        i += 4;
+        const uint32_t inSize = *(uint32_t *)(out.data() + i);
+        i += 4;
+        if (inSize == 0)
+            i += outSize; // chunk not compressed
+        else
+            i += inSize; // skip the actual compressed chunk
+        decompSize += outSize;
+    }
+    ASSERT_EQ(bufferSize, decompSize);
+    decompSize = 0;
+
+    unsigned char * decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+    ASSERT_EQ(decompSize, bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        EXPECT_EQ((unsigned char)(i * 13), decomp[i]) << "xor with 0 ref is identity";
+    free(decomp);
+
+    buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        buffer[i] = i * 13;
+    out.clear();
+    dbg.CompressReadPixelBuffer(&out);
+    decompSize = 0;
+    decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+    ASSERT_EQ(decompSize, bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        EXPECT_EQ(0, decomp[i]) << "xor with same ref is 0";
+    free(decomp);
+
+    buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        buffer[i] = i * 19;
+    out.clear();
+    dbg.CompressReadPixelBuffer(&out);
+    decompSize = 0;
+    decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+    ASSERT_EQ(decompSize, bufferSize);
+    for (unsigned int i = 0; i < bufferSize; i++)
+        EXPECT_EQ((unsigned char)(i * 13) ^ (unsigned char)(i * 19), decomp[i])
+        << "xor ref";
+    free(decomp);
+}
+
+TEST_F(DbgContextTest, UseProgram)
+{
+    static const GLuint _program = 74568;
+    static const struct Attribute {
+        const char * name;
+        GLint location;
+        GLint size;
+        GLenum type;
+    } _attributes [] = {
+        {"aaa", 2, 2, GL_FLOAT_VEC2},
+        {"bb", 6, 2, GL_FLOAT_MAT2},
+        {"c", 1, 1, GL_FLOAT},
+    };
+    static const unsigned int _attributeCount = sizeof(_attributes) / sizeof(*_attributes);
+    struct GL {
+        static void GetProgramiv(GLuint program, GLenum pname, GLint* params) {
+            EXPECT_EQ(_program, program);
+            ASSERT_NE((GLint *)NULL, params);
+            switch (pname) {
+            case GL_ACTIVE_ATTRIBUTES:
+                *params = _attributeCount;
+                return;
+            case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
+                *params = 4; // includes NULL terminator
+                return;
+            default:
+                ADD_FAILURE() << "not handled pname: " << pname;
+            }
+        }
+
+        static GLint GetAttribLocation(GLuint program, const GLchar* name) {
+            EXPECT_EQ(_program, program);
+            for (unsigned int i = 0; i < _attributeCount; i++)
+                if (!strcmp(name, _attributes[i].name))
+                    return _attributes[i].location;
+            ADD_FAILURE() << "unknown attribute name: " << name;
+            return -1;
+        }
+
+        static void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize,
+                                    GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+            EXPECT_EQ(_program, program);
+            ASSERT_LT(index, _attributeCount);
+            const Attribute & att = _attributes[index];
+            ASSERT_GE(bufsize, strlen(att.name) + 1);
+            ASSERT_NE((GLint *)NULL, size);
+            ASSERT_NE((GLenum *)NULL, type);
+            ASSERT_NE((GLchar *)NULL, name);
+            strcpy(name, att.name);
+            if (length)
+                *length = strlen(name) + 1;
+            *size = att.size;
+            *type = att.type;
+        }
+    };
+    hooks.gl.glGetProgramiv = GL::GetProgramiv;
+    hooks.gl.glGetAttribLocation = GL::GetAttribLocation;
+    hooks.gl.glGetActiveAttrib = GL::GetActiveAttrib;
+    dbg.glUseProgram(_program);
+    EXPECT_EQ(10, dbg.maxAttrib);
+    dbg.glUseProgram(0);
+    EXPECT_EQ(0, dbg.maxAttrib);
+}
+}  // namespace
+
+int main(int argc, char **argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/opengl/libs/GLES2_dbg/test/test_server.cpp b/opengl/libs/GLES2_dbg/test/test_server.cpp
new file mode 100644
index 0000000..b6401e0
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/test_server.cpp
@@ -0,0 +1,251 @@
+/*
+ ** Copyright 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.
+ */
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "egl_tls.h"
+#include "hooks.h"
+
+namespace android
+{
+extern FILE * file;
+extern unsigned int MAX_FILE_SIZE;
+extern pthread_key_t dbgEGLThreadLocalStorageKey;
+};
+
+// tmpfile fails, so need to manually make a writable file first
+static const char * filePath = "/data/local/tmp/dump.gles2dbg";
+
+class ServerFileTest : public ::testing::Test
+{
+protected:
+    ServerFileTest() { }
+
+    virtual ~ServerFileTest() { }
+
+    virtual void SetUp() {
+        MAX_FILE_SIZE = 8 << 20;
+        ASSERT_EQ((FILE *)NULL, file);
+        file = fopen("/data/local/tmp/dump.gles2dbg", "wb+");
+        ASSERT_NE((FILE *)NULL, file) << "make sure file is writable: "
+        << filePath;
+    }
+
+    virtual void TearDown() {
+        ASSERT_NE((FILE *)NULL, file);
+        fclose(file);
+        file = NULL;
+    }
+
+    void Read(glesv2debugger::Message & msg) const {
+        msg.Clear();
+        uint32_t len = 0;
+        ASSERT_EQ(sizeof(len), fread(&len, 1, sizeof(len), file));
+        ASSERT_GT(len, 0u);
+        char * buffer = new char [len];
+        ASSERT_EQ(len, fread(buffer, 1, len, file));
+        msg.ParseFromArray(buffer, len);
+        delete buffer;
+    }
+
+    void CheckNoAvailable() {
+        const long pos = ftell(file);
+        fseek(file, 0, SEEK_END);
+        EXPECT_EQ(pos, ftell(file)) << "check no available";
+    }
+};
+
+TEST_F(ServerFileTest, Send)
+{
+    glesv2debugger::Message msg, cmd, read;
+    msg.set_context_id(1);
+    msg.set_function(msg.glFinish);
+    msg.set_expect_response(false);
+    msg.set_type(msg.BeforeCall);
+    rewind(file);
+    android::Send(msg, cmd);
+    rewind(file);
+    Read(read);
+    EXPECT_EQ(msg.context_id(), read.context_id());
+    EXPECT_EQ(msg.function(), read.function());
+    EXPECT_EQ(msg.expect_response(), read.expect_response());
+    EXPECT_EQ(msg.type(), read.type());
+}
+
+TEST_F(ServerFileTest, CreateDbgContext)
+{
+    gl_hooks_t hooks;
+    struct Constant {
+        GLenum pname;
+        GLint param;
+    };
+    static const Constant constants [] = {
+        {GL_MAX_VERTEX_ATTRIBS, 16},
+        {GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 32},
+        {GL_IMPLEMENTATION_COLOR_READ_FORMAT, GL_RGBA},
+        {GL_IMPLEMENTATION_COLOR_READ_TYPE, GL_UNSIGNED_BYTE},
+    };
+    struct HookMock {
+        static void GetIntegerv(GLenum pname, GLint* params) {
+            ASSERT_TRUE(params != NULL);
+            for (unsigned int i = 0; i < sizeof(constants) / sizeof(*constants); i++)
+                if (pname == constants[i].pname) {
+                    *params = constants[i].param;
+                    return;
+                }
+            FAIL() << "GetIntegerv unknown pname: " << pname;
+        }
+        static GLenum GetError() {
+            return GL_NO_ERROR;
+        }
+    };
+    hooks.gl.glGetError = HookMock::GetError;
+    hooks.gl.glGetIntegerv = HookMock::GetIntegerv;
+    DbgContext * const dbg = CreateDbgContext(-1, 1, &hooks);
+    ASSERT_TRUE(dbg != NULL);
+    EXPECT_TRUE(dbg->vertexAttribs != NULL);
+
+    rewind(file);
+    glesv2debugger::Message read;
+    for (unsigned int i = 0; i < 2; i++) {
+        Read(read);
+        EXPECT_EQ(reinterpret_cast<int>(dbg), read.context_id());
+        EXPECT_FALSE(read.expect_response());
+        EXPECT_EQ(read.Response, read.type());
+        EXPECT_EQ(read.SETPROP, read.function());
+        EXPECT_EQ(read.GLConstant, read.prop());
+        GLint expectedConstant = 0;
+        HookMock::GetIntegerv(read.arg0(), &expectedConstant);
+        EXPECT_EQ(expectedConstant, read.arg1());
+    }
+    CheckNoAvailable();
+    DestroyDbgContext(dbg);
+}
+
+void * glNoop()
+{
+    return 0;
+}
+
+class ServerFileContextTest : public ServerFileTest
+{
+protected:
+    tls_t tls;
+    gl_hooks_t hooks;
+
+    ServerFileContextTest() { }
+
+    virtual ~ServerFileContextTest() { }
+
+    virtual void SetUp() {
+        ServerFileTest::SetUp();
+
+        if (dbgEGLThreadLocalStorageKey == -1)
+            pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
+        ASSERT_NE(-1, dbgEGLThreadLocalStorageKey);
+        tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+        ASSERT_NE((void *)NULL, tls.dbg);
+        pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls);
+        for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
+            ((void **)&hooks)[i] = reinterpret_cast<void *>(glNoop);
+    }
+
+    virtual void TearDown() {
+        ServerFileTest::TearDown();
+    }
+};
+
+TEST_F(ServerFileContextTest, MessageLoop)
+{
+    static const int arg0 = 45;
+    static const float arg7 = -87.2331f;
+    static const int arg8 = -3;
+    static const int * ret = reinterpret_cast<int *>(870);
+
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            msg.set_arg0(arg0);
+            msg.set_arg7((int &)arg7);
+            msg.set_arg8(arg8);
+            return ret;
+        }
+    } caller;
+    const int contextId = reinterpret_cast<int>(tls.dbg);
+    glesv2debugger::Message msg, read;
+
+    EXPECT_EQ(ret, MessageLoop(caller, msg, msg.glFinish));
+
+    rewind(file);
+    Read(read);
+    EXPECT_EQ(contextId, read.context_id());
+    EXPECT_EQ(read.glFinish, read.function());
+    EXPECT_EQ(false, read.expect_response());
+    EXPECT_EQ(read.BeforeCall, read.type());
+
+    Read(read);
+    EXPECT_EQ(contextId, read.context_id());
+    EXPECT_EQ(read.glFinish, read.function());
+    EXPECT_EQ(false, read.expect_response());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_TRUE(read.has_time());
+    EXPECT_EQ(arg0, read.arg0());
+    const int readArg7 = read.arg7();
+    EXPECT_EQ(arg7, (float &)readArg7);
+    EXPECT_EQ(arg8, read.arg8());
+
+    const long pos = ftell(file);
+    fseek(file, 0, SEEK_END);
+    EXPECT_EQ(pos, ftell(file))
+    << "should only write the BeforeCall and AfterCall messages";
+}
+
+TEST_F(ServerFileContextTest, DisableEnableVertexAttribArray)
+{
+    Debug_glEnableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 2); // should just ignore invalid index
+
+    glesv2debugger::Message read;
+    rewind(file);
+    Read(read);
+    EXPECT_EQ(read.glEnableVertexAttribArray, read.function());
+    EXPECT_EQ(tls.dbg->MAX_VERTEX_ATTRIBS + 2, read.arg0());
+    Read(read);
+
+    rewind(file);
+    Debug_glDisableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 4); // should just ignore invalid index
+    rewind(file);
+    Read(read);
+    Read(read);
+
+    for (unsigned int i = 0; i < tls.dbg->MAX_VERTEX_ATTRIBS; i += 5) {
+        rewind(file);
+        Debug_glEnableVertexAttribArray(i);
+        EXPECT_TRUE(tls.dbg->vertexAttribs[i].enabled);
+        rewind(file);
+        Read(read);
+        EXPECT_EQ(read.glEnableVertexAttribArray, read.function());
+        EXPECT_EQ(i, read.arg0());
+        Read(read);
+
+        rewind(file);
+        Debug_glDisableVertexAttribArray(i);
+        EXPECT_FALSE(tls.dbg->vertexAttribs[i].enabled);
+        rewind(file);
+        Read(read);
+        EXPECT_EQ(read.glDisableVertexAttribArray, read.function());
+        EXPECT_EQ(i, read.arg0());
+        Read(read);
+    }
+}
diff --git a/opengl/libs/GLES2_dbg/test/test_socket.cpp b/opengl/libs/GLES2_dbg/test/test_socket.cpp
new file mode 100644
index 0000000..617292e
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/test_socket.cpp
@@ -0,0 +1,474 @@
+/*
+ ** Copyright 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.
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "egl_tls.h"
+#include "hooks.h"
+
+namespace android
+{
+extern int serverSock, clientSock;
+extern pthread_key_t dbgEGLThreadLocalStorageKey;
+};
+
+void * glNoop();
+
+class SocketContextTest : public ::testing::Test
+{
+protected:
+    tls_t tls;
+    gl_hooks_t hooks;
+    int sock;
+    char * buffer;
+    unsigned int bufferSize;
+
+    SocketContextTest() : sock(-1) {
+    }
+
+    virtual ~SocketContextTest() {
+    }
+
+    virtual void SetUp() {
+        if (dbgEGLThreadLocalStorageKey == -1)
+            pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
+        ASSERT_NE(-1, dbgEGLThreadLocalStorageKey);
+        tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+        ASSERT_TRUE(tls.dbg != NULL);
+        pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls);
+        for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
+            ((void **)&hooks)[i] = (void *)glNoop;
+
+        int socks[2] = {-1, -1};
+        ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socks));
+        clientSock = socks[0];
+        sock = socks[1];
+
+        bufferSize = 128;
+        buffer = new char [128];
+        ASSERT_NE((char *)NULL, buffer);
+    }
+
+    virtual void TearDown() {
+        close(sock);
+        close(clientSock);
+        clientSock = -1;
+        delete buffer;
+    }
+
+    void Write(glesv2debugger::Message & msg) const {
+        msg.set_context_id((int)tls.dbg);
+        msg.set_type(msg.Response);
+        ASSERT_TRUE(msg.has_context_id());
+        ASSERT_TRUE(msg.has_function());
+        ASSERT_TRUE(msg.has_type());
+        ASSERT_TRUE(msg.has_expect_response());
+        static std::string str;
+        msg.SerializeToString(&str);
+        const uint32_t len = str.length();
+        ASSERT_EQ(sizeof(len), send(sock, &len, sizeof(len), 0));
+        ASSERT_EQ(str.length(), send(sock, str.data(), str.length(), 0));
+    }
+
+    void Read(glesv2debugger::Message & msg) {
+        int available = 0;
+        ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
+        ASSERT_GT(available, 0);
+        uint32_t len = 0;
+        ASSERT_EQ(sizeof(len), recv(sock, &len, sizeof(len), 0));
+        if (len > bufferSize) {
+            bufferSize = len;
+            buffer = new char[bufferSize];
+            ASSERT_TRUE(buffer != NULL);
+        }
+        ASSERT_EQ(len, recv(sock, buffer, len, 0));
+        msg.Clear();
+        msg.ParseFromArray(buffer, len);
+        ASSERT_TRUE(msg.has_context_id());
+        ASSERT_TRUE(msg.has_function());
+        ASSERT_TRUE(msg.has_type());
+        ASSERT_TRUE(msg.has_expect_response());
+    }
+
+    void CheckNoAvailable() {
+        int available = 0;
+        ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
+        ASSERT_EQ(available, 0);
+    }
+};
+
+TEST_F(SocketContextTest, MessageLoopSkip)
+{
+    static const int arg0 = 45;
+    static const float arg7 = -87.2331f;
+    static const int arg8 = -3;
+    static const int * ret = (int *)870;
+
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            msg.set_arg0(arg0);
+            msg.set_arg7((int &)arg7);
+            msg.set_arg8(arg8);
+            return ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    tls.dbg->expectResponse.Bit(msg.glFinish, true);
+
+    cmd.set_function(cmd.SKIP);
+    cmd.set_expect_response(false);
+    Write(cmd);
+
+    EXPECT_NE(ret, MessageLoop(caller, msg, msg.glFinish));
+
+    Read(read);
+    EXPECT_EQ(read.glFinish, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_NE(arg0, read.arg0());
+    EXPECT_NE((int &)arg7, read.arg7());
+    EXPECT_NE(arg8, read.arg8());
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopContinue)
+{
+    static const int arg0 = GL_FRAGMENT_SHADER;
+    static const int ret = -342;
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            msg.set_ret(ret);
+            return (int *)ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    tls.dbg->expectResponse.Bit(msg.glCreateShader, true);
+
+    cmd.set_function(cmd.CONTINUE);
+    cmd.set_expect_response(false); // MessageLoop should automatically skip after continue
+    Write(cmd);
+
+    msg.set_arg0(arg0);
+    EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateShader));
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_EQ(arg0, read.arg0());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_EQ(ret, read.ret());
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopGenerateCall)
+{
+    static const int ret = -342;
+    static unsigned int createShader, createProgram;
+    createShader = 0;
+    createProgram = 0;
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            const int r = (int)_c->glCreateProgram();
+            msg.set_ret(r);
+            return (int *)r;
+        }
+        static GLuint CreateShader(const GLenum type) {
+            createShader++;
+            return type;
+        }
+        static GLuint CreateProgram() {
+            createProgram++;
+            return ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glCreateShader = caller.CreateShader;
+    hooks.gl.glCreateProgram = caller.CreateProgram;
+    tls.dbg->expectResponse.Bit(msg.glCreateProgram, true);
+
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_FRAGMENT_SHADER);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.Clear();
+    cmd.set_function(cmd.CONTINUE);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_VERTEX_SHADER);
+    cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
+    Write(cmd);
+
+    EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_EQ(ret, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
+
+    EXPECT_EQ(2, createShader);
+    EXPECT_EQ(1, createProgram);
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopSetProp)
+{
+    static const int ret = -342;
+    static unsigned int createShader, createProgram;
+    createShader = 0;
+    createProgram = 0;
+    struct Caller : public FunctionCall {
+        const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+            const int r = (int)_c->glCreateProgram();
+            msg.set_ret(r);
+            return (int *)r;
+        }
+        static GLuint CreateShader(const GLenum type) {
+            createShader++;
+            return type;
+        }
+        static GLuint CreateProgram() {
+            createProgram++;
+            return ret;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glCreateShader = caller.CreateShader;
+    hooks.gl.glCreateProgram = caller.CreateProgram;
+    tls.dbg->expectResponse.Bit(msg.glCreateProgram, false);
+
+    cmd.set_function(cmd.SETPROP);
+    cmd.set_prop(cmd.ExpectResponse);
+    cmd.set_arg0(cmd.glCreateProgram);
+    cmd.set_arg1(true);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.Clear();
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_FRAGMENT_SHADER);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.set_function(cmd.SETPROP);
+    cmd.set_prop(cmd.CaptureDraw);
+    cmd.set_arg0(819);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.Clear();
+    cmd.set_function(cmd.CONTINUE);
+    cmd.set_expect_response(true);
+    Write(cmd);
+
+    cmd.set_function(cmd.glCreateShader);
+    cmd.set_arg0(GL_VERTEX_SHADER);
+    cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
+    Write(cmd);
+
+    EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
+
+    EXPECT_TRUE(tls.dbg->expectResponse.Bit(msg.glCreateProgram));
+    EXPECT_EQ(819, tls.dbg->captureDraw);
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateProgram, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+    EXPECT_EQ(ret, read.ret());
+
+    Read(read);
+    EXPECT_EQ(read.glCreateShader, read.function());
+    EXPECT_EQ(read.AfterGeneratedCall, read.type());
+    EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
+
+    EXPECT_EQ(2, createShader);
+    EXPECT_EQ(1, createProgram);
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, TexImage2D)
+{
+    static const GLenum _target = GL_TEXTURE_2D;
+    static const GLint _level = 1, _internalformat = GL_RGBA;
+    static const GLsizei _width = 2, _height = 2;
+    static const GLint _border = 333;
+    static const GLenum _format = GL_RGB, _type = GL_UNSIGNED_SHORT_5_6_5;
+    static const short _pixels [_width * _height] = {11, 22, 33, 44};
+    static unsigned int texImage2D;
+    texImage2D = 0;
+
+    struct Caller {
+        static void TexImage2D(GLenum target, GLint level, GLint internalformat,
+                               GLsizei width, GLsizei height, GLint border,
+                               GLenum format, GLenum type, const GLvoid* pixels) {
+            EXPECT_EQ(_target, target);
+            EXPECT_EQ(_level, level);
+            EXPECT_EQ(_internalformat, internalformat);
+            EXPECT_EQ(_width, width);
+            EXPECT_EQ(_height, height);
+            EXPECT_EQ(_border, border);
+            EXPECT_EQ(_format, format);
+            EXPECT_EQ(_type, type);
+            EXPECT_EQ(0, memcmp(_pixels, pixels, sizeof(_pixels)));
+            texImage2D++;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glTexImage2D = caller.TexImage2D;
+    tls.dbg->expectResponse.Bit(msg.glTexImage2D, false);
+
+    Debug_glTexImage2D(_target, _level, _internalformat, _width, _height, _border,
+                       _format, _type, _pixels);
+    EXPECT_EQ(1, texImage2D);
+
+    Read(read);
+    EXPECT_EQ(read.glTexImage2D, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_EQ(_target, read.arg0());
+    EXPECT_EQ(_level, read.arg1());
+    EXPECT_EQ(_internalformat, read.arg2());
+    EXPECT_EQ(_width, read.arg3());
+    EXPECT_EQ(_height, read.arg4());
+    EXPECT_EQ(_border, read.arg5());
+    EXPECT_EQ(_format, read.arg6());
+    EXPECT_EQ(_type, read.arg7());
+
+    EXPECT_TRUE(read.has_data());
+    uint32_t dataLen = 0;
+    const unsigned char * data = tls.dbg->Decompress(read.data().data(),
+                                 read.data().length(), &dataLen);
+    EXPECT_EQ(sizeof(_pixels), dataLen);
+    if (sizeof(_pixels) == dataLen)
+        EXPECT_EQ(0, memcmp(_pixels, data, sizeof(_pixels)));
+
+    Read(read);
+    EXPECT_EQ(read.glTexImage2D, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+
+    CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, CopyTexImage2D)
+{
+    static const GLenum _target = GL_TEXTURE_2D;
+    static const GLint _level = 1, _internalformat = GL_RGBA;
+    static const GLint _x = 9, _y = 99;
+    static const GLsizei _width = 2, _height = 3;
+    static const GLint _border = 333;
+    static const int _pixels [_width * _height] = {11, 22, 33, 44, 55, 66};
+    static unsigned int copyTexImage2D, readPixels;
+    copyTexImage2D = 0, readPixels = 0;
+
+    struct Caller {
+        static void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
+                                   GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
+            EXPECT_EQ(_target, target);
+            EXPECT_EQ(_level, level);
+            EXPECT_EQ(_internalformat, internalformat);
+            EXPECT_EQ(_x, x);
+            EXPECT_EQ(_y, y);
+            EXPECT_EQ(_width, width);
+            EXPECT_EQ(_height, height);
+            EXPECT_EQ(_border, border);
+            copyTexImage2D++;
+        }
+        static void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+                               GLenum format, GLenum type, GLvoid* pixels) {
+            EXPECT_EQ(_x, x);
+            EXPECT_EQ(_y, y);
+            EXPECT_EQ(_width, width);
+            EXPECT_EQ(_height, height);
+            EXPECT_EQ(GL_RGBA, format);
+            EXPECT_EQ(GL_UNSIGNED_BYTE, type);
+            ASSERT_TRUE(pixels != NULL);
+            memcpy(pixels, _pixels, sizeof(_pixels));
+            readPixels++;
+        }
+    } caller;
+    glesv2debugger::Message msg, read, cmd;
+    hooks.gl.glCopyTexImage2D = caller.CopyTexImage2D;
+    hooks.gl.glReadPixels = caller.ReadPixels;
+    tls.dbg->expectResponse.Bit(msg.glCopyTexImage2D, false);
+
+    Debug_glCopyTexImage2D(_target, _level, _internalformat, _x, _y, _width, _height,
+                           _border);
+    ASSERT_EQ(1, copyTexImage2D);
+    ASSERT_EQ(1, readPixels);
+
+    Read(read);
+    EXPECT_EQ(read.glCopyTexImage2D, read.function());
+    EXPECT_EQ(read.BeforeCall, read.type());
+    EXPECT_EQ(_target, read.arg0());
+    EXPECT_EQ(_level, read.arg1());
+    EXPECT_EQ(_internalformat, read.arg2());
+    EXPECT_EQ(_x, read.arg3());
+    EXPECT_EQ(_y, read.arg4());
+    EXPECT_EQ(_width, read.arg5());
+    EXPECT_EQ(_height, read.arg6());
+    EXPECT_EQ(_border, read.arg7());
+
+    EXPECT_TRUE(read.has_data());
+    EXPECT_EQ(read.ReferencedImage, read.data_type());
+    EXPECT_EQ(GL_RGBA, read.pixel_format());
+    EXPECT_EQ(GL_UNSIGNED_BYTE, read.pixel_type());
+    uint32_t dataLen = 0;
+    unsigned char * const data = tls.dbg->Decompress(read.data().data(),
+                                 read.data().length(), &dataLen);
+    ASSERT_EQ(sizeof(_pixels), dataLen);
+    for (unsigned i = 0; i < sizeof(_pixels) / sizeof(*_pixels); i++)
+        EXPECT_EQ(_pixels[i], ((const int *)data)[i]) << "xor with 0 ref is identity";
+    free(data);
+
+    Read(read);
+    EXPECT_EQ(read.glCopyTexImage2D, read.function());
+    EXPECT_EQ(read.AfterCall, read.type());
+
+    CheckNoAvailable();
+}
diff --git a/opengl/libs/egl_tls.h b/opengl/libs/egl_tls.h
new file mode 100644
index 0000000..087989a
--- /dev/null
+++ b/opengl/libs/egl_tls.h
@@ -0,0 +1,40 @@
+/*
+ ** Copyright 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.
+ */
+
+#ifndef ANDROID_EGL_TLS_H
+#define ANDROID_EGL_TLS_H
+
+#include <EGL/egl.h>
+
+#include "glesv2dbg.h"
+
+namespace android
+{
+struct tls_t {
+    tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE), dbg(0) { }
+    ~tls_t() {
+        if (dbg)
+            DestroyDbgContext(dbg);
+    }
+
+    EGLint      error;
+    EGLContext  ctx;
+    EGLBoolean  logCallWithNoContext;
+    DbgContext* dbg;
+};
+}
+
+#endif
diff --git a/opengl/libs/glesv2dbg.h b/opengl/libs/glesv2dbg.h
index 8029dce..ee2c011 100644
--- a/opengl/libs/glesv2dbg.h
+++ b/opengl/libs/glesv2dbg.h
@@ -13,20 +13,27 @@
  ** See the License for the specific language governing permissions and
  ** limitations under the License.
  */
- 
+
 #ifndef _GLESV2_DBG_H_
 #define _GLESV2_DBG_H_
 
+#include <pthread.h>
+
 namespace android
 {
-    struct DbgContext;
-    
-    DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks);
-    void DestroyDbgContext(DbgContext * const dbg);
-    
-    void StartDebugServer(unsigned short port); // create and bind socket if haven't already
-    void StopDebugServer(); // close socket if open
-    
+struct DbgContext;
+
+DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
+                              const unsigned version, const gl_hooks_t * const hooks);
+
+void DestroyDbgContext(DbgContext * const dbg);
+
+// create and bind socket if haven't already, if failed to create socket or
+//  forceUseFile, then open /data/local/tmp/dump.gles2dbg, exit when size reached
+void StartDebugServer(const unsigned short port, const bool forceUseFile,
+                      const unsigned int maxFileSize, const char * const filePath);
+void StopDebugServer(); // close socket if open
+
 }; // namespace android
 
 #endif // #ifndef _GLESV2_DBG_H_
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 64cff96..a774841 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -93,7 +93,11 @@
 int DisplayHardware::getHeight() const          { return mHeight; }
 PixelFormat DisplayHardware::getFormat() const  { return mFormat; }
 uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; }
-uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; }
+
+uint32_t DisplayHardware::getMaxViewportDims() const {
+    return mMaxViewportDims[0] < mMaxViewportDims[1] ?
+            mMaxViewportDims[0] : mMaxViewportDims[1];
+}
 
 void DisplayHardware::init(uint32_t dpy)
 {
@@ -228,7 +232,7 @@
             eglQueryString(display, EGL_EXTENSIONS));
 
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
-    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
+    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
 
 
 #ifdef EGL_ANDROID_swap_rectangle
@@ -260,7 +264,7 @@
     LOGI("version   : %s", extensions.getVersion());
     LOGI("extensions: %s", extensions.getExtension());
     LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
-    LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
+    LOGI("GL_MAX_VIEWPORT_DIMS = %d x %d", mMaxViewportDims[0], mMaxViewportDims[1]);
     LOGI("flags = %08x", mFlags);
 
     // Unbind the context from this thread
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index ee7a2af..cdf89fd 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -108,7 +108,7 @@
     PixelFormat     mFormat;
     uint32_t        mFlags;
     mutable uint32_t mPageFlipCount;
-    GLint           mMaxViewportDims;
+    GLint           mMaxViewportDims[2];
     GLint           mMaxTextureSize;
     
     HWComposer*     mHwc;