merge in KQS81M
diff --git a/data/etc/android.hardware.consumerir.xml b/data/etc/android.hardware.consumerir.xml
new file mode 100644
index 0000000..adba2f9
--- /dev/null
+++ b/data/etc/android.hardware.consumerir.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!-- This is the standard feature indicating that the device can communicate
+     using Near-Field Communications (NFC). -->
+<permissions>
+    <feature name="android.hardware.consumerir" />
+</permissions>
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 75ee30c..a5fdfb9 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -273,7 +273,7 @@
 private:
     // createImage creates a new EGLImage from a GraphicBuffer.
     EGLImageKHR createImage(EGLDisplay dpy,
-            const sp<GraphicBuffer>& graphicBuffer);
+            const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
 
     // freeBufferLocked frees up the given buffer slot.  If the slot has been
     // initialized this will release the reference to the GraphicBuffer in that
@@ -386,6 +386,10 @@
         // mEglImage is the EGLImage created from mGraphicBuffer.
         EGLImageKHR mEglImage;
 
+        // mCropRect is the crop rectangle passed to EGL when mEglImage was
+        // created.
+        Rect mCropRect;
+
         // mFence is the EGL sync object that must signal before the buffer
         // associated with this buffer slot may be dequeued. It is initialized
         // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h
index ac67f94..de3aeb1 100644
--- a/include/media/hardware/HardwareAPI.h
+++ b/include/media/hardware/HardwareAPI.h
@@ -89,6 +89,31 @@
   buffer_handle_t pHandle;
 };
 
+// A pointer to this struct is passed to OMX_SetParameter() when the extension
+// index "OMX.google.android.index.prepareForAdaptivePlayback" is given.
+//
+// This method is used to signal a video decoder, that the user has requested
+// seamless resolution change support (if bEnable is set to OMX_TRUE).
+// nMaxFrameWidth and nMaxFrameHeight are the dimensions of the largest
+// anticipated frames in the video.  If bEnable is OMX_FALSE, no resolution
+// change is expected, and the nMaxFrameWidth/Height fields are unused.
+//
+// If the decoder supports dynamic output buffers, it may ignore this
+// request.  Otherwise, it shall request resources in such a way so that it
+// avoids full port-reconfiguration (due to output port-definition change)
+// during resolution changes.
+//
+// DO NOT USE THIS STRUCTURE AS IT WILL BE REMOVED.  INSTEAD, IMPLEMENT
+// METADATA SUPPORT FOR VIDEO DECODERS.
+struct PrepareForAdaptivePlaybackParams {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_BOOL bEnable;
+    OMX_U32 nMaxFrameWidth;
+    OMX_U32 nMaxFrameHeight;
+};
+
 // A pointer to this struct is passed to OMX_SetParameter when the extension
 // index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is
 // given.  This call will only be performed if a prior call was made with the
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
index e21e6a8..2f4c3c4 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -32,6 +32,8 @@
 
     virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag,
             const String16& packageName) = 0;
+    virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
+            const String16& packageName, int uid) = 0;
     virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags) = 0;
 };
 
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 87d66e2..c165a68 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -668,11 +668,15 @@
                 mConnectedApi = api;
                 output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size());
 
-                // set-up a death notification so that we can disconnect automatically
-                // when/if the remote producer dies.
-                // This will fail with INVALID_OPERATION if the "token" is local to our process.
-                if (token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)) == NO_ERROR) {
-                    mConnectedProducerToken = token;
+                // set-up a death notification so that we can disconnect
+                // automatically when/if the remote producer dies.
+                if (token != NULL && token->remoteBinder() != NULL) {
+                    status_t err = token->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
+                    if (err == NO_ERROR) {
+                        mConnectedProducerToken = token;
+                    } else {
+                        ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err);
+                    }
                 }
             }
             break;
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index cf3f12a..7ee3081 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -41,6 +41,9 @@
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
+EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+#define CROP_EXT_STR "EGL_ANDROID_image_crop"
+
 namespace android {
 
 // Macros for including the GLConsumer name in log messages
@@ -89,6 +92,30 @@
 Mutex GLConsumer::sStaticInitLock;
 sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer;
 
+static bool hasEglAndroidImageCropImpl() {
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS);
+    size_t cropExtLen = strlen(CROP_EXT_STR);
+    size_t extsLen = strlen(exts);
+    bool equal = !strcmp(CROP_EXT_STR, exts);
+    bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1);
+    bool atEnd = (cropExtLen+1) < extsLen &&
+            !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1));
+    bool inMiddle = strstr(exts, " " CROP_EXT_STR " ");
+    return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglAndroidImageCrop() {
+    // Only compute whether the extension is present once the first time this
+    // function is called.
+    static bool hasIt = hasEglAndroidImageCropImpl();
+    return hasIt;
+}
+
+static bool isEglImageCroppable(const Rect& crop) {
+    return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0);
+}
+
 GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
         uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
     ConsumerBase(bq, isControlledByApp),
@@ -279,19 +306,30 @@
     }
 
     int slot = item->mBuf;
-    if (item->mGraphicBuffer != NULL) {
-        // This buffer has not been acquired before, so we must assume
-        // that any EGLImage in mEglSlots is stale.
-        if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
-            if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
-                ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
-                      slot);
-                // keep going
-            }
-            mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
+    bool destroyEglImage = false;
+
+    if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
+        if (item->mGraphicBuffer != NULL) {
+            // This buffer has not been acquired before, so we must assume
+            // that any EGLImage in mEglSlots is stale.
+            destroyEglImage = true;
+        } else if (mEglSlots[slot].mCropRect != item->mCrop) {
+            // We've already seen this buffer before, but it now has a
+            // different crop rect, so we'll need to recreate the EGLImage if
+            // we're using the EGL_ANDROID_image_crop extension.
+            destroyEglImage = hasEglAndroidImageCrop();
         }
     }
 
+    if (destroyEglImage) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
+            ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
+                  slot);
+            // keep going
+        }
+        mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
+    }
+
     return NO_ERROR;
 }
 
@@ -334,13 +372,15 @@
     // EGLImage when detaching from a context but the buffer has not been
     // re-allocated.
     if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
-        EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
+        EGLImageKHR image = createImage(mEglDisplay,
+                mSlots[buf].mGraphicBuffer, item.mCrop);
         if (image == EGL_NO_IMAGE_KHR) {
             ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
                   mEglDisplay, buf);
             return UNKNOWN_ERROR;
         }
         mEglSlots[buf].mEglImage = image;
+        mEglSlots[buf].mCropRect = item.mCrop;
     }
 
     // Do whatever sync ops we need to do before releasing the old slot.
@@ -581,7 +621,8 @@
             mCurrentTexture, mCurrentTextureBuf.get());
 
     // Create a temporary EGLImageKHR.
-    EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
+    Rect crop;
+    EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop);
     if (image == EGL_NO_IMAGE_KHR) {
         return UNKNOWN_ERROR;
     }
@@ -753,60 +794,66 @@
         ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
     }
 
-    Rect cropRect = mCurrentCrop;
-    float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
-    float bufferWidth = buf->getWidth();
-    float bufferHeight = buf->getHeight();
-    if (!cropRect.isEmpty()) {
-        float shrinkAmount = 0.0f;
-        if (mFilteringEnabled) {
-            // In order to prevent bilinear sampling beyond the edge of the
-            // crop rectangle we may need to shrink it by 2 texels in each
-            // dimension.  Normally this would just need to take 1/2 a texel
-            // off each end, but because the chroma channels of YUV420 images
-            // are subsampled we may need to shrink the crop region by a whole
-            // texel on each side.
-            switch (buf->getPixelFormat()) {
-                case PIXEL_FORMAT_RGBA_8888:
-                case PIXEL_FORMAT_RGBX_8888:
-                case PIXEL_FORMAT_RGB_888:
-                case PIXEL_FORMAT_RGB_565:
-                case PIXEL_FORMAT_BGRA_8888:
-                    // We know there's no subsampling of any channels, so we
-                    // only need to shrink by a half a pixel.
-                    shrinkAmount = 0.5;
-                    break;
+    float mtxBeforeFlipV[16];
+    if (!isEglImageCroppable(mCurrentCrop)) {
+        Rect cropRect = mCurrentCrop;
+        float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+        float bufferWidth = buf->getWidth();
+        float bufferHeight = buf->getHeight();
+        if (!cropRect.isEmpty()) {
+            float shrinkAmount = 0.0f;
+            if (mFilteringEnabled) {
+                // In order to prevent bilinear sampling beyond the edge of the
+                // crop rectangle we may need to shrink it by 2 texels in each
+                // dimension.  Normally this would just need to take 1/2 a texel
+                // off each end, but because the chroma channels of YUV420 images
+                // are subsampled we may need to shrink the crop region by a whole
+                // texel on each side.
+                switch (buf->getPixelFormat()) {
+                    case PIXEL_FORMAT_RGBA_8888:
+                    case PIXEL_FORMAT_RGBX_8888:
+                    case PIXEL_FORMAT_RGB_888:
+                    case PIXEL_FORMAT_RGB_565:
+                    case PIXEL_FORMAT_BGRA_8888:
+                        // We know there's no subsampling of any channels, so we
+                        // only need to shrink by a half a pixel.
+                        shrinkAmount = 0.5;
+                        break;
 
-                default:
-                    // If we don't recognize the format, we must assume the
-                    // worst case (that we care about), which is YUV420.
-                    shrinkAmount = 1.0;
-                    break;
+                    default:
+                        // If we don't recognize the format, we must assume the
+                        // worst case (that we care about), which is YUV420.
+                        shrinkAmount = 1.0;
+                        break;
+                }
+            }
+
+            // Only shrink the dimensions that are not the size of the buffer.
+            if (cropRect.width() < bufferWidth) {
+                tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+                sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
+                        bufferWidth;
+            }
+            if (cropRect.height() < bufferHeight) {
+                ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
+                        bufferHeight;
+                sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
+                        bufferHeight;
             }
         }
+        float crop[16] = {
+            sx, 0, 0, 0,
+            0, sy, 0, 0,
+            0, 0, 1, 0,
+            tx, ty, 0, 1,
+        };
 
-        // Only shrink the dimensions that are not the size of the buffer.
-        if (cropRect.width() < bufferWidth) {
-            tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
-            sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
-                    bufferWidth;
-        }
-        if (cropRect.height() < bufferHeight) {
-            ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
-                    bufferHeight;
-            sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
-                    bufferHeight;
+        mtxMul(mtxBeforeFlipV, crop, xform);
+    } else {
+        for (int i = 0; i < 16; i++) {
+            mtxBeforeFlipV[i] = xform[i];
         }
     }
-    float crop[16] = {
-        sx, 0, 0, 0,
-        0, sy, 0, 0,
-        0, 0, 1, 0,
-        tx, ty, 0, 1,
-    };
-
-    float mtxBeforeFlipV[16];
-    mtxMul(mtxBeforeFlipV, crop, xform);
 
     // SurfaceFlinger expects the top of its window textures to be at a Y
     // coordinate of 0, so GLConsumer must behave the same way.  We don't
@@ -828,12 +875,26 @@
 }
 
 EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
-        const sp<GraphicBuffer>& graphicBuffer) {
+        const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
     EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
     EGLint attrs[] = {
-        EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
+        EGL_IMAGE_PRESERVED_KHR,        EGL_TRUE,
+        EGL_IMAGE_CROP_LEFT_ANDROID,    crop.left,
+        EGL_IMAGE_CROP_TOP_ANDROID,     crop.top,
+        EGL_IMAGE_CROP_RIGHT_ANDROID,   crop.right,
+        EGL_IMAGE_CROP_BOTTOM_ANDROID,  crop.bottom,
         EGL_NONE,
     };
+    if (!crop.isValid()) {
+        // No crop rect to set, so terminate the attrib array before the crop.
+        attrs[2] = EGL_NONE;
+    } else if (!isEglImageCroppable(crop)) {
+        // The crop rect is not at the origin, so we can't set the crop on the
+        // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
+        // extension.  In the future we can add a layered extension that
+        // removes this restriction if there is hardware that can support it.
+        attrs[2] = EGL_NONE;
+    }
     EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
             EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
     if (image == EGL_NO_IMAGE_KHR) {
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 6c505ed..3b2984a 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -494,6 +494,14 @@
 #define EGL_FRAMEBUFFER_TARGET_ANDROID		0x3147
 #endif
 
+#ifndef EGL_ANDROID_image_crop
+#define EGL_ANDROID_image_crop 1
+#define EGL_IMAGE_CROP_LEFT_ANDROID   0x3148
+#define EGL_IMAGE_CROP_TOP_ANDROID    0x3149
+#define EGL_IMAGE_CROP_RIGHT_ANDROID  0x314A
+#define EGL_IMAGE_CROP_BOTTOM_ANDROID 0x314B
+#endif
+
 #ifndef EGL_ANDROID_blob_cache
 #define EGL_ANDROID_blob_cache 1
 typedef khronos_ssize_t EGLsizeiANDROID;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 04d5f45..0cc5265 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -99,6 +99,7 @@
         "EGL_NV_system_time "
         "EGL_ANDROID_image_native_buffer "      // mandatory
         "EGL_KHR_wait_sync "                    // strongly recommended
+        "EGL_ANDROID_recordable "               // mandatory
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -106,8 +107,7 @@
 //      "EGL_IMG_hibernate_process "            // optional
 //      "EGL_ANDROID_native_fence_sync "        // strongly recommended
 //      "EGL_ANDROID_framebuffer_target "       // mandatory for HWC 1.1
-//      "EGL_ANDROID_recordable "               // mandatory
-
+//      "EGL_ANDROID_image_crop "               // optional
 
 /*
  * EGL Extensions entry-points exposed to 3rd party applications
diff --git a/opengl/specs/README b/opengl/specs/README
index eb86869..f4de1b3 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -14,4 +14,8 @@
 0x3145               EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync)
 0x3146               EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync)
 0x3147               EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target)
-0x3148 - 0x314F      (unused)
+0x3148               EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop)
+0x3149               EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop)
+0x314A               EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
+0x314B               EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
+0x314C - 0x314F      (unused)
diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp
index 3f5b81e..9f60e75 100644
--- a/services/powermanager/IPowerManager.cpp
+++ b/services/powermanager/IPowerManager.cpp
@@ -30,7 +30,8 @@
 // must be kept in sync with IPowerManager.aidl
 enum {
     ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION,
-    RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 1,
+    ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1,
+    RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2,
 };
 
 class BpPowerManager : public BpInterface<IPowerManager>
@@ -55,6 +56,20 @@
         return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply);
     }
 
+    virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag,
+            const String16& packageName, int uid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor());
+
+        data.writeStrongBinder(lock);
+        data.writeInt32(flags);
+        data.writeString16(tag);
+        data.writeString16(packageName);
+        data.writeInt32(uid); // uid to blame for the work
+        return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply);
+    }
+
     virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags)
     {
         Parcel data, reply;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 7cc4ce1..bd89bd5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -4,6 +4,7 @@
 LOCAL_SRC_FILES:= \
     Client.cpp \
     DisplayDevice.cpp \
+    DispSync.cpp \
     EventThread.cpp \
     FrameTracker.cpp \
     Layer.cpp \
@@ -53,6 +54,23 @@
   LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS)
 endif
 
+ifeq ($(TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK),true)
+    LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK
+endif
+
+# See build/target/board/generic/BoardConfig.mk for a description of this setting.
+ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
+    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
+else
+    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=0
+endif
+
+ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),)
+    LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=$(PRESENT_TIME_OFFSET_FROM_VSYNC_NS)
+else
+    LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=0
+endif
+
 LOCAL_CFLAGS += -fvisibility=hidden
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
new file mode 100644
index 0000000..7e67138
--- /dev/null
+++ b/services/surfaceflinger/DispSync.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <math.h>
+
+#include <cutils/log.h>
+
+#include <ui/Fence.h>
+
+#include <utils/String8.h>
+#include <utils/Thread.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include "DispSync.h"
+#include "EventLog/EventLog.h"
+
+namespace android {
+
+// Setting this to true enables verbose tracing that can be used to debug
+// vsync event model or phase issues.
+static const bool traceDetailedInfo = false;
+
+// This is the threshold used to determine when hardware vsync events are
+// needed to re-synchronize the software vsync model with the hardware.  The
+// error metric used is the mean of the squared difference between each
+// present time and the nearest software-predicted vsync.
+static const nsecs_t errorThreshold = 160000000000;
+
+// This works around the lack of support for the sync framework on some
+// devices.
+#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK
+static const bool runningWithoutSyncFramework = true;
+#else
+static const bool runningWithoutSyncFramework = false;
+#endif
+
+// This is the offset from the present fence timestamps to the corresponding
+// vsync event.
+static const int64_t presentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
+
+class DispSyncThread: public Thread {
+public:
+
+    DispSyncThread():
+            mStop(false),
+            mPeriod(0),
+            mPhase(0),
+            mWakeupLatency(0) {
+    }
+
+    virtual ~DispSyncThread() {}
+
+    void updateModel(nsecs_t period, nsecs_t phase) {
+        Mutex::Autolock lock(mMutex);
+        mPeriod = period;
+        mPhase = phase;
+        mCond.signal();
+    }
+
+    void stop() {
+        Mutex::Autolock lock(mMutex);
+        mStop = true;
+        mCond.signal();
+    }
+
+    virtual bool threadLoop() {
+        status_t err;
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        nsecs_t nextEventTime = 0;
+
+        while (true) {
+            Vector<CallbackInvocation> callbackInvocations;
+
+            nsecs_t targetTime = 0;
+
+            { // Scope for lock
+                Mutex::Autolock lock(mMutex);
+
+                if (mStop) {
+                    return false;
+                }
+
+                if (mPeriod == 0) {
+                    err = mCond.wait(mMutex);
+                    if (err != NO_ERROR) {
+                        ALOGE("error waiting for new events: %s (%d)",
+                                strerror(-err), err);
+                        return false;
+                    }
+                    continue;
+                }
+
+                nextEventTime = computeNextEventTimeLocked(now);
+                targetTime = nextEventTime - mWakeupLatency;
+
+                bool isWakeup = false;
+
+                if (now < targetTime) {
+                    err = mCond.waitRelative(mMutex, targetTime - now);
+
+                    if (err == TIMED_OUT) {
+                        isWakeup = true;
+                    } else if (err != NO_ERROR) {
+                        ALOGE("error waiting for next event: %s (%d)",
+                                strerror(-err), err);
+                        return false;
+                    }
+                }
+
+                now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+                if (isWakeup) {
+                    mWakeupLatency = ((mWakeupLatency * 63) +
+                            (now - targetTime)) / 64;
+                    if (mWakeupLatency > 500000) {
+                        // Don't correct by more than 500 us
+                        mWakeupLatency = 500000;
+                    }
+                    if (traceDetailedInfo) {
+                        ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
+                        ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
+                    }
+                }
+
+                callbackInvocations = gatherCallbackInvocationsLocked(now);
+            }
+
+            if (callbackInvocations.size() > 0) {
+                fireCallbackInvocations(callbackInvocations);
+            }
+        }
+
+        return false;
+    }
+
+    status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
+        Mutex::Autolock lock(mMutex);
+
+        for (size_t i = 0; i < mEventListeners.size(); i++) {
+            if (mEventListeners[i].mCallback == callback) {
+                return BAD_VALUE;
+            }
+        }
+
+        EventListener listener;
+        listener.mPhase = phase;
+        listener.mCallback = callback;
+        listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        mEventListeners.push(listener);
+
+        mCond.signal();
+
+        return NO_ERROR;
+    }
+
+    status_t removeEventListener(const sp<DispSync::Callback>& callback) {
+        Mutex::Autolock lock(mMutex);
+
+        for (size_t i = 0; i < mEventListeners.size(); i++) {
+            if (mEventListeners[i].mCallback == callback) {
+                mEventListeners.removeAt(i);
+                mCond.signal();
+                return NO_ERROR;
+            }
+        }
+
+        return BAD_VALUE;
+    }
+
+    // This method is only here to handle the runningWithoutSyncFramework
+    // case.
+    bool hasAnyEventListeners() {
+        Mutex::Autolock lock(mMutex);
+        return !mEventListeners.empty();
+    }
+
+private:
+
+    struct EventListener {
+        nsecs_t mPhase;
+        nsecs_t mLastEventTime;
+        sp<DispSync::Callback> mCallback;
+    };
+
+    struct CallbackInvocation {
+        sp<DispSync::Callback> mCallback;
+        nsecs_t mEventTime;
+    };
+
+    nsecs_t computeNextEventTimeLocked(nsecs_t now) {
+        nsecs_t nextEventTime = INT64_MAX;
+        for (size_t i = 0; i < mEventListeners.size(); i++) {
+            nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
+                    now);
+
+            if (t < nextEventTime) {
+                nextEventTime = t;
+            }
+        }
+
+        return nextEventTime;
+    }
+
+    Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+        Vector<CallbackInvocation> callbackInvocations;
+        nsecs_t ref = now - mPeriod;
+
+        for (size_t i = 0; i < mEventListeners.size(); i++) {
+            nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
+                    ref);
+
+            if (t - mWakeupLatency < now) {
+                CallbackInvocation ci;
+                ci.mCallback = mEventListeners[i].mCallback;
+                ci.mEventTime = t;
+                callbackInvocations.push(ci);
+                mEventListeners.editItemAt(i).mLastEventTime = t;
+            }
+        }
+
+        return callbackInvocations;
+    }
+
+    nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
+            nsecs_t ref) {
+
+        nsecs_t lastEventTime = listener.mLastEventTime;
+        if (ref < lastEventTime) {
+            ref = lastEventTime;
+        }
+
+        nsecs_t phase = mPhase + listener.mPhase;
+        nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase;
+
+        if (t - listener.mLastEventTime < mPeriod / 2) {
+            t += mPeriod;
+        }
+
+        return t;
+    }
+
+    void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
+        for (size_t i = 0; i < callbacks.size(); i++) {
+            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
+        }
+    }
+
+    bool mStop;
+
+    nsecs_t mPeriod;
+    nsecs_t mPhase;
+    nsecs_t mWakeupLatency;
+
+    Vector<EventListener> mEventListeners;
+
+    Mutex mMutex;
+    Condition mCond;
+};
+
+class ZeroPhaseTracer : public DispSync::Callback {
+public:
+    ZeroPhaseTracer() : mParity(false) {}
+
+    virtual void onDispSyncEvent(nsecs_t when) {
+        mParity = !mParity;
+        ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
+    }
+
+private:
+    bool mParity;
+};
+
+DispSync::DispSync() {
+    mThread = new DispSyncThread();
+    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+
+    reset();
+    beginResync();
+
+    if (traceDetailedInfo) {
+        // If runningWithoutSyncFramework is true then the ZeroPhaseTracer
+        // would prevent HW vsync event from ever being turned off.
+        // Furthermore the zero-phase tracing is not needed because any time
+        // there is an event registered we will turn on the HW vsync events.
+        if (!runningWithoutSyncFramework) {
+            addEventListener(0, new ZeroPhaseTracer());
+        }
+    }
+}
+
+DispSync::~DispSync() {}
+
+void DispSync::reset() {
+    Mutex::Autolock lock(mMutex);
+
+    mNumResyncSamples = 0;
+    mFirstResyncSample = 0;
+    mNumResyncSamplesSincePresent = 0;
+    resetErrorLocked();
+}
+
+bool DispSync::addPresentFence(const sp<Fence>& fence) {
+    Mutex::Autolock lock(mMutex);
+
+    mPresentFences[mPresentSampleOffset] = fence;
+    mPresentTimes[mPresentSampleOffset] = 0;
+    mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
+    mNumResyncSamplesSincePresent = 0;
+
+    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+        const sp<Fence>& f(mPresentFences[i]);
+        if (f != NULL) {
+            nsecs_t t = f->getSignalTime();
+            if (t < INT64_MAX) {
+                mPresentFences[i].clear();
+                mPresentTimes[i] = t + presentTimeOffset;
+            }
+        }
+    }
+
+    updateErrorLocked();
+
+    return mPeriod == 0 || mError > errorThreshold;
+}
+
+void DispSync::beginResync() {
+    Mutex::Autolock lock(mMutex);
+
+    mNumResyncSamples = 0;
+}
+
+bool DispSync::addResyncSample(nsecs_t timestamp) {
+    Mutex::Autolock lock(mMutex);
+
+    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
+    mResyncSamples[idx] = timestamp;
+
+    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
+        mNumResyncSamples++;
+    } else {
+        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
+    }
+
+    updateModelLocked();
+
+    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
+        resetErrorLocked();
+    }
+
+    if (runningWithoutSyncFramework) {
+        // If we don't have the sync framework we will never have
+        // addPresentFence called.  This means we have no way to know whether
+        // or not we're synchronized with the HW vsyncs, so we just request
+        // that the HW vsync events be turned on whenever we need to generate
+        // SW vsync events.
+        return mThread->hasAnyEventListeners();
+    }
+
+    return mPeriod == 0 || mError > errorThreshold;
+}
+
+void DispSync::endResync() {
+}
+
+status_t DispSync::addEventListener(nsecs_t phase,
+        const sp<Callback>& callback) {
+
+    Mutex::Autolock lock(mMutex);
+    return mThread->addEventListener(phase, callback);
+}
+
+status_t DispSync::removeEventListener(const sp<Callback>& callback) {
+    Mutex::Autolock lock(mMutex);
+    return mThread->removeEventListener(callback);
+}
+
+void DispSync::setPeriod(nsecs_t period) {
+    Mutex::Autolock lock(mMutex);
+    mPeriod = period;
+    mPhase = 0;
+}
+
+void DispSync::updateModelLocked() {
+    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
+        nsecs_t durationSum = 0;
+        for (size_t i = 1; i < mNumResyncSamples; i++) {
+            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+            size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
+            durationSum += mResyncSamples[idx] - mResyncSamples[prev];
+        }
+
+        mPeriod = durationSum / (mNumResyncSamples - 1);
+
+        double sampleAvgX = 0;
+        double sampleAvgY = 0;
+        double scale = 2.0 * M_PI / double(mPeriod);
+        for (size_t i = 0; i < mNumResyncSamples; i++) {
+            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+            nsecs_t sample = mResyncSamples[idx];
+            double samplePhase = double(sample % mPeriod) * scale;
+            sampleAvgX += cos(samplePhase);
+            sampleAvgY += sin(samplePhase);
+        }
+
+        sampleAvgX /= double(mNumResyncSamples);
+        sampleAvgY /= double(mNumResyncSamples);
+
+        mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
+
+        if (mPhase < 0) {
+            mPhase += mPeriod;
+        }
+
+        if (traceDetailedInfo) {
+            ATRACE_INT64("DispSync:Period", mPeriod);
+            ATRACE_INT64("DispSync:Phase", mPhase);
+        }
+
+        mThread->updateModel(mPeriod, mPhase);
+    }
+}
+
+void DispSync::updateErrorLocked() {
+    if (mPeriod == 0) {
+        return;
+    }
+
+    int numErrSamples = 0;
+    nsecs_t sqErrSum = 0;
+
+    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+        nsecs_t sample = mPresentTimes[i];
+        if (sample > mPhase) {
+            nsecs_t sampleErr = (sample - mPhase) % mPeriod;
+            if (sampleErr > mPeriod / 2) {
+                sampleErr -= mPeriod;
+            }
+            sqErrSum += sampleErr * sampleErr;
+            numErrSamples++;
+        }
+    }
+
+    if (numErrSamples > 0) {
+        mError = sqErrSum / numErrSamples;
+    } else {
+        mError = 0;
+    }
+
+    if (traceDetailedInfo) {
+        ATRACE_INT64("DispSync:Error", mError);
+    }
+}
+
+void DispSync::resetErrorLocked() {
+    mPresentSampleOffset = 0;
+    mError = 0;
+    for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+        mPresentFences[i].clear();
+        mPresentTimes[i] = 0;
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
new file mode 100644
index 0000000..c4280aa
--- /dev/null
+++ b/services/surfaceflinger/DispSync.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 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_DISPSYNC_H
+#define ANDROID_DISPSYNC_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class String8;
+class Fence;
+class DispSyncThread;
+
+// DispSync maintains a model of the periodic hardware-based vsync events of a
+// display and uses that model to execute period callbacks at specific phase
+// offsets from the hardware vsync events.  The model is constructed by
+// feeding consecutive hardware event timestamps to the DispSync object via
+// the addResyncSample method.
+//
+// The model is validated using timestamps from Fence objects that are passed
+// to the DispSync object via the addPresentFence method.  These fence
+// timestamps should correspond to a hardware vsync event, but they need not
+// be consecutive hardware vsync times.  If this method determines that the
+// current model accurately represents the hardware event times it will return
+// false to indicate that a resynchronization (via addResyncSample) is not
+// needed.
+class DispSync {
+
+public:
+
+    class Callback: public virtual RefBase {
+    public:
+        virtual ~Callback() {};
+        virtual void onDispSyncEvent(nsecs_t when) = 0;
+    };
+
+    DispSync();
+    ~DispSync();
+
+    void reset();
+
+    // addPresentFence adds a fence for use in validating the current vsync
+    // event model.  The fence need not be signaled at the time
+    // addPresentFence is called.  When the fence does signal, its timestamp
+    // should correspond to a hardware vsync event.  Unlike the
+    // addResyncSample method, the timestamps of consecutive fences need not
+    // correspond to consecutive hardware vsync events.
+    //
+    // This method should be called with the retire fence from each HWComposer
+    // set call that affects the display.
+    bool addPresentFence(const sp<Fence>& fence);
+
+    // The beginResync, addResyncSample, and endResync methods are used to re-
+    // synchronize the DispSync's model to the hardware vsync events.  The re-
+    // synchronization process involves first calling beginResync, then
+    // calling addResyncSample with a sequence of consecutive hardware vsync
+    // event timestamps, and finally calling endResync when addResyncSample
+    // indicates that no more samples are needed by returning false.
+    //
+    // This resynchronization process should be performed whenever the display
+    // is turned on (i.e. once immediately after it's turned on) and whenever
+    // addPresentFence returns true indicating that the model has drifted away
+    // from the hardware vsync events.
+    void beginResync();
+    bool addResyncSample(nsecs_t timestamp);
+    void endResync();
+
+    // The setPreiod method sets the vsync event model's period to a specific
+    // value.  This should be used to prime the model when a display is first
+    // turned on.  It should NOT be used after that.
+    void setPeriod(nsecs_t period);
+
+    // addEventListener registers a callback to be called repeatedly at the
+    // given phase offset from the hardware vsync events.  The callback is
+    // called from a separate thread and it should return reasonably quickly
+    // (i.e. within a few hundred microseconds).
+    status_t addEventListener(nsecs_t phase, const sp<Callback>& callback);
+
+    // removeEventListener removes an already-registered event callback.  Once
+    // this method returns that callback will no longer be called by the
+    // DispSync object.
+    status_t removeEventListener(const sp<Callback>& callback);
+
+private:
+
+    void updateModelLocked();
+    void updateErrorLocked();
+    void resetErrorLocked();
+
+    enum { MAX_RESYNC_SAMPLES = 32 };
+    enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 };
+    enum { NUM_PRESENT_SAMPLES = 8 };
+    enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 12 };
+
+    // mPeriod is the computed period of the modeled vsync events in
+    // nanoseconds.
+    nsecs_t mPeriod;
+
+    // mPhase is the phase offset of the modeled vsync events.  It is the
+    // number of nanoseconds from time 0 to the first vsync event.
+    nsecs_t mPhase;
+
+    // mError is the computed model error.  It is based on the difference
+    // between the estimated vsync event times and those observed in the
+    // mPresentTimes array.
+    nsecs_t mError;
+
+    // These member variables are the state used during the resynchronization
+    // process to store information about the hardware vsync event times used
+    // to compute the model.
+    nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+    size_t mFirstResyncSample;
+    size_t mNumResyncSamples;
+    int mNumResyncSamplesSincePresent;
+
+    // These member variables store information about the present fences used
+    // to validate the currently computed model.
+    sp<Fence> mPresentFences[NUM_PRESENT_SAMPLES];
+    nsecs_t mPresentTimes[NUM_PRESENT_SAMPLES];
+    size_t mPresentSampleOffset;
+
+    // mThread is the thread from which all the callbacks are called.
+    sp<DispSyncThread> mThread;
+
+    // mMutex is used to protect access to all member variables.
+    mutable Mutex mMutex;
+};
+
+}
+
+#endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 32c55fd..179d956 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -285,7 +285,7 @@
 void HWComposer::vsync(int disp, int64_t timestamp) {
     if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
         char tag[16];
-        snprintf(tag, sizeof(tag), "VSYNC_%1u", disp);
+        snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
         ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
 
         mEventHandler.onVSyncReceived(disp, timestamp);
@@ -524,7 +524,14 @@
             disp.framebufferTarget->handle = disp.fbTargetHandle;
             disp.framebufferTarget->transform = 0;
             disp.framebufferTarget->blending = HWC_BLENDING_PREMULT;
-            disp.framebufferTarget->sourceCrop = r;
+            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
+                disp.framebufferTarget->sourceCropf.left = 0;
+                disp.framebufferTarget->sourceCropf.top = 0;
+                disp.framebufferTarget->sourceCropf.right = disp.width;
+                disp.framebufferTarget->sourceCropf.bottom = disp.height;
+            } else {
+                disp.framebufferTarget->sourceCrop = r;
+            }
             disp.framebufferTarget->displayFrame = r;
             disp.framebufferTarget->visibleRegionScreen.numRects = 1;
             disp.framebufferTarget->visibleRegionScreen.rects =
@@ -604,6 +611,10 @@
         // here we're just making sure that "skip" layers are set
         // to HWC_FRAMEBUFFER and we're also counting how many layers
         // we have of each type.
+        //
+        // If there are no window layers, we treat the display has having FB
+        // composition, because SurfaceFlinger will use GLES to draw the
+        // wormhole region.
         for (size_t i=0 ; i<mNumDisplays ; i++) {
             DisplayData& disp(mDisplayData[i]);
             disp.hasFbComp = false;
@@ -625,6 +636,11 @@
                         disp.hasOvComp = true;
                     }
                 }
+                if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {
+                    disp.hasFbComp = true;
+                }
+            } else {
+                disp.hasFbComp = true;
             }
         }
     }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index c06043d..c5a14b0 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -147,6 +147,10 @@
             mFbProducerSlot, fbBuffer.get(),
             mOutputProducerSlot, outBuffer.get());
 
+    // At this point we know the output buffer acquire fence,
+    // so update HWC state with it.
+    mHwc.setOutputBuffer(mDisplayId, mOutputFence, outBuffer);
+
     return mHwc.fbPost(mDisplayId, mFbFence, fbBuffer);
 }
 
@@ -258,6 +262,17 @@
     Source source = fbSourceForCompositionType(mCompositionType);
 
     if (source == SOURCE_SINK) {
+
+        if (mOutputProducerSlot < 0) {
+            // Last chance bailout if something bad happened earlier. For example,
+            // in a GLES configuration, if the sink disappears then dequeueBuffer
+            // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+            // will soldier on. So we end up here without a buffer. There should
+            // be lots of scary messages in the log just before this.
+            VDS_LOGE("dequeueBuffer: no buffer, bailing out");
+            return NO_MEMORY;
+        }
+
         // We already dequeued the output buffer. If the GLES driver wants
         // something incompatible, we have to cancel and get a new one. This
         // will mean that HWC will see a different output buffer between
@@ -404,7 +419,11 @@
         return result;
     mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
 
-    result = mHwc.setOutputBuffer(mDisplayId, mOutputFence,
+    // On GLES-only frames, we don't have the right output buffer acquire fence
+    // until after GLES calls queueBuffer(). So here we just set the buffer
+    // (for use in HWC prepare) but not the fence; we'll call this again with
+    // the proper fence once we have it.
+    result = mHwc.setOutputBuffer(mDisplayId, Fence::NO_FENCE,
             mProducerBuffers[mOutputProducerSlot]);
 
     return result;
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index a61ad72..3528b62 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -36,9 +36,10 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger),
+EventThread::EventThread(const sp<VSyncSource>& src)
+    : mVSyncSource(src),
       mUseSoftwareVSync(false),
+      mVsyncEnabled(false),
       mDebugVsyncEnabled(false) {
 
     for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
@@ -110,19 +111,13 @@
     }
 }
 
-
-void EventThread::onVSyncReceived(int type, nsecs_t timestamp) {
-    ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
-            "received vsync event for an invalid display (id=%d)", type);
-
+void EventThread::onVSyncEvent(nsecs_t timestamp) {
     Mutex::Autolock _l(mLock);
-    if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        mVSyncEvent[type].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
-        mVSyncEvent[type].header.id = type;
-        mVSyncEvent[type].header.timestamp = timestamp;
-        mVSyncEvent[type].vsync.count++;
-        mCondition.broadcast();
-    }
+    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+    mVSyncEvent[0].header.id = 0;
+    mVSyncEvent[0].header.timestamp = timestamp;
+    mVSyncEvent[0].vsync.count++;
+    mCondition.broadcast();
 }
 
 void EventThread::onHotplugReceived(int type, bool connected) {
@@ -308,18 +303,23 @@
 void EventThread::enableVSyncLocked() {
     if (!mUseSoftwareVSync) {
         // never enable h/w VSYNC when screen is off
-        mFlinger->eventControl(DisplayDevice::DISPLAY_PRIMARY,
-                SurfaceFlinger::EVENT_VSYNC, true);
-        mPowerHAL.vsyncHint(true);
+        if (!mVsyncEnabled) {
+            mVsyncEnabled = true;
+            mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
+            mVSyncSource->setVSyncEnabled(true);
+            mPowerHAL.vsyncHint(true);
+        }
     }
     mDebugVsyncEnabled = true;
 }
 
 void EventThread::disableVSyncLocked() {
-    mFlinger->eventControl(DisplayDevice::DISPLAY_PRIMARY,
-            SurfaceFlinger::EVENT_VSYNC, false);
-    mPowerHAL.vsyncHint(false);
-    mDebugVsyncEnabled = false;
+    if (mVsyncEnabled) {
+        mVsyncEnabled = false;
+        mVSyncSource->setVSyncEnabled(false);
+        mPowerHAL.vsyncHint(false);
+        mDebugVsyncEnabled = false;
+    }
 }
 
 void EventThread::dump(String8& result) const {
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 5e88693..f6ab4a7 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -39,7 +39,21 @@
 
 // ---------------------------------------------------------------------------
 
-class EventThread : public Thread {
+
+class VSyncSource : public virtual RefBase {
+public:
+    class Callback: public virtual RefBase {
+    public:
+        virtual ~Callback() {}
+        virtual void onVSyncEvent(nsecs_t when) = 0;
+    };
+
+    virtual ~VSyncSource() {}
+    virtual void setVSyncEnabled(bool enable) = 0;
+    virtual void setCallback(const sp<Callback>& callback) = 0;
+};
+
+class EventThread : public Thread, private VSyncSource::Callback {
     class Connection : public BnDisplayEventConnection {
     public:
         Connection(const sp<EventThread>& eventThread);
@@ -62,7 +76,7 @@
 
 public:
 
-    EventThread(const sp<SurfaceFlinger>& flinger);
+    EventThread(const sp<VSyncSource>& src);
 
     sp<Connection> createEventConnection() const;
     status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -76,8 +90,7 @@
     // called after the screen is turned on from main thread
     void onScreenAcquired();
 
-    // called when receiving a vsync event
-    void onVSyncReceived(int type, nsecs_t timestamp);
+    // called when receiving a hotplug event
     void onHotplugReceived(int type, bool connected);
 
     Vector< sp<EventThread::Connection> > waitForEvent(
@@ -89,12 +102,14 @@
     virtual bool        threadLoop();
     virtual void        onFirstRef();
 
+    virtual void onVSyncEvent(nsecs_t timestamp);
+
     void removeDisplayEventConnection(const wp<Connection>& connection);
     void enableVSyncLocked();
     void disableVSyncLocked();
 
     // constants
-    sp<SurfaceFlinger> mFlinger;
+    sp<VSyncSource> mVSyncSource;
     PowerHAL mPowerHAL;
 
     mutable Mutex mLock;
@@ -105,6 +120,7 @@
     Vector< DisplayEventReceiver::Event > mPendingEvents;
     DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
     bool mUseSoftwareVSync;
+    bool mVsyncEnabled;
 
     // for debugging
     bool mDebugVsyncEnabled;
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 46f628d..ba82cad 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -30,9 +30,25 @@
 // ---------------------------------------------------------------------------
 
 RenderEngine* RenderEngine::create(EGLDisplay display, EGLConfig config) {
+    EGLint renderableType = 0;
+    EGLint contextClientVersion = 0;
+
+    // query the renderable type, setting the EGL_CONTEXT_CLIENT_VERSION accordingly
+    if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+        LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+    }
+
+    if (renderableType & EGL_OPENGL_ES2_BIT) {
+        contextClientVersion = 2;
+    } else if (renderableType & EGL_OPENGL_ES_BIT) {
+        contextClientVersion = 1;
+    } else {
+        LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+    }
+
     // Also create our EGLContext
     EGLint contextAttributes[] = {
-            EGL_CONTEXT_CLIENT_VERSION, 2,      // MUST be first
+            EGL_CONTEXT_CLIENT_VERSION, contextClientVersion,      // MUST be first
 #ifdef EGL_IMG_context_priority
 #ifdef HAS_CONTEXT_PRIORITY
 #warning "using EGL_IMG_context_priority"
@@ -41,13 +57,7 @@
 #endif
             EGL_NONE, EGL_NONE
     };
-
     EGLContext ctxt = eglCreateContext(display, config, NULL, contextAttributes);
-    if (ctxt == EGL_NO_CONTEXT) {
-        // maybe ES 2.x is not supported
-        ALOGW("can't create an ES 2.x context, trying 1.x");
-        ctxt = eglCreateContext(display, config, NULL, contextAttributes + 2);
-    }
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt==EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -191,6 +201,10 @@
     glDeleteTextures(count, names);
 }
 
+void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) {
+    glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+}
+
 void RenderEngine::dump(String8& result) {
     const GLExtensions& extensions(GLExtensions::getInstance());
     result.appendFormat("GLES: %s, %s, %s\n",
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 5f331d4..3c7f9ab 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -70,6 +70,7 @@
     void disableScissor();
     void genTextures(size_t count, uint32_t* names);
     void deleteTextures(size_t count, uint32_t const* names);
+    void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels);
 
     class BindImageAsFramebuffer {
         RenderEngine& mEngine;
@@ -106,8 +107,6 @@
     virtual size_t getMaxTextureSize() const = 0;
     virtual size_t getMaxViewportDims() const = 0;
 
-
-
     EGLContext getEGLContext() const;
 };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index def4618..3733ede 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -59,6 +59,7 @@
 #include "Colorizer.h"
 #include "DdmConnection.h"
 #include "DisplayDevice.h"
+#include "DispSync.h"
 #include "EventThread.h"
 #include "Layer.h"
 #include "LayerDim.h"
@@ -84,6 +85,37 @@
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
 namespace android {
+
+// This works around the lack of support for the sync framework on some
+// devices.
+#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK
+static const bool runningWithoutSyncFramework = true;
+#else
+static const bool runningWithoutSyncFramework = false;
+#endif
+
+// This is the phase offset in nanoseconds of the software vsync event
+// relative to the vsync event reported by HWComposer.  The software vsync
+// event is when SurfaceFlinger and Choreographer-based applications run each
+// frame.
+//
+// This phase offset allows adjustment of the minimum latency from application
+// wake-up (by Choregographer) time to the time at which the resulting window
+// image is displayed.  This value may be either positive (after the HW vsync)
+// or negative (before the HW vsync).  Setting it to 0 will result in a
+// minimum latency of two vsync periods because the app and SurfaceFlinger
+// will run just after the HW vsync.  Setting it to a positive number will
+// result in the minimum latency being:
+//
+//     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
+//
+// Note that reducing this latency makes it more likely for the applications
+// to not have their window content image ready in time.  When this happens
+// the latency will end up being an additional vsync period, and animations
+// will hiccup.  Therefore, this latency should be tuned somewhat
+// conservatively (or at least with awareness of the trade-off being made).
+static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
+
 // ---------------------------------------------------------------------------
 
 const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -114,6 +146,7 @@
         mDebugInTransaction(0),
         mLastTransactionTime(0),
         mBootFinished(false),
+        mPrimaryHWVsyncEnabled(false),
         mDaltonize(false)
 {
     ALOGI("SurfaceFlinger is starting");
@@ -366,56 +399,103 @@
     operator EGLint const* () const { return &mList.keyAt(0).v; }
 };
 
-EGLConfig SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisualId) {
+status_t SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisualId,
+    EGLint renderableType, EGLConfig* config) {
     // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
     // it is to be used with WIFI displays
-    EGLConfig config;
-    EGLint dummy;
     status_t err;
+    EGLint wantedAttribute;
+    EGLint wantedAttributeValue;
 
     EGLAttributeVector attribs;
-    // TODO: enable ES2
-    //attribs[EGL_RENDERABLE_TYPE]            = EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT;
-    attribs[EGL_SURFACE_TYPE]               = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
-    attribs[EGL_RECORDABLE_ANDROID]         = EGL_TRUE;
-    attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
-    attribs[EGL_RED_SIZE]                   = 8;
-    attribs[EGL_GREEN_SIZE]                 = 8;
-    attribs[EGL_BLUE_SIZE]                  = 8;
+    if (renderableType) {
+        attribs[EGL_RENDERABLE_TYPE]            = renderableType;
+        attribs[EGL_RECORDABLE_ANDROID]         = EGL_TRUE;
+        attribs[EGL_SURFACE_TYPE]               = EGL_WINDOW_BIT|EGL_PBUFFER_BIT;
+        attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
+        attribs[EGL_RED_SIZE]                   = 8;
+        attribs[EGL_GREEN_SIZE]                 = 8;
+        attribs[EGL_BLUE_SIZE]                  = 8;
+        wantedAttribute                         = EGL_NONE;
+        wantedAttributeValue                    = EGL_NONE;
 
-    err = selectConfigForAttribute(display, attribs, EGL_NONE, EGL_NONE, &config);
-    if (!err)
-        goto success;
+    } else {
+        // if no renderable type specified, fallback to a simplified query
+        wantedAttribute                         = EGL_NATIVE_VISUAL_ID;
+        wantedAttributeValue                    = nativeVisualId;
+    }
 
-    // this didn't work, probably because we're on the emulator...
-    // try a simplified query
-    ALOGW("no suitable EGLConfig found, trying a simpler query");
-    attribs.remove(EGL_RENDERABLE_TYPE);
-    attribs.remove(EGL_FRAMEBUFFER_TARGET_ANDROID);
-    attribs.remove(EGL_RECORDABLE_ANDROID);
-    attribs.remove(EGL_RED_SIZE);
-    attribs.remove(EGL_GREEN_SIZE);
-    attribs.remove(EGL_BLUE_SIZE);
-    err = selectConfigForAttribute(display, attribs,
-            EGL_NATIVE_VISUAL_ID, nativeVisualId, &config);
-    if (!err)
-        goto success;
-
-    // this EGL is too lame for Android
-    LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
-    return 0;
-
-success:
-    if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy))
-        ALOGW_IF(dummy == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
-    return config;
+    err = selectConfigForAttribute(display, attribs, wantedAttribute,
+        wantedAttributeValue, config);
+    if (err == NO_ERROR) {
+        EGLint caveat;
+        if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+            ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+    }
+    return err;
 }
 
-void SurfaceFlinger::init() {
+class DispSyncSource : public VSyncSource, private DispSync::Callback {
+public:
+    DispSyncSource(DispSync* dispSync) : mValue(0), mDispSync(dispSync) {}
 
+    virtual ~DispSyncSource() {}
+
+    virtual void setVSyncEnabled(bool enable) {
+        // Do NOT lock the mutex here so as to avoid any mutex ordering issues
+        // with locking it in the onDispSyncEvent callback.
+        if (enable) {
+            status_t err = mDispSync->addEventListener(vsyncPhaseOffsetNs,
+                    static_cast<DispSync::Callback*>(this));
+            if (err != NO_ERROR) {
+                ALOGE("error registering vsync callback: %s (%d)",
+                        strerror(-err), err);
+            }
+            ATRACE_INT("VsyncOn", 1);
+        } else {
+            status_t err = mDispSync->removeEventListener(
+                    static_cast<DispSync::Callback*>(this));
+            if (err != NO_ERROR) {
+                ALOGE("error unregistering vsync callback: %s (%d)",
+                        strerror(-err), err);
+            }
+            ATRACE_INT("VsyncOn", 0);
+        }
+    }
+
+    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+        Mutex::Autolock lock(mMutex);
+        mCallback = callback;
+    }
+
+private:
+    virtual void onDispSyncEvent(nsecs_t when) {
+        sp<VSyncSource::Callback> callback;
+        {
+            Mutex::Autolock lock(mMutex);
+            callback = mCallback;
+
+            mValue = (mValue + 1) % 2;
+            ATRACE_INT("VSYNC", mValue);
+        }
+
+        if (callback != NULL) {
+            callback->onVSyncEvent(when);
+        }
+    }
+
+    int mValue;
+
+    DispSync* mDispSync;
+    sp<VSyncSource::Callback> mCallback;
+    Mutex mMutex;
+};
+
+void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
 
+    status_t err;
     Mutex::Autolock _l(mStateLock);
 
     // initialize EGL for the default display
@@ -427,8 +507,27 @@
     mHwc = new HWComposer(this,
             *static_cast<HWComposer::EventHandler *>(this));
 
-    // initialize the config and context (can't fail)
-    mEGLConfig = selectEGLConfig(mEGLDisplay, mHwc->getVisualID());
+    // First try to get an ES2 config
+    err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), EGL_OPENGL_ES2_BIT,
+            &mEGLConfig);
+
+    if (err != NO_ERROR) {
+        // If ES2 fails, try ES1
+        err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(),
+                EGL_OPENGL_ES_BIT, &mEGLConfig);
+    }
+
+    if (err != NO_ERROR) {
+        // still didn't work, probably because we're on the emulator...
+        // try a simplified query
+        ALOGW("no suitable EGLConfig found, trying a simpler query");
+        err = selectEGLConfig(mEGLDisplay, mHwc->getVisualID(), 0, &mEGLConfig);
+    }
+
+    if (err != NO_ERROR) {
+        // this EGL is too lame for android
+        LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+    }
 
     // print some debugging info
     EGLint r,g,b,a;
@@ -488,9 +587,15 @@
     getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
 
     // start the EventThread
-    mEventThread = new EventThread(this);
+    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync);
+    mEventThread = new EventThread(vsyncSrc);
     mEventQueue.setEventThread(mEventThread);
 
+    // set a fake vsync period if there is no HWComposer
+    if (mHwc->initCheck() != NO_ERROR) {
+        mPrimaryDispSync.setPeriod(16666667);
+    }
+
     // initialize our drawing state
     mDrawingState = mCurrentState;
 
@@ -645,17 +750,49 @@
     } while (true);
 }
 
-void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
-    if (mEventThread == NULL) {
-        // This is a temporary workaround for b/7145521.  A non-null pointer
-        // does not mean EventThread has finished initializing, so this
-        // is not a correct fix.
-        ALOGW("WARNING: EventThread not started, ignoring vsync");
-        return;
+void SurfaceFlinger::enableHardwareVsync() {
+    Mutex::Autolock _l(mHWVsyncLock);
+    if (!mPrimaryHWVsyncEnabled) {
+        mPrimaryDispSync.beginResync();
+        eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+        mPrimaryHWVsyncEnabled = true;
     }
-    if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
-        // we should only receive DisplayDevice::DisplayType from the vsync callback
-        mEventThread->onVSyncReceived(type, timestamp);
+}
+
+void SurfaceFlinger::resyncToHardwareVsync() {
+    Mutex::Autolock _l(mHWVsyncLock);
+
+    const nsecs_t period =
+            getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+
+    mPrimaryDispSync.reset();
+    mPrimaryDispSync.setPeriod(period);
+
+    if (!mPrimaryHWVsyncEnabled) {
+        mPrimaryDispSync.beginResync();
+        eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+        mPrimaryHWVsyncEnabled = true;
+    }
+}
+
+void SurfaceFlinger::disableHardwareVsync() {
+    Mutex::Autolock _l(mHWVsyncLock);
+    if (mPrimaryHWVsyncEnabled) {
+        eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
+        mPrimaryDispSync.endResync();
+        mPrimaryHWVsyncEnabled = false;
+    }
+}
+
+void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
+    if (type == 0) {
+        bool needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
+
+        if (needsHwVsync) {
+            enableHardwareVsync();
+        } else {
+            disableHardwareVsync();
+        }
     }
 }
 
@@ -683,6 +820,7 @@
 }
 
 void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
+    ATRACE_CALL();
     getHwComposer().eventControl(disp, event, enabled);
 }
 
@@ -788,11 +926,27 @@
         layers[i]->onPostComposition();
     }
 
+    const HWComposer& hwc = getHwComposer();
+    sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+
+    if (presentFence->isValid()) {
+        if (mPrimaryDispSync.addPresentFence(presentFence)) {
+            enableHardwareVsync();
+        } else {
+            disableHardwareVsync();
+        }
+    }
+
+    if (runningWithoutSyncFramework) {
+        const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+        if (hw->isScreenAcquired()) {
+            enableHardwareVsync();
+        }
+    }
+
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
 
-        const HWComposer& hwc = getHwComposer();
-        sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
         if (presentFence->isValid()) {
             mAnimFrameTracker.setActualPresentFence(presentFence);
         } else {
@@ -949,6 +1103,12 @@
         hwc.commit();
     }
 
+    // make the default display current because the VirtualDisplayDevice code cannot
+    // deal with dequeueBuffer() being called outside of the composition loop; however
+    // the code below can call glFlush() which is allowed (and does in some case) call
+    // dequeueBuffer().
+    getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
+
     for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
         sp<const DisplayDevice> hw(mDisplays[dpy]);
         const Vector< sp<Layer> >& currentLayers(hw->getVisibleLayersSortedByZ());
@@ -1507,7 +1667,7 @@
     HWComposer::LayerListIterator cur = hwc.begin(id);
     const HWComposer::LayerListIterator end = hwc.end(id);
 
-    const bool hasGlesComposition = hwc.hasGlesComposition(id) || (cur==end);
+    bool hasGlesComposition = hwc.hasGlesComposition(id);
     if (hasGlesComposition) {
         if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) {
             ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -1580,9 +1740,10 @@
             if (!clip.isEmpty()) {
                 switch (cur->getCompositionType()) {
                     case HWC_OVERLAY: {
+                        const Layer::State& state(layer->getDrawingState());
                         if ((cur->getHints() & HWC_HINT_CLEAR_FB)
                                 && i
-                                && layer->isOpaque()
+                                && layer->isOpaque() && (state.alpha == 0xFF)
                                 && hasGlesComposition) {
                             // never clear the very first layer since we're
                             // guaranteed the FB is already cleared
@@ -2016,6 +2177,8 @@
         if (type == DisplayDevice::DISPLAY_PRIMARY) {
             // FIXME: eventthread only knows about the main display right now
             mEventThread->onScreenAcquired();
+
+            resyncToHardwareVsync();
         }
     }
     mVisibleRegionsDirty = true;
@@ -2818,6 +2981,15 @@
                         // dependent on the context's EGLConfig.
                         renderScreenImplLocked(hw, reqWidth, reqHeight,
                                 minLayerZ, maxLayerZ, true);
+
+                        if (DEBUG_SCREENSHOTS) {
+                            uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
+                            getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
+                            checkScreenshot(reqWidth, reqHeight, reqWidth, pixels,
+                                    hw, minLayerZ, maxLayerZ);
+                            delete [] pixels;
+                        }
+
                     } else {
                         ALOGE("got GL_FRAMEBUFFER_COMPLETE_OES error while taking screenshot");
                         result = INVALID_OPERATION;
@@ -2838,13 +3010,12 @@
     return result;
 }
 
-void SurfaceFlinger::checkScreenshot(const sp<GraphicBuffer>& buf, void const* vaddr,
-        const sp<const DisplayDevice>& hw,
-        uint32_t minLayerZ, uint32_t maxLayerZ) {
+void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
+        const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
     if (DEBUG_SCREENSHOTS) {
-        for (ssize_t y=0 ; y<buf->height ; y++) {
-            uint32_t const * p = (uint32_t const *)vaddr + y*buf->stride;
-            for (ssize_t x=0 ; x<buf->width ; x++) {
+        for (size_t y=0 ; y<h ; y++) {
+            uint32_t const * p = (uint32_t const *)vaddr + y*s;
+            for (size_t x=0 ; x<w ; x++) {
                 if (p[x] != 0xFF000000) return;
             }
         }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 347e3e3..f1c19c2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -48,6 +48,7 @@
 
 #include "Barrier.h"
 #include "DisplayDevice.h"
+#include "DispSync.h"
 #include "FrameTracker.h"
 #include "MessageQueue.h"
 
@@ -317,7 +318,8 @@
      */
     static status_t selectConfigForAttribute(EGLDisplay dpy,
         EGLint const* attrs, EGLint attribute, EGLint value, EGLConfig* outConfig);
-    static EGLConfig selectEGLConfig(EGLDisplay disp, EGLint visualId);
+    static status_t selectEGLConfig(EGLDisplay disp, EGLint visualId,
+        EGLint renderableType, EGLConfig* config);
     size_t getMaxTextureSize() const;
     size_t getMaxViewportDims() const;
 
@@ -377,6 +379,12 @@
      * Display management
      */
 
+    /* ------------------------------------------------------------------------
+     * VSync
+     */
+     void enableHardwareVsync();
+     void disableHardwareVsync();
+     void resyncToHardwareVsync();
 
     /* ------------------------------------------------------------------------
      * Debugging & dumpsys
@@ -387,7 +395,7 @@
     void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const;
     bool startDdmConnection();
     static void appendSfConfigString(String8& result);
-    void checkScreenshot(const sp<GraphicBuffer>& buf, void const* vaddr,
+    void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
             const sp<const DisplayDevice>& hw,
             uint32_t minLayerZ, uint32_t maxLayerZ);
 
@@ -450,11 +458,16 @@
     // these are thread safe
     mutable MessageQueue mEventQueue;
     FrameTracker mAnimFrameTracker;
+    DispSync mPrimaryDispSync;
 
     // protected by mDestroyedLayerLock;
     mutable Mutex mDestroyedLayerLock;
     Vector<Layer const *> mDestroyedLayers;
 
+    // protected by mHWVsyncLock
+    Mutex mHWVsyncLock;
+    bool mPrimaryHWVsyncEnabled;
+
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */