Merge "Change API from flush(handle) to flush(). Call flush on all active sensors in the given SensorEventConnection." into klp-dev
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..b2a604c
--- /dev/null
+++ b/services/surfaceflinger/DispSync.cpp
@@ -0,0 +1,483 @@
+/*
+ * 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;
+    mThread->updateModel(mPeriod, mPhase);
+}
+
+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 7132b2f..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);
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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e374548..4a1373e 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");
@@ -402,8 +435,63 @@
     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...");
 
@@ -499,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;
 
@@ -656,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();
+        }
     }
 }
 
@@ -694,6 +820,7 @@
 }
 
 void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
+    ATRACE_CALL();
     getHwComposer().eventControl(disp, event, enabled);
 }
 
@@ -799,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 {
@@ -2034,6 +2177,8 @@
         if (type == DisplayDevice::DISPLAY_PRIMARY) {
             // FIXME: eventthread only knows about the main display right now
             mEventThread->onScreenAcquired();
+
+            resyncToHardwareVsync();
         }
     }
     mVisibleRegionsDirty = true;
@@ -2837,6 +2982,27 @@
                         renderScreenImplLocked(hw, reqWidth, reqHeight,
                                 minLayerZ, maxLayerZ, true);
 
+                        // Create a sync point and wait on it, so we know the buffer is
+                        // ready before we pass it along.  We can't trivially call glFlush(),
+                        // so we use a wait flag instead.
+                        // TODO: pass a sync fd to queueBuffer() and let the consumer wait.
+                        EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
+                        if (sync != EGL_NO_SYNC_KHR) {
+                            EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync,
+                                    EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 2000000000 /*2 sec*/);
+                            EGLint eglErr = eglGetError();
+                            eglDestroySyncKHR(mEGLDisplay, sync);
+                            if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+                                ALOGW("captureScreen: fence wait timed out");
+                            } else {
+                                ALOGW_IF(eglErr != EGL_SUCCESS,
+                                        "captureScreen: error waiting on EGL fence: %#x", eglErr);
+                            }
+                        } else {
+                            ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError());
+                            // not fatal
+                        }
+
                         if (DEBUG_SCREENSHOTS) {
                             uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
                             getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0e9955c..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"
 
@@ -378,6 +379,12 @@
      * Display management
      */
 
+    /* ------------------------------------------------------------------------
+     * VSync
+     */
+     void enableHardwareVsync();
+     void disableHardwareVsync();
+     void resyncToHardwareVsync();
 
     /* ------------------------------------------------------------------------
      * Debugging & dumpsys
@@ -451,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
      */