Merge "BufferQueue returns proper code on acquire"
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 25505f8..4556505 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -383,7 +383,7 @@
     }
 
     /* create a new, empty traces.txt file to receive stack dumps */
-    int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666);  /* -rw-rw-rw- */
+    int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
     if (fd < 0) {
         fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
         return NULL;
diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp
index d532296..f7d8abe 100644
--- a/libs/utils/Trace.cpp
+++ b/libs/utils/Trace.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "Trace"
+
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index ca11863..2d41aa7 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -248,7 +248,6 @@
 typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void);
 #endif
 
-
 /* EGL_ANDROID_blob_cache
  */
 #ifndef EGL_ANDROID_blob_cache
@@ -263,6 +262,14 @@
         EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
 #endif
 
+/* EGL_IMG_hibernate_process
+ */
+#ifndef EGL_IMG_hibernate_process
+#define EGL_IMG_hibernate_process 1
+typedef EGLBoolean (EGLAPIENTRYP PFEGLHIBERNATEPROCESSIMGPROC)(void);
+typedef EGLBoolean (EGLAPIENTRYP PFEGLAWAKENPROCESSIMGPROC)(void);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index e8a14d8..b658240 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -203,27 +203,27 @@
 
 // ----------------------------------------------------------------------------
 
-egl_display_t* validate_display(EGLDisplay dpy) {
-    egl_display_t * const dp = get_display(dpy);
+egl_display_ptr validate_display(EGLDisplay dpy) {
+    egl_display_ptr dp = get_display(dpy);
     if (!dp)
-        return setError(EGL_BAD_DISPLAY, (egl_display_t*)NULL);
+        return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL));
     if (!dp->isReady())
-        return setError(EGL_NOT_INITIALIZED, (egl_display_t*)NULL);
+        return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL));
 
     return dp;
 }
 
-egl_connection_t* validate_display_config(EGLDisplay dpy, EGLConfig,
-        egl_display_t const*& dp) {
-    dp = validate_display(dpy);
+egl_display_ptr validate_display_connection(EGLDisplay dpy,
+        egl_connection_t*& cnx) {
+    cnx = NULL;
+    egl_display_ptr dp = validate_display(dpy);
     if (!dp)
-        return (egl_connection_t*) NULL;
-
-    egl_connection_t* const cnx = &gEGLImpl;
+        return dp;
+    cnx = &gEGLImpl;
     if (cnx->dso == 0) {
-        return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
+        return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL));
     }
-    return cnx;
+    return dp;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 8bfa16d..1bc4eb7 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -133,7 +133,7 @@
 {
     clearError();
 
-    egl_display_t * const dp = get_display(dpy);
+    egl_display_ptr dp = get_display(dpy);
     if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
     EGLBoolean res = dp->initialize(major, minor);
@@ -149,7 +149,7 @@
 
     clearError();
 
-    egl_display_t* const dp = get_display(dpy);
+    egl_display_ptr dp = get_display(dpy);
     if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
     EGLBoolean res = dp->terminate();
@@ -167,7 +167,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     if (num_config==0) {
@@ -192,7 +192,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     if (num_config==0) {
@@ -215,9 +215,9 @@
 {
     clearError();
 
-    egl_display_t const* dp = 0;
-    egl_connection_t* cnx = validate_display_config(dpy, config, dp);
-    if (!cnx) return EGL_FALSE;
+    egl_connection_t* cnx = NULL;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (!dp) return EGL_FALSE;
     
     return cnx->egl.eglGetConfigAttrib(
             dp->disp.dpy, config, attribute, value);
@@ -233,9 +233,9 @@
 {
     clearError();
 
-    egl_display_t const* dp = 0;
-    egl_connection_t* cnx = validate_display_config(dpy, config, dp);
-    if (cnx) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
         EGLDisplay iDpy = dp->disp.dpy;
         EGLint format;
 
@@ -267,7 +267,8 @@
         EGLSurface surface = cnx->egl.eglCreateWindowSurface(
                 iDpy, config, window, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, window, surface, cnx);
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, window,
+                    surface, cnx);
             return s;
         }
 
@@ -284,13 +285,14 @@
 {
     clearError();
 
-    egl_display_t const* dp = 0;
-    egl_connection_t* cnx = validate_display_config(dpy, config, dp);
-    if (cnx) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
         EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
                 dp->disp.dpy, config, pixmap, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
+                    surface, cnx);
             return s;
         }
     }
@@ -302,13 +304,14 @@
 {
     clearError();
 
-    egl_display_t const* dp = 0;
-    egl_connection_t* cnx = validate_display_config(dpy, config, dp);
-    if (cnx) {
+    egl_connection_t* cnx = NULL;
+    egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dp) {
         EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
                 dp->disp.dpy, config, attrib_list);
         if (surface != EGL_NO_SURFACE) {
-            egl_surface_t* s = new egl_surface_t(dpy, config, NULL, surface, cnx);
+            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
+                    surface, cnx);
             return s;
         }
     }
@@ -319,10 +322,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -339,10 +342,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -355,12 +358,12 @@
     ATRACE_CALL();
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) {
         return;
     }
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get()) {
         setError(EGL_BAD_SURFACE, EGL_FALSE);
         return;
@@ -381,9 +384,9 @@
 {
     clearError();
 
-    egl_display_t const* dp = 0;
-    egl_connection_t* cnx = validate_display_config(dpy, config, dp);
-    if (cnx) {
+    egl_connection_t* cnx = NULL;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (dpy) {
         if (share_list != EGL_NO_CONTEXT) {
             egl_context_t* const c = get_context(share_list);
             share_list = c->context;
@@ -406,7 +409,8 @@
                     }
                 };
             }
-            egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
+            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
+                    version);
 #if EGL_TRACE
             if (gEGLDebugLevel > 0)
                 GLTrace_eglCreateContext(version, c);
@@ -421,11 +425,11 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp)
         return EGL_FALSE;
 
-    ContextRef _c(dp, ctx);
+    ContextRef _c(dp.get(), ctx);
     if (!_c.get())
         return setError(EGL_BAD_CONTEXT, EGL_FALSE);
     
@@ -442,7 +446,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = get_display(dpy);
+    egl_display_ptr dp = validate_display(dpy);
     if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
     // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
@@ -454,9 +458,9 @@
     }
 
     // get a reference to the object passed in
-    ContextRef _c(dp, ctx);
-    SurfaceRef _d(dp, draw);
-    SurfaceRef _r(dp, read);
+    ContextRef _c(dp.get(), ctx);
+    SurfaceRef _d(dp.get(), draw);
+    SurfaceRef _r(dp.get(), read);
 
     // validate the context (if not EGL_NO_CONTEXT)
     if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
@@ -506,7 +510,7 @@
     }
 
 
-    EGLBoolean result = const_cast<egl_display_t*>(dp)->makeCurrent(c, cur_c,
+    EGLBoolean result = dp->makeCurrent(c, cur_c,
             draw, read, ctx,
             impl_draw, impl_read, impl_ctx);
 
@@ -538,10 +542,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    ContextRef _c(dp, ctx);
+    ContextRef _c(dp.get(), ctx);
     if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
 
     egl_context_t * const c = get_context(ctx);
@@ -645,9 +649,11 @@
         return  NULL;
     }
 
-    // The EGL_ANDROID_blob_cache extension should not be exposed to
-    // applications.  It is used internally by the Android EGL layer.
-    if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID")) {
+    // These extensions should not be exposed to applications. They're used
+    // internally by the Android EGL layer.
+    if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID") ||
+        !strcmp(procname, "eglHibernateProcessIMG") ||
+        !strcmp(procname, "eglAwakenProcessIMG")) {
         return NULL;
     }
 
@@ -715,15 +721,78 @@
     return addr;
 }
 
+class FrameCompletionThread : public Thread {
+public:
+
+    static void queueSync(EGLSyncKHR sync) {
+        static sp<FrameCompletionThread> thread(new FrameCompletionThread);
+        static bool running = false;
+        if (!running) {
+            thread->run("GPUFrameCompletion");
+            running = true;
+        }
+        {
+            Mutex::Autolock lock(thread->mMutex);
+            ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d",
+                    thread->mFramesQueued).string());
+            thread->mQueue.push_back(sync);
+            thread->mCondition.signal();
+            thread->mFramesQueued++;
+            ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size());
+        }
+    }
+
+private:
+    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {}
+
+    virtual bool threadLoop() {
+        EGLSyncKHR sync;
+        uint32_t frameNum;
+        {
+            Mutex::Autolock lock(mMutex);
+            while (mQueue.isEmpty()) {
+                mCondition.wait(mMutex);
+            }
+            sync = mQueue[0];
+            frameNum = mFramesCompleted;
+        }
+        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        {
+            ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d",
+                    frameNum).string());
+            EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+            if (result == EGL_FALSE) {
+                ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+            } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+                ALOGE("FrameCompletion: timeout waiting for fence");
+            }
+            eglDestroySyncKHR(dpy, sync);
+        }
+        {
+            Mutex::Autolock lock(mMutex);
+            mQueue.removeAt(0);
+            mFramesCompleted++;
+            ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+        }
+        return true;
+    }
+
+    uint32_t mFramesQueued;
+    uint32_t mFramesCompleted;
+    Vector<EGLSyncKHR> mQueue;
+    Condition mCondition;
+    Mutex mMutex;
+};
+
 EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
 {
     ATRACE_CALL();
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, draw);
+    SurfaceRef _s(dp.get(), draw);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -744,7 +813,19 @@
         }
     }
 
-    return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    EGLBoolean result = s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+
+    if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+        EGLSyncKHR sync = EGL_NO_SYNC_KHR;
+        {
+            sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+        }
+        if (sync != EGL_NO_SYNC_KHR) {
+            FrameCompletionThread::queueSync(sync);
+        }
+    }
+
+    return result;
 }
 
 EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
@@ -752,10 +833,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -767,7 +848,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return (const char *) NULL;
 
     switch (name) {
@@ -795,10 +876,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -815,10 +896,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -835,10 +916,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -854,7 +935,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean res = EGL_TRUE;
@@ -948,9 +1029,9 @@
 {
     clearError();
 
-    egl_display_t const* dp = 0;
-    egl_connection_t* cnx = validate_display_config(dpy, config, dp);
-    if (!cnx) return EGL_FALSE;
+    egl_connection_t* cnx = NULL;
+    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+    if (!dp) return EGL_FALSE;
     if (cnx->egl.eglCreatePbufferFromClientBuffer) {
         return cnx->egl.eglCreatePbufferFromClientBuffer(
                 dp->disp.dpy, buftype, buffer, config, attrib_list);
@@ -967,10 +1048,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -986,10 +1067,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
-    SurfaceRef _s(dp, surface);
+    SurfaceRef _s(dp.get(), surface);
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
@@ -1005,10 +1086,10 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_NO_IMAGE_KHR;
 
-    ContextRef _c(dp, ctx);
+    ContextRef _c(dp.get(), ctx);
     egl_context_t * const c = _c.get();
 
     EGLImageKHR result = EGL_NO_IMAGE_KHR;
@@ -1026,7 +1107,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     egl_connection_t* const cnx = &gEGLImpl;
@@ -1045,7 +1126,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_NO_SYNC_KHR;
 
     EGLSyncKHR result = EGL_NO_SYNC_KHR;
@@ -1060,7 +1141,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
@@ -1076,7 +1157,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
@@ -1093,7 +1174,7 @@
 {
     clearError();
 
-    egl_display_t const * const dp = validate_display(dpy);
+    const egl_display_ptr dp = validate_display(dpy);
     if (!dp) return EGL_FALSE;
 
     EGLBoolean result = EGL_FALSE;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index b80afd6..2e08f24 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -14,6 +14,8 @@
  ** limitations under the License.
  */
 
+#define __STDC_LIMIT_MACROS 1
+
 #include <string.h>
 
 #include "egl_cache.h"
@@ -57,6 +59,7 @@
 // extensions not exposed to applications but used by the ANDROID system
 //      "EGL_ANDROID_recordable "               // mandatory
 //      "EGL_ANDROID_blob_cache "               // strongly recommended
+//      "EGL_IMG_hibernate_process "            // optional
 
 extern void initEglTraceLevel();
 extern void initEglDebugLevel();
@@ -67,7 +70,8 @@
 egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
 
 egl_display_t::egl_display_t() :
-    magic('_dpy'), finishOnSwap(false), refs(0) {
+    magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0),
+    mWakeCount(0), mHibernating(false), mAttemptHibernation(false) {
 }
 
 egl_display_t::~egl_display_t() {
@@ -239,6 +243,11 @@
         finishOnSwap = true;
     }
 
+    property_get("debug.egl.traceGpuCompletion", value, "0");
+    if (atoi(value)) {
+        traceGpuCompletion = true;
+    }
+
     refs++;
     if (major != NULL)
         *major = VERSION_MAJOR;
@@ -341,12 +350,18 @@
                     disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 c->onMakeCurrent(draw, read);
+                if (!cur_c) {
+                    mWakeCount++;
+                    mAttemptHibernation = false;
+                }
             }
         } else {
             result = cur_c->cnx->egl.eglMakeCurrent(
                     disp.dpy, impl_draw, impl_read, impl_ctx);
             if (result == EGL_TRUE) {
                 cur_c->onLooseCurrent();
+                mWakeCount--;
+                mAttemptHibernation = true;
             }
         }
     }
@@ -363,6 +378,52 @@
     return result;
 }
 
+bool egl_display_t::enter() {
+    Mutex::Autolock _l(lock);
+    ALOGE_IF(mWakeCount < 0 || mWakeCount == INT32_MAX,
+             "Invalid WakeCount (%d) on enter\n", mWakeCount);
+    mWakeCount++;
+    if (CC_UNLIKELY(mHibernating)) {
+        ALOGV("Awakening\n");
+        egl_connection_t* const cnx = &gEGLImpl;
+        if (!cnx->egl.eglAwakenProcessIMG()) {
+            ALOGE("Failed to awaken EGL implementation\n");
+            return false;
+        }
+        mHibernating = false;
+    }
+    return true;
+}
+
+void egl_display_t::leave() {
+    Mutex::Autolock _l(lock);
+    ALOGE_IF(mWakeCount <= 0, "Invalid WakeCount (%d) on leave\n", mWakeCount);
+    if (--mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
+        egl_connection_t* const cnx = &gEGLImpl;
+        mAttemptHibernation = false;
+        if (cnx->egl.eglHibernateProcessIMG && cnx->egl.eglAwakenProcessIMG) {
+            ALOGV("Hibernating\n");
+            if (!cnx->egl.eglHibernateProcessIMG()) {
+                ALOGE("Failed to hibernate EGL implementation\n");
+                return;
+            }
+            mHibernating = true;
+        }
+    }
+}
+
+void egl_display_t::onWindowSurfaceCreated() {
+    Mutex::Autolock _l(lock);
+    mWakeCount++;
+    mAttemptHibernation = false;
+}
+
+void egl_display_t::onWindowSurfaceDestroyed() {
+    Mutex::Autolock _l(lock);
+    mWakeCount--;
+    mAttemptHibernation = true;
+}
+
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 43738ea..28607da 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -27,6 +27,7 @@
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 
+#include <cutils/compiler.h>
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 #include <utils/String8.h>
@@ -69,6 +70,16 @@
     // add reference to this object. returns true if this is a valid object.
     bool getObject(egl_object_t* object) const;
 
+    // These notifications allow the display to keep track of how many window
+    // surfaces exist, which it uses to decide whether to hibernate the
+    // underlying EGL implementation. They can be called by any thread without
+    // holding a lock, but must be called via egl_display_ptr to ensure
+    // proper hibernate/wakeup sequencing. If a surface destruction triggers
+    // hibernation, hibernation will be delayed at least until the calling
+    // thread's egl_display_ptr is destroyed.
+    void onWindowSurfaceCreated();
+    void onWindowSurfaceDestroyed();
+
     static egl_display_t* get(EGLDisplay dpy);
     static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
 
@@ -107,9 +118,14 @@
 
 public:
     DisplayImpl     disp;
-    bool    finishOnSwap;
+    bool    finishOnSwap;       // property: debug.egl.finish
+    bool    traceGpuCompletion; // property: debug.egl.traceGpuCompletion
 
 private:
+    friend class egl_display_ptr;
+    bool enter();
+    void leave();
+
             uint32_t                    refs;
     mutable Mutex                       lock;
             SortedVector<egl_object_t*> objects;
@@ -117,19 +133,81 @@
             String8 mVersionString;
             String8 mClientApiString;
             String8 mExtensionString;
+            int32_t mWakeCount;
+            bool    mHibernating;
+            bool    mAttemptHibernation;
 };
 
 // ----------------------------------------------------------------------------
 
-inline egl_display_t* get_display(EGLDisplay dpy) {
+// An egl_display_ptr is a kind of smart pointer for egl_display_t objects.
+// It doesn't refcount the egl_display_t, but does ensure that the underlying
+// EGL implementation is "awake" (not hibernating) and ready for use as long
+// as the egl_display_ptr exists.
+class egl_display_ptr {
+public:
+    explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {
+        if (mDpy) {
+            if (CC_UNLIKELY(!mDpy->enter())) {
+                mDpy = NULL;
+            }
+        }
+    }
+
+    // We only really need a C++11 move constructor, not a copy constructor.
+    // A move constructor would save an enter()/leave() pair on every EGL API
+    // call. But enabling -std=c++0x causes lots of errors elsewhere, so I
+    // can't use a move constructor until those are cleaned up.
+    //
+    // egl_display_ptr(egl_display_ptr&& other) {
+    //     mDpy = other.mDpy;
+    //     other.mDpy = NULL;
+    // }
+    //
+    egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {
+        if (mDpy) {
+            mDpy->enter();
+        }
+    }
+
+    ~egl_display_ptr() {
+        if (mDpy) {
+            mDpy->leave();
+        }
+    }
+
+    const egl_display_t* operator->() const { return mDpy; }
+          egl_display_t* operator->()       { return mDpy; }
+
+    const egl_display_t* get() const { return mDpy; }
+          egl_display_t* get()       { return mDpy; }
+
+    operator bool() const { return mDpy != NULL; }
+
+private:
+    egl_display_t* mDpy;
+
+    // non-assignable
+    egl_display_ptr& operator=(const egl_display_ptr&);
+};
+
+// ----------------------------------------------------------------------------
+
+inline egl_display_ptr get_display(EGLDisplay dpy) {
+    return egl_display_ptr(egl_display_t::get(dpy));
+}
+
+// Does not ensure EGL is unhibernated. Use with caution: calls into the
+// underlying EGL implementation are not safe.
+inline egl_display_t* get_display_nowake(EGLDisplay dpy) {
     return egl_display_t::get(dpy);
 }
 
 // ----------------------------------------------------------------------------
 
-egl_display_t* validate_display(EGLDisplay dpy);
-egl_connection_t* validate_display_config(EGLDisplay dpy,
-        EGLConfig config, egl_display_t const*& dp);
+egl_display_ptr validate_display(EGLDisplay dpy);
+egl_display_ptr validate_display_connection(EGLDisplay dpy,
+        egl_connection_t*& cnx);
 EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx);
 EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface);
 
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index bdd2a7e..9feb716 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -67,3 +67,8 @@
 
 EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
 EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
+
+/* IMG extensions */
+
+EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
+EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
\ No newline at end of file
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index d0cbb31..aaa5e72 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -63,12 +63,33 @@
 
 // ----------------------------------------------------------------------------
 
+egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config,
+        EGLNativeWindowType win, EGLSurface surface,
+        egl_connection_t const* cnx) :
+    egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx)
+{
+    if (win) {
+        getDisplay()->onWindowSurfaceCreated();
+    }
+}
+
+egl_surface_t::~egl_surface_t() {
+    ANativeWindow* const window = win.get();
+    if (window != NULL) {
+        native_window_set_buffers_format(window, 0);
+        if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
+            ALOGW("EGLNativeWindowType %p disconnect failed", window);
+        }
+        getDisplay()->onWindowSurfaceDestroyed();
+    }
+}
+
+// ----------------------------------------------------------------------------
+
 egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
         egl_connection_t const* cnx, int version) :
-    egl_object_t(get_display(dpy)), dpy(dpy), context(context),
-            config(config), read(0), draw(0), cnx(cnx),
-            version(version)
-{
+    egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
+            config(config), read(0), draw(0), cnx(cnx), version(version) {
 }
 
 void egl_context_t::onLooseCurrent() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 4d91f54..2a08424 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -127,24 +127,14 @@
 
 class egl_surface_t : public egl_object_t {
 protected:
-    ~egl_surface_t() {
-        ANativeWindow* const window = win.get();
-        if (window != NULL) {
-            native_window_set_buffers_format(window, 0);
-            if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
-                ALOGW("EGLNativeWindowType %p disconnect failed", window);
-            }
-        }
-    }
+    ~egl_surface_t();
 public:
     typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
 
-    egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win,
-            EGLSurface surface, egl_connection_t const* cnx) :
-        egl_object_t(get_display(dpy)), dpy(dpy), surface(surface),
-                config(config), win(win), cnx(cnx) {
-    }
-    EGLDisplay dpy;
+    egl_surface_t(egl_display_t* dpy, EGLConfig config,
+            EGLNativeWindowType win, EGLSurface surface,
+            egl_connection_t const* cnx);
+
     EGLSurface surface;
     EGLConfig config;
     sp<ANativeWindow> win;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index e742d3e..4e13377 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -23,6 +25,7 @@
 #include <utils/Errors.h>
 #include <utils/String8.h>
 #include <utils/Thread.h>
+#include <utils/Trace.h>
 #include <utils/Vector.h>
 
 #include <hardware/hardware.h>
@@ -48,8 +51,9 @@
       mNumOVLayers(0), mNumFBLayers(0),
       mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE),
       mEventHandler(handler),
-      mRefreshPeriod(refreshPeriod)
+      mRefreshPeriod(refreshPeriod), mVSyncCount(0)
 {
+    bool needVSyncThread = false;
     int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
     ALOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
     if (err == 0) {
@@ -64,12 +68,17 @@
                 mHwc->registerProcs(mHwc, &mCBContext.procs);
                 memset(mCBContext.procs.zero, 0, sizeof(mCBContext.procs.zero));
             }
-
             if (mHwc->common.version < HWC_DEVICE_API_VERSION_0_3) {
-                // we don't have VSYNC support, we need to fake it
-                mVSyncThread = new VSyncThread(*this);
+                needVSyncThread = true;
             }
         }
+    } else {
+        needVSyncThread = true;
+    }
+
+    if (needVSyncThread) {
+        // we don't have VSYNC support, we need to fake it
+        mVSyncThread = new VSyncThread(*this);
     }
 }
 
@@ -100,20 +109,20 @@
 }
 
 void HWComposer::vsync(int dpy, int64_t timestamp) {
+    ATRACE_INT("VSYNC", ++mVSyncCount&1);
     mEventHandler.onVSyncReceived(dpy, timestamp);
 }
 
 status_t HWComposer::eventControl(int event, int enabled) {
     status_t err = NO_ERROR;
-    if (mHwc->common.version >= HWC_DEVICE_API_VERSION_0_3) {
+    if (mHwc && mHwc->common.version >= HWC_DEVICE_API_VERSION_0_3) {
         err = mHwc->methods->eventControl(mHwc, event, enabled);
-    } else {
-        if (mVSyncThread != NULL) {
-            mVSyncThread->setEnabled(enabled);
-        } else {
-            err = BAD_VALUE;
-        }
     }
+
+    if (err == NO_ERROR && mVSyncThread != NULL) {
+        mVSyncThread->setEnabled(enabled);
+    }
+
     return err;
 }
 
@@ -245,4 +254,58 @@
 }
 
 // ---------------------------------------------------------------------------
+
+HWComposer::VSyncThread::VSyncThread(HWComposer& hwc)
+    : mHwc(hwc), mEnabled(false),
+      mNextFakeVSync(0),
+      mRefreshPeriod(hwc.mRefreshPeriod)
+{
+}
+
+void HWComposer::VSyncThread::setEnabled(bool enabled) {
+    Mutex::Autolock _l(mLock);
+    mEnabled = enabled;
+    mCondition.signal();
+}
+
+void HWComposer::VSyncThread::onFirstRef() {
+    run("VSyncThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+}
+
+bool HWComposer::VSyncThread::threadLoop() {
+    { // scope for lock
+        Mutex::Autolock _l(mLock);
+        while (!mEnabled) {
+            mCondition.wait(mLock);
+        }
+    }
+
+    const nsecs_t period = mRefreshPeriod;
+    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    nsecs_t next_vsync = mNextFakeVSync;
+    nsecs_t sleep = next_vsync - now;
+    if (sleep < 0) {
+        // we missed, find where the next vsync should be
+        sleep = (period - ((now - next_vsync) % period));
+        next_vsync = now + sleep;
+    }
+    mNextFakeVSync = next_vsync + period;
+
+    struct timespec spec;
+    spec.tv_sec  = next_vsync / 1000000000;
+    spec.tv_nsec = next_vsync % 1000000000;
+
+    int err;
+    do {
+        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
+    } while (err<0 && errno == EINTR);
+
+    if (err == 0) {
+        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
+    }
+
+    return true;
+}
+
+// ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 60a6367..7814594 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -97,56 +97,11 @@
         bool mEnabled;
         mutable nsecs_t mNextFakeVSync;
         nsecs_t mRefreshPeriod;
-
-        virtual void onFirstRef() {
-            run("VSyncThread",
-                PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
-        }
-
-        virtual bool threadLoop() {
-            { // scope for lock
-                Mutex::Autolock _l(mLock);
-                while (!mEnabled) {
-                    mCondition.wait(mLock);
-                }
-            }
-
-            const nsecs_t period = mRefreshPeriod;
-            const nsecs_t now = systemTime(CLOCK_MONOTONIC);
-            nsecs_t next_vsync = mNextFakeVSync;
-            nsecs_t sleep = next_vsync - now;
-            if (sleep < 0) {
-                // we missed, find where the next vsync should be
-                sleep = (period - ((now - next_vsync) % period));
-                next_vsync = now + sleep;
-            }
-            mNextFakeVSync = next_vsync + period;
-
-            struct timespec spec;
-            spec.tv_sec  = next_vsync / 1000000000;
-            spec.tv_nsec = next_vsync % 1000000000;
-
-            // NOTE: EINTR can happen with clock_nanosleep(), in case of
-            // any error (including EINTR) we go through the condition's
-            // test -- this is always correct and easy.
-            if (::clock_nanosleep(CLOCK_MONOTONIC,
-                    TIMER_ABSTIME, &spec, NULL) == 0) {
-                mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
-            }
-            return true;
-        }
-
+        virtual void onFirstRef();
+        virtual bool threadLoop();
     public:
-        VSyncThread(HWComposer& hwc) :
-            mHwc(hwc), mEnabled(false),
-            mNextFakeVSync(0),
-            mRefreshPeriod(hwc.mRefreshPeriod) {
-        }
-        void setEnabled(bool enabled) {
-            Mutex::Autolock _l(mLock);
-            mEnabled = enabled;
-            mCondition.signal();
-        }
+        VSyncThread(HWComposer& hwc);
+        void setEnabled(bool enabled);
     };
 
     friend class VSyncThread;
@@ -187,6 +142,7 @@
     cb_context              mCBContext;
     EventHandler&           mEventHandler;
     nsecs_t                 mRefreshPeriod;
+    size_t                  mVSyncCount;
     sp<VSyncThread>         mVSyncThread;
 };
 
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index b05b7c2..cc44186 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -123,13 +123,9 @@
 
         Mutex::Autolock _l(mLock);
         do {
-            // check if we have received a VSYNC event
-            if (mVSyncTimestamp) {
-                // we have a VSYNC event pending
-                timestamp = mVSyncTimestamp;
-                mVSyncTimestamp = 0;
-                break;
-            }
+            // latch VSYNC event if any
+            timestamp = mVSyncTimestamp;
+            mVSyncTimestamp = 0;
 
             // check if we should be waiting for VSYNC events
             bool waitForNextVsync = false;
@@ -144,17 +140,33 @@
                 }
             }
 
-            // enable or disable VSYNC events
-            mHw.getHwComposer().eventControl(
-                    HWComposer::EVENT_VSYNC, waitForNextVsync);
+            if (timestamp) {
+                if (!waitForNextVsync) {
+                    // we received a VSYNC but we have no clients
+                    // don't report it, and disable VSYNC events
+                    mHw.getHwComposer().eventControl(
+                            HWComposer::EVENT_VSYNC, false);
+                } else {
+                    // report VSYNC event
+                    break;
+                }
+            } else {
+                // never disable VSYNC events immediately, instead
+                // we'll wait to receive the event and we'll
+                // reevaluate whether we need to dispatch it and/or
+                // disable VSYNC events then.
+                if (waitForNextVsync) {
+                    // enable
+                    mHw.getHwComposer().eventControl(
+                            HWComposer::EVENT_VSYNC, true);
+                }
+            }
 
             // wait for something to happen
             mCondition.wait(mLock);
         } while(true);
 
         // process vsync event
-
-        ATRACE_INT("VSYNC", mDeliveredEvents&1);
         mDeliveredEvents++;
         mLastVSyncTimestamp = timestamp;