Improve the VSYNC api a bit.

- add the ability to set the vsync delivery rate, when the rate is
set to N>1 (ie: receive every N vsync), SF process' is woken up for
all of vsync, but clients only see the every N events.

- add the concept of one-shot vsync events, with a call-back
to request the next one. currently the call-back is a binder IPC.

Change-Id: I09f71df0b0ba0d88ed997645e2e2497d553c9a1b
diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp
index a0aa9c0..77ecbd2 100644
--- a/services/surfaceflinger/DisplayEventConnection.cpp
+++ b/services/surfaceflinger/DisplayEventConnection.cpp
@@ -25,6 +25,7 @@
 
 #include "SurfaceFlinger.h"
 #include "DisplayEventConnection.h"
+#include "EventThread.h"
 
 // ---------------------------------------------------------------------------
 
@@ -33,30 +34,38 @@
 // ---------------------------------------------------------------------------
 
 DisplayEventConnection::DisplayEventConnection(
-        const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger), mChannel(new BitTube())
+        const sp<EventThread>& eventThread)
+    : mEventThread(eventThread), mChannel(new BitTube())
 {
 }
 
 DisplayEventConnection::~DisplayEventConnection() {
-    mFlinger->cleanupDisplayEventConnection(this);
+    mEventThread->unregisterDisplayEventConnection(this);
 }
 
 void DisplayEventConnection::onFirstRef() {
-    // nothing to do here for now.
+    // NOTE: mEventThread doesn't hold a strong reference on us
+    mEventThread->registerDisplayEventConnection(this);
 }
 
 sp<BitTube> DisplayEventConnection::getDataChannel() const {
     return mChannel;
 }
 
+void DisplayEventConnection::setVsyncRate(uint32_t count) {
+    mEventThread->setVsyncRate(count, this);
+}
+
+void DisplayEventConnection::requestNextVsync() {
+    mEventThread->requestNextVsync(this);
+}
+
 status_t DisplayEventConnection::postEvent(const DisplayEventReceiver::Event& event)
 {
     ssize_t size = mChannel->write(&event, sizeof(DisplayEventReceiver::Event));
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
 }
 
-
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayEventConnection.h b/services/surfaceflinger/DisplayEventConnection.h
index 46cf64b..cc3ee36 100644
--- a/services/surfaceflinger/DisplayEventConnection.h
+++ b/services/surfaceflinger/DisplayEventConnection.h
@@ -32,13 +32,13 @@
 // ---------------------------------------------------------------------------
 
 class BitTube;
-class SurfaceFlinger;
+class EventThread;
 
 // ---------------------------------------------------------------------------
 
 class DisplayEventConnection : public BnDisplayEventConnection {
 public:
-    DisplayEventConnection(const sp<SurfaceFlinger>& flinger);
+    DisplayEventConnection(const sp<EventThread>& flinger);
 
     status_t postEvent(const DisplayEventReceiver::Event& event);
 
@@ -46,8 +46,10 @@
     virtual ~DisplayEventConnection();
     virtual void onFirstRef();
     virtual sp<BitTube> getDataChannel() const;
+    virtual void setVsyncRate(uint32_t count);
+    virtual void requestNextVsync();    // asynchronous
 
-    sp<SurfaceFlinger> const mFlinger;
+    sp<EventThread> const mEventThread;
     sp<BitTube> const mChannel;
 };
 
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index 42477a9..dc39f88 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -47,7 +47,8 @@
 status_t EventThread::registerDisplayEventConnection(
         const sp<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.add(connection);
+    ConnectionInfo info;
+    mDisplayEventConnections.add(connection, info);
     mCondition.signal();
     return NO_ERROR;
 }
@@ -55,44 +56,97 @@
 status_t EventThread::unregisterDisplayEventConnection(
         const wp<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.remove(connection);
+    mDisplayEventConnections.removeItem(connection);
     mCondition.signal();
     return NO_ERROR;
 }
 
-status_t EventThread::removeDisplayEventConnection(
+void EventThread::removeDisplayEventConnection(
         const wp<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.remove(connection);
-    return NO_ERROR;
+    mDisplayEventConnections.removeItem(connection);
+}
+
+EventThread::ConnectionInfo* EventThread::getConnectionInfoLocked(
+        const wp<DisplayEventConnection>& connection) {
+    ssize_t index = mDisplayEventConnections.indexOfKey(connection);
+    if (index < 0) return NULL;
+    return &mDisplayEventConnections.editValueAt(index);
+}
+
+void EventThread::setVsyncRate(uint32_t count,
+        const wp<DisplayEventConnection>& connection) {
+    if (int32_t(count) >= 0) { // server must protect against bad params
+        Mutex::Autolock _l(mLock);
+        ConnectionInfo* info = getConnectionInfoLocked(connection);
+        if (info) {
+            info->count = (count == 0) ? -1 : count;
+            mCondition.signal();
+        }
+    }
+}
+
+void EventThread::requestNextVsync(
+        const wp<DisplayEventConnection>& connection) {
+    Mutex::Autolock _l(mLock);
+    ConnectionInfo* info = getConnectionInfoLocked(connection);
+    if (info) {
+        if (info->count < 0) {
+            info->count = 0;
+        }
+        mCondition.signal();
+    }
 }
 
 bool EventThread::threadLoop() {
 
     nsecs_t timestamp;
     DisplayEventReceiver::Event vsync;
-    SortedVector<wp<DisplayEventConnection> > displayEventConnections;
+    KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > displayEventConnections;
 
     { // scope for the lock
         Mutex::Autolock _l(mLock);
         do {
             // wait for listeners
-            while (!mDisplayEventConnections.size()) {
+            do {
+                bool waitForNextVsync = false;
+                size_t count = mDisplayEventConnections.size();
+                for (size_t i=0 ; i<count ; i++) {
+                    const ConnectionInfo& info(
+                            mDisplayEventConnections.valueAt(i));
+                    if (info.count >= 1) {
+                        // continuous mode
+                        waitForNextVsync = true;
+                    } else {
+                        // one-shot event
+                        if (info.count >= -1) {
+                            ConnectionInfo& info(
+                                    mDisplayEventConnections.editValueAt(i));
+                            info.count--;
+                            if (info.count == -1) {
+                                // fired this time around
+                                waitForNextVsync = true;
+                            }
+                        }
+                    }
+                }
+
+                if (waitForNextVsync)
+                    break;
+
                 mCondition.wait(mLock);
-            }
+            } while(true);
 
             // wait for vsync
             mLock.unlock();
             timestamp = mHw.waitForVSync();
             mLock.lock();
+            mDeliveredEvents++;
 
             // make sure we still have some listeners
         } while (!mDisplayEventConnections.size());
 
-
         // dispatch vsync events to listeners...
-        mDeliveredEvents++;
-
         vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
         vsync.header.timestamp = timestamp;
         vsync.vsync.count = mDeliveredEvents;
@@ -104,9 +158,30 @@
 
     const size_t count = displayEventConnections.size();
     for (size_t i=0 ; i<count ; i++) {
-        sp<DisplayEventConnection> conn(displayEventConnections.itemAt(i).promote());
+        sp<DisplayEventConnection> conn(displayEventConnections.keyAt(i).promote());
         // make sure the connection didn't die
         if (conn != NULL) {
+
+            const ConnectionInfo& info(
+                    displayEventConnections.valueAt(i));
+
+            if ((info.count > 1) && (mDeliveredEvents % info.count)) {
+                // continuous event, but not time to send this event yet
+                continue;
+            } else if (info.count < -1) {
+                // disabled event
+                continue;
+            } else if (info.count == 0) {
+                // impossible by construction. but we prefer to be safe.
+                continue;
+            }
+
+            // here, either:
+            // count = -1 : one-shot scheduled this time around
+            // count =  1 : continuous not rate-limited
+            // count >  1 : continuous, rate-limited
+            // Note: count == 0 is not possible by construction
+
             status_t err = conn->postEvent(vsync);
             if (err == -EAGAIN || err == -EWOULDBLOCK) {
                 // The destination doesn't accept events anymore, it's probably
@@ -118,12 +193,12 @@
                 // handle any other error on the pipe as fatal. the only
                 // reasonable thing to do is to clean-up this connection.
                 // The most common error we'll get here is -EPIPE.
-                removeDisplayEventConnection(displayEventConnections.itemAt(i));
+                removeDisplayEventConnection(displayEventConnections.keyAt(i));
             }
         } else {
             // somehow the connection is dead, but we still have it in our list
             // just clean the list.
-            removeDisplayEventConnection(displayEventConnections.itemAt(i));
+            removeDisplayEventConnection(displayEventConnections.keyAt(i));
         }
     }
 
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 4872c2b..35bd299 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -24,7 +24,7 @@
 
 #include <utils/Errors.h>
 #include <utils/threads.h>
-#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
 
 #include "DisplayEventConnection.h"
 
@@ -51,6 +51,11 @@
     status_t unregisterDisplayEventConnection(
             const wp<DisplayEventConnection>& connection);
 
+    void setVsyncRate(uint32_t count,
+            const wp<DisplayEventConnection>& connection);
+
+    void requestNextVsync(const wp<DisplayEventConnection>& connection);
+
     void dump(String8& result, char* buffer, size_t SIZE) const;
 
 private:
@@ -58,7 +63,20 @@
     virtual status_t    readyToRun();
     virtual void        onFirstRef();
 
-    status_t removeDisplayEventConnection(
+    struct ConnectionInfo {
+        ConnectionInfo() : count(-1) { }
+
+        // count >= 1 : continuous event. count is the vsync rate
+        // count == 0 : one-shot event that has not fired
+        // count ==-1 : one-shot event that fired this round / disabled
+        // count ==-2 : one-shot event that fired the round before
+        int32_t count;
+    };
+
+    void removeDisplayEventConnection(
+            const wp<DisplayEventConnection>& connection);
+
+    ConnectionInfo* getConnectionInfoLocked(
             const wp<DisplayEventConnection>& connection);
 
     // constants
@@ -69,7 +87,9 @@
     mutable Condition mCondition;
 
     // protected by mLock
-    SortedVector<wp<DisplayEventConnection> > mDisplayEventConnections;
+    KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections;
+
+    // main thread only
     size_t mDeliveredEvents;
 };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 58196d8..014c7e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -389,16 +389,10 @@
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
-    sp<DisplayEventConnection> result(new DisplayEventConnection(this));
-    mEventThread->registerDisplayEventConnection(result);
+    sp<DisplayEventConnection> result(new DisplayEventConnection(mEventThread));
     return result;
 }
 
-void SurfaceFlinger::cleanupDisplayEventConnection(
-        const wp<DisplayEventConnection>& connection) {
-    mEventThread->unregisterDisplayEventConnection(connection);
-}
-
 // ----------------------------------------------------------------------------
 #if 0
 #pragma mark -
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e6d2cd9..41caee3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -335,9 +335,6 @@
             status_t electronBeamOffAnimationImplLocked();
             status_t electronBeamOnAnimationImplLocked();
 
-            void cleanupDisplayEventConnection(
-                    const wp<DisplayEventConnection>& connection);
-
             void        debugFlashRegions();
             void        debugShowFPS() const;
             void        drawWormhole() const;