Early wake-up for transitions (1/2)

On some devices it's very likely that we fall into GL comp during
app transitions. However, SF offsets are chosen in a way such that
the time to finish a frame is just too tight to be completely jank
free when hitting GL composition in SurfaceFlinger. Thus, we
introduce the concept of a separate early offset, and wakeup
SurfaceFlinger at that time if we think that hitting GL comp is
likely, or we already hit GL comp in the last frame.

Test: Open app, check vsync offsets in systrace
Test: Open many dialogs/apps to fall into GPU comp.
Bug: 75985430
Change-Id: Ie17e30c4575359fa11bb8912f68dcafe3e569ddb
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 9e01fd0..7acbd11 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -209,6 +209,28 @@
         return BAD_VALUE;
     }
 
+    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
+        Mutex::Autolock lock(mMutex);
+
+        for (size_t i = 0; i < mEventListeners.size(); i++) {
+            if (mEventListeners[i].mCallback == callback) {
+                EventListener& listener = mEventListeners.editItemAt(i);
+                const nsecs_t oldPhase = listener.mPhase;
+                listener.mPhase = phase;
+
+                // Pretend that the last time this event was handled at the same frame but with the
+                // new offset to allow for a seamless offset change without double-firing or
+                // skipping.
+                listener.mLastEventTime -= (oldPhase - phase);
+                mCond.signal();
+                return NO_ERROR;
+            }
+        }
+
+        return BAD_VALUE;
+    }
+
     // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
     // case.
     bool hasAnyEventListeners() {
@@ -487,6 +509,11 @@
     return mThread->removeEventListener(callback);
 }
 
+status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
+    Mutex::Autolock lock(mMutex);
+    return mThread->changePhaseOffset(callback, phase);
+}
+
 void DispSync::setPeriod(nsecs_t period) {
     Mutex::Autolock lock(mMutex);
     mPeriod = period;