Added Display increments, more controls, and a readme to the replayer

Change-Id: Id21144b7f808858149772de511951546fddfb252
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
index cad11a0..77de8dc 100644
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.cpp
@@ -23,7 +23,7 @@
 using namespace android;
 
 BufferQueueScheduler::BufferQueueScheduler(
-        const sp<SurfaceControl>& surfaceControl, const RGB& color, int id)
+        const sp<SurfaceControl>& surfaceControl, const HSV& color, int id)
       : mSurfaceControl(surfaceControl), mColor(color), mSurfaceId(id), mContinueScheduling(true) {}
 
 void BufferQueueScheduler::startScheduling() {
@@ -44,6 +44,7 @@
 
             bufferUpdate(event.dimensions);
             fillSurface(event.event);
+            mColor.modulate();
             lock.lock();
             mBufferEvents.pop();
         }
@@ -64,7 +65,7 @@
 }
 
 void BufferQueueScheduler::setSurfaceControl(
-        const sp<SurfaceControl>& surfaceControl, const RGB& color) {
+        const sp<SurfaceControl>& surfaceControl, const HSV& color) {
     std::lock_guard<std::mutex> lock(mMutex);
     mSurfaceControl = surfaceControl;
     mColor = color;
@@ -76,7 +77,7 @@
     s->setBuffersDimensions(dimensions.width, dimensions.height);
 }
 
-void BufferQueueScheduler::fillSurface(std::shared_ptr<Event> event) {
+void BufferQueueScheduler::fillSurface(const std::shared_ptr<Event>& event) {
     ANativeWindow_Buffer outBuffer;
     sp<Surface> s = mSurfaceControl->getSurface();
 
@@ -87,13 +88,15 @@
         return;
     }
 
+    auto color = mColor.getRGB();
+
     auto img = reinterpret_cast<uint8_t*>(outBuffer.bits);
     for (int y = 0; y < outBuffer.height; y++) {
         for (int x = 0; x < outBuffer.width; x++) {
             uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = mColor.r;
-            pixel[1] = mColor.g;
-            pixel[2] = mColor.b;
+            pixel[0] = color.r;
+            pixel[1] = color.g;
+            pixel[2] = color.b;
             pixel[3] = LAYER_ALPHA;
         }
     }
diff --git a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
index fc8e20c..cb20fcc 100644
--- a/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
+++ b/cmds/surfacereplayer/replayer/BufferQueueScheduler.h
@@ -52,23 +52,23 @@
 
 class BufferQueueScheduler {
   public:
-    BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const RGB& color, int id);
+    BufferQueueScheduler(const sp<SurfaceControl>& surfaceControl, const HSV& color, int id);
 
     void startScheduling();
     void addEvent(const BufferEvent&);
     void stopScheduling();
 
-    void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const RGB& color);
+    void setSurfaceControl(const sp<SurfaceControl>& surfaceControl, const HSV& color);
 
   private:
     void bufferUpdate(const Dimensions& dimensions);
 
     // Lock and fill the surface, block until the event is signaled by the main loop,
     // then unlock and post the buffer.
-    void fillSurface(std::shared_ptr<Event>);
+    void fillSurface(const std::shared_ptr<Event>& event);
 
     sp<SurfaceControl> mSurfaceControl;
-    RGB mColor;
+    HSV mColor;
     const int mSurfaceId;
 
     bool mContinueScheduling;
diff --git a/cmds/surfacereplayer/replayer/Color.h b/cmds/surfacereplayer/replayer/Color.h
index d644b8d..ce644be 100644
--- a/cmds/surfacereplayer/replayer/Color.h
+++ b/cmds/surfacereplayer/replayer/Color.h
@@ -22,76 +22,96 @@
 
 namespace android {
 
-typedef struct RGB {
-    RGB() = default;
+constexpr double modulateFactor = .0001;
+constexpr double modulateLimit = .80;
+
+struct RGB {
     RGB(uint8_t rIn, uint8_t gIn, uint8_t bIn) : r(rIn), g(gIn), b(bIn) {}
 
     uint8_t r = 0;
     uint8_t g = 0;
     uint8_t b = 0;
-} RGB;
+};
 
-typedef struct HSV {
+struct HSV {
     HSV() = default;
     HSV(double hIn, double sIn, double vIn) : h(hIn), s(sIn), v(vIn) {}
 
     double h = 0;
     double s = 0;
     double v = 0;
-} HSV;
 
-static inline RGB HSVToRGB(HSV hsv) {
+    RGB getRGB() const;
+
+    bool modulateUp = false;
+
+    void modulate();
+};
+
+void inline HSV::modulate() {
+    if(modulateUp) {
+        v += modulateFactor;
+    } else {
+        v -= modulateFactor;
+    }
+
+    if(v <= modulateLimit || v >= 1) {
+        modulateUp = !modulateUp;
+    }
+}
+
+inline RGB HSV::getRGB() const {
     using namespace std;
     double r = 0, g = 0, b = 0;
 
-    if (hsv.s == 0) {
-        r = hsv.v;
-        g = hsv.v;
-        b = hsv.v;
+    if (s == 0) {
+        r = v;
+        g = v;
+        b = v;
     } else {
-        hsv.h = static_cast<int>(hsv.h) % 360;
-        hsv.h = hsv.h / 60;
+        auto tempHue = static_cast<int>(h) % 360;
+        tempHue = tempHue / 60;
 
-        int i = static_cast<int>(trunc(hsv.h));
-        double f = hsv.h - i;
+        int i = static_cast<int>(trunc(tempHue));
+        double f = h - i;
 
-        double x = hsv.v * (1.0 - hsv.s);
-        double y = hsv.v * (1.0 - (hsv.s * f));
-        double z = hsv.v * (1.0 - (hsv.s * (1.0 - f)));
+        double x = v * (1.0 - s);
+        double y = v * (1.0 - (s * f));
+        double z = v * (1.0 - (s * (1.0 - f)));
 
         switch (i) {
             case 0:
-                r = hsv.v;
+                r = v;
                 g = z;
                 b = x;
                 break;
 
             case 1:
                 r = y;
-                g = hsv.v;
+                g = v;
                 b = x;
                 break;
 
             case 2:
                 r = x;
-                g = hsv.v;
+                g = v;
                 b = z;
                 break;
 
             case 3:
                 r = x;
                 g = y;
-                b = hsv.v;
+                b = v;
                 break;
 
             case 4:
                 r = z;
                 g = x;
-                b = hsv.v;
+                b = v;
                 break;
 
             default:
-                r = hsv.v;
+                r = v;
                 g = x;
                 b = y;
                 break;
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp
index bf5da52..dd1dd7d 100644
--- a/cmds/surfacereplayer/replayer/Main.cpp
+++ b/cmds/surfacereplayer/replayer/Main.cpp
@@ -41,9 +41,16 @@
     std::cout << "  -m  Stops the replayer at the start of the trace and switches ";
                  "to manual replay\n";
 
-    std::cout << "  -t [Number of Threads]  Specifies the number of threads to be used while "
+    std::cout << "\n  -t [Number of Threads]  Specifies the number of threads to be used while "
                  "replaying (default is " << android::DEFAULT_THREADS << ")\n";
 
+    std::cout << "\n  -s [Timestamp]  Specify at what timestamp should the replayer switch "
+                 "to manual replay\n";
+
+    std::cout << "  -n  Ignore timestamps and run through trace as fast as possible\n";
+
+    std::cout << "  -l  Indefinitely loop the replayer\n";
+
     std::cout << "  -h  Display help menu\n";
 
     std::cout << std::endl;
@@ -51,11 +58,14 @@
 
 int main(int argc, char** argv) {
     std::string filename;
+    bool loop = false;
+    bool wait = true;
     bool pauseBeginning = false;
     int numThreads = DEFAULT_THREADS;
+    long stopHere = -1;
 
     int opt = 0;
-    while ((opt = getopt(argc, argv, "pt:h?")) != -1) {
+    while ((opt = getopt(argc, argv, "mt:s:nlh?")) != -1) {
         switch (opt) {
             case 'm':
                 pauseBeginning = true;
@@ -63,6 +73,15 @@
             case 't':
                 numThreads = atoi(optarg);
                 break;
+            case 's':
+                stopHere = atol(optarg);
+                break;
+            case 'n':
+                wait = false;
+                break;
+            case 'l':
+                loop = true;
+                break;
             case 'h':
             case '?':
                 printHelpMenu();
@@ -81,8 +100,11 @@
     }
     filename.assign(input[0]);
 
-    android::Replayer r(filename, pauseBeginning, numThreads);
-    auto status = r.replay();
+    status_t status = NO_ERROR;
+    do {
+        android::Replayer r(filename, pauseBeginning, numThreads, wait, stopHere);
+        status = r.replay();
+    } while(loop);
 
     if (status == NO_ERROR) {
         std::cout << "Successfully finished replaying trace" << std::endl;
diff --git a/cmds/surfacereplayer/replayer/README.md b/cmds/surfacereplayer/replayer/README.md
new file mode 100644
index 0000000..893f0dc
--- /dev/null
+++ b/cmds/surfacereplayer/replayer/README.md
@@ -0,0 +1,262 @@
+SurfaceReplayer Documentation
+===================
+
+[go/SurfaceReplayer](go/SurfaceReplayer)
+
+SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by
+[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays
+
+* Creation and deletion of surfaces/displays
+* Alterations to the surfaces/displays called Transactions
+* Buffer Updates to surfaces
+* VSync events
+
+At their specified times to be as close to the original trace.
+
+Usage
+--------
+
+###Creating a trace
+
+SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to
+utilize it. To allow it to write to the device, run
+
+`setenforce 0`
+
+To start recording a trace, run
+
+`service call SurfaceFlinger 1020 i32 1`
+
+To stop recording, run
+
+`service call SurfaceFlinger 1020 i32 0`
+
+The default location for the trace is `/data/SurfaceTrace.dat`
+
+###Executable
+
+To replay a specific trace, execute
+
+`/data/local/tmp/surfacereplayer /absolute/path/to/trace`
+
+inside the android shell. This will replay the full trace and then exit. Running this command
+outside of the shell by prepending `adb shell` will not allow for manual control and will not turn
+off VSync injections if it interrupted in any way other than fully replaying the trace
+
+The replay will not fill surfaces with their contents during the capture. Rather they are given a
+random color which will be the same every time the trace is replayed. Surfaces modulate their color
+at buffer updates.
+
+**Options:**
+
+- -m    pause the replayer at the start of the trace for manual replay
+- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3)
+- -s [Timestamp] switches to manual replay at specified timestamp
+- -n    Ignore timestamps and run through trace as fast as possible
+- -l    Indefinitely loop the replayer
+- -h    displays help menu
+
+**Manual Replay:**
+When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled
+by the user. Pressing CTRL-C again will exit the replayer.
+
+Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to
+input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter
+without inputting a command repeats the previous command.
+
+- n  - steps the replayer to the next VSync event
+- ni - steps the replayer to the next increment
+- c  - continues normal replaying
+- c [milliseconds] - continue until specified number of milliseconds have passed
+- s [timestamp]    - continue and stop at specified timestamp
+- l  - list out timestamp of current increment
+- h  - displays help menu
+
+###Shared Library
+
+To use the shared library include these shared libraries
+
+`libsurfacereplayer`
+`libprotobuf-cpp-full`
+`libutils`
+
+And the static library
+
+`libtrace_proto`
+
+Include the replayer header at the top of your file
+
+`#include <replayer/Replayer.h>`
+
+There are two constructors for the replayer
+
+`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)`
+`Replayer(Trace& trace, ... ditto ...)`
+
+The first constructor takes in the filepath where the trace is located and loads in the trace
+object internally.
+- replayManually - **True**: if the replayer will immediately switch to manual replay at the start
+- numThreads - Number of worker threads the replayer will use.
+- wait - **False**: Replayer ignores waits in between increments
+- stopHere - Time stamp of where the replayer should run to then switch to manual replay
+
+The second constructor includes all of the same parameters but takes in a preloaded trace object.
+To use add
+
+`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>`
+
+To your file
+
+After initializing the Replayer call
+
+    replayer.replay();
+
+And the trace will start replaying. Once the trace is finished replaying, the function will return.
+The layers that are visible at the end of the trace will remain on screen until the program
+terminates.
+
+
+**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was
+never executed. This can be fixed by executing
+
+`service call SurfaceFlinger 23 i32 0`
+
+in the android shell
+
+Code Breakdown
+-------------
+
+The Replayer is composed of 5 components.
+
+- The data format of the trace (Trace.proto)
+- The Replayer object (Replayer.cpp)
+- The synchronization mechanism to signal threads within the Replayer (Event.cpp)
+- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp)
+- The Main executable (Main.cpp)
+
+### Traces
+
+Traces are represented as a protobuf message located in surfacereplayer/proto/src.
+
+**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger).
+**Increments** contain the time stamp of when it occurred and a *oneof* which can be a
+
+ - Transaction
+ - SurfaceCreation
+ - SurfaceDeletion
+ - DisplayCreation
+ - DisplayDeleteion
+ - BufferUpdate
+ - VSyncEvent
+ - PowerModeUpdate
+
+**Transactions** contain whether the transaction was synchronous or animated and *repeated*
+**SurfaceChanges** and **DisplayChanges**
+
+- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as
+position, alpha, hidden, size, etc.
+- **DisplayChanges** contain the id of the display being manipulated and can be changes such as
+size, layer stack, projection, etc.
+
+**Surface/Display Creation** contain the id of the surface/display and the name of the
+surface/display
+
+**Surface/Display Deletion** contain the id of the surface/display to be deleted
+
+**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the
+buffer, and the frame number.
+
+**VSyncEvents** contain when the VSync event has occurred.
+
+**PowerModeUpdates** contain the id of the display being updated and what mode it is being
+changed to.
+
+To output the contents of a trace in a readable format, execute
+
+`**aprotoc** --decode=Trace \
+-I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \
+$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \
+ < **YourTraceFile.dat** > **YourOutputName.txt**`
+
+
+###Replayer
+
+Fundamentally the replayer loads a trace and iterates through each increment, waiting the required
+amount of time until the increment should be executed, then executing the increment. The first
+increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and
+goes from there.
+
+Increments from the trace are played asynchronously rather than one by one, being dispatched by
+the main thread, queued up in a thread pool and completed when the main thread deems they are
+ready to finish execution.
+
+When an increment is dispatched, it completes as much work as it can before it has to be
+synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action
+(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event
+object. The main thread holds a queue of these Event objects and completes the
+corresponding Event base on its time stamp. After completing an increment, the main thread will
+dispatch another increment and continue.
+
+The main thread's execution flow is outlined below
+
+    initReplay() //queue up the initial increments
+    while(!pendingIncrements.empty()) { //while increments remaining
+        event = pendingIncrement.pop();
+        wait(event.time_stamp(); //waitUntil it is time to complete this increment
+
+        event.complete() //signal to let event finish
+        if(increments remaing()) {
+            dispatchEvent() //queue up another increment
+        }
+    }
+
+A worker thread's flow looks like so
+
+    //dispatched!
+    Execute non-time sensitive work here
+    ...
+    event.readyToExecute() //time sensitive point...waiting for Main Thread
+    ...
+    Finish execution
+
+
+### Event
+
+An Event is a simple synchronization mechanism used to facilitate communication between the main
+and worker threads. Every time an increment is dispatched, an Event object is also created.
+
+An Event can be in 4 different states:
+
+- **SettingUp** - The worker is in the process of completing all non-time sensitive work
+- **Waiting** - The worker is waiting on the main thread to signal it.
+- **Signaled** - The worker has just been signaled by the main thread
+- **Running** - The worker is running again and finishing the rest of its work.
+
+When the main thread wants to finish the execution of a worker, the worker can either still be
+**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the
+main thread will **Signal** it to complete. The worker thread changes itself to the **Running**
+state once **Signaled**. This last step exists in order to communicate back to the main thread that
+the worker thread has actually started completing its execution, rather than being preempted right
+after signalling. Once this happens, the main thread schedules the next worker. This makes sure
+there is a constant amount of workers running at one time.
+
+This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the
+worker and main thread respectively.
+
+### BufferQueueScheduler
+
+During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a
+buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential
+**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it,
+which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by
+handling when **BufferUpdates** should be scheduled, making sure that they don't overlap.
+
+When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a
+**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one
+every time an Event is completed.
+
+### Main
+
+The main exectuable reads in the command line arguments. Creates the Replayer using those
+arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit
+gracefully, if there are then it will report the error and then exit.
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 950ebbb..ace10d1 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -22,6 +22,7 @@
 
 #include <binder/IMemory.h>
 
+#include <gui/BufferQueue.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <private/gui/ComposerService.h>
@@ -40,15 +41,24 @@
 #include <functional>
 #include <iostream>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <thread>
+#include <vector>
 
 using namespace android;
 
 std::atomic_bool Replayer::sReplayingManually(false);
 
-Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads)
-      : mTrace(), mLoaded(false), mIncrementIndex(0), mCurrentTime(0), mNumThreads(numThreads) {
+Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait,
+        nsecs_t stopHere)
+      : mTrace(),
+        mLoaded(false),
+        mIncrementIndex(0),
+        mCurrentTime(0),
+        mNumThreads(numThreads),
+        mWaitForTimeStamps(wait),
+        mStopTimeStamp(stopHere) {
     srand(RAND_COLOR_SEED);
 
     std::fstream input(filename, std::ios::in | std::ios::binary);
@@ -62,19 +72,32 @@
     mCurrentTime = mTrace.increment(0).time_stamp();
 
     sReplayingManually.store(replayManually);
+
+    if (stopHere < 0) {
+        mHasStopped = true;
+    }
 }
 
-Replayer::Replayer(const Trace& t, bool replayManually, int numThreads)
-      : mTrace(t), mLoaded(true), mIncrementIndex(0), mCurrentTime(0), mNumThreads(numThreads) {
+Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
+      : mTrace(t),
+        mLoaded(true),
+        mIncrementIndex(0),
+        mCurrentTime(0),
+        mNumThreads(numThreads),
+        mWaitForTimeStamps(wait),
+        mStopTimeStamp(stopHere) {
     srand(RAND_COLOR_SEED);
     mCurrentTime = mTrace.increment(0).time_stamp();
 
     sReplayingManually.store(replayManually);
+
+    if (stopHere < 0) {
+        mHasStopped = true;
+    }
 }
 
 status_t Replayer::replay() {
-    // for manual control
-    signal(SIGINT, Replayer::stopAutoReplayHandler);
+    signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
 
     ALOGV("There are %d increments.", mTrace.increment_size());
 
@@ -91,11 +114,18 @@
 
     ALOGV("Starting actual Replay!");
     while (!mPendingIncrements.empty()) {
+        mCurrentIncrement = mTrace.increment(mIncrementIndex);
+
+        if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
+            mHasStopped = true;
+            sReplayingManually.store(true);
+        }
+
         waitForConsoleCommmand();
 
-        auto pastIncrement = mTrace.increment(mIncrementIndex);
-
-        waitUntilTimestamp(pastIncrement.time_stamp());
+        if (mWaitForTimeStamps) {
+            waitUntilTimestamp(mCurrentIncrement.time_stamp());
+        }
 
         auto event = mPendingIncrements.front();
         mPendingIncrements.pop();
@@ -116,7 +146,7 @@
         }
 
         mIncrementIndex++;
-        mCurrentTime = pastIncrement.time_stamp();
+        mCurrentTime = mCurrentIncrement.time_stamp();
     }
 
     SurfaceComposerClient::enableVSyncInjections(false);
@@ -146,6 +176,21 @@
     sReplayingManually.store(true);
 }
 
+std::vector<std::string> split(const std::string& s, const char delim) {
+    std::vector<std::string> elems;
+    std::stringstream ss(s);
+    std::string item;
+    while (getline(ss, item, delim)) {
+        elems.push_back(item);
+    }
+    return elems;
+}
+
+bool isNumber(const std::string& s) {
+    return !s.empty() &&
+           std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
+}
+
 void Replayer::waitForConsoleCommmand() {
     if (!sReplayingManually || mWaitingForNextVSync) {
         return;
@@ -158,18 +203,65 @@
 
         if (input.empty()) {
             input = mLastInput;
+        } else {
+            mLastInput = input;
         }
 
-        input = mLastInput;
-        if (input == "n") {  // next vsync
+        if (mLastInput.empty()) {
+            continue;
+        }
+
+        std::vector<std::string> inputs = split(input, ' ');
+
+        if (inputs[0] == "n") {  // next vsync
             mWaitingForNextVSync = true;
             break;
-        } else if (input == "c") {  // continue
+
+        } else if (inputs[0] == "ni") {  // next increment
+            break;
+
+        } else if (inputs[0] == "c") {  // continue
+            if (inputs.size() > 1 && isNumber(inputs[1])) {
+                long milliseconds = stoi(inputs[1]);
+                std::thread([&] {
+                    std::cout << "Started!" << std::endl;
+                    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+                    sReplayingManually.store(true);
+                    std::cout << "Should have stopped!" << std::endl;
+                }).detach();
+            }
             sReplayingManually.store(false);
             mWaitingForNextVSync = false;
             break;
-        } else if (input == "h") {  // help
-                                    // add help menu
+
+        } else if (inputs[0] == "s") {  // stop at this timestamp
+            if (inputs.size() < 1) {
+                std::cout << "No time stamp given" << std::endl;
+                continue;
+            }
+            sReplayingManually.store(false);
+            mStopTimeStamp = stol(inputs[1]);
+            mHasStopped = false;
+            break;
+        } else if (inputs[0] == "l") {  // list
+            std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
+            continue;
+        } else if (inputs[0] == "q") {  // quit
+            SurfaceComposerClient::enableVSyncInjections(false);
+            exit(0);
+
+        } else if (inputs[0] == "h") {  // help
+                                        // add help menu
+            std::cout << "Manual Replay options:\n";
+            std::cout << " n  - Go to next VSync\n";
+            std::cout << " ni - Go to next increment\n";
+            std::cout << " c  - Continue\n";
+            std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
+            std::cout << " s [timestamp]    - Continue and stop at specified timestamp\n";
+            std::cout << " l  - List out timestamp of current increment\n";
+            std::cout << " h  - Display help menu\n";
+            std::cout << std::endl;
+            continue;
         }
 
         std::cout << "Invalid Command" << std::endl;
@@ -186,11 +278,13 @@
         case increment.kTransaction: {
             std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
         } break;
-        case increment.kCreate: {
-            std::thread(&Replayer::createSurfaceControl, this, increment.create(), event).detach();
+        case increment.kSurfaceCreation: {
+            std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
+                    .detach();
         } break;
-        case increment.kDelete: {
-            std::thread(&Replayer::deleteSurfaceControl, this, increment.delete_(), event).detach();
+        case increment.kSurfaceDeletion: {
+            std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event)
+                    .detach();
         } break;
         case increment.kBufferUpdate: {
             std::lock_guard<std::mutex> lock1(mLayerLock);
@@ -216,6 +310,18 @@
         case increment.kVsyncEvent: {
             std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
         } break;
+        case increment.kDisplayCreation: {
+            std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
+                    .detach();
+        } break;
+        case increment.kDisplayDeletion: {
+            std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
+                    .detach();
+        } break;
+        case increment.kPowerModeUpdate: {
+            std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
+                    .detach();
+        } break;
         default:
             ALOGE("Unknown Increment Type: %d", increment.increment_case());
             status = BAD_VALUE;
@@ -228,83 +334,12 @@
 status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
     ALOGV("Started Transaction");
 
-    if (t.change_size() == 0) {
-        event->readyToExecute();
-        return NO_ERROR;
-    }
-
-    Change change = t.change(0);
-
-    std::unique_lock<std::mutex> lock(mLayerLock);
-    if (mLayers[change.id()] == nullptr) {
-        mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
-    }
-    lock.unlock();
-
     SurfaceComposerClient::openGlobalTransaction();
 
     status_t status = NO_ERROR;
 
-    for (const Change& change : t.change()) {
-        std::unique_lock<std::mutex> lock(mLayerLock);
-        if (mLayers[change.id()] == nullptr) {
-            mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
-        }
-
-        switch (change.Change_case()) {
-            case Change::ChangeCase::kPosition:
-                status = setPosition(change.id(), change.position());
-                break;
-            case Change::ChangeCase::kSize:
-                status = setSize(change.id(), change.size());
-                break;
-            case Change::ChangeCase::kAlpha:
-                status = setAlpha(change.id(), change.alpha());
-                break;
-            case Change::ChangeCase::kLayer:
-                status = setLayer(change.id(), change.layer());
-                break;
-            case Change::ChangeCase::kCrop:
-                status = setCrop(change.id(), change.crop());
-                break;
-            case Change::ChangeCase::kMatrix:
-                status = setMatrix(change.id(), change.matrix());
-                break;
-            case Change::ChangeCase::kFinalCrop:
-                status = setFinalCrop(change.id(), change.final_crop());
-                break;
-            case Change::ChangeCase::kOverrideScalingMode:
-                status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
-                break;
-            case Change::ChangeCase::kTransparentRegionHint:
-                status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
-                break;
-            case Change::ChangeCase::kLayerStack:
-                status = setLayerStack(change.id(), change.layer_stack());
-                break;
-            case Change::ChangeCase::kHiddenFlag:
-                status = setHiddenFlag(change.id(), change.hidden_flag());
-                break;
-            case Change::ChangeCase::kOpaqueFlag:
-                status = setOpaqueFlag(change.id(), change.opaque_flag());
-                break;
-            case Change::ChangeCase::kSecureFlag:
-                status = setSecureFlag(change.id(), change.secure_flag());
-                break;
-            case Change::ChangeCase::kDeferredTransaction:
-                waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
-                status = setDeferredTransaction(change.id(), change.deferred_transaction());
-                break;
-            default:
-                status = NO_ERROR;
-                break;
-        }
-
-        if (status != NO_ERROR) {
-            ALOGE("SET TRANSACTION FAILED");
-            return status;
-        }
-    }
+    status = doSurfaceTransaction(t.surface_change());
+    doDisplayTransaction(t.display_change());
 
     if (t.animation()) {
         SurfaceComposerClient::setAnimationTransaction();
@@ -319,27 +354,120 @@
     return status;
 }
 
-status_t Replayer::setPosition(uint32_t id, const PositionChange& pc) {
+status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) {
+    status_t status = NO_ERROR;
+
+    for (const SurfaceChange& change : surfaceChanges) {
+        std::unique_lock<std::mutex> lock(mLayerLock);
+        if (mLayers[change.id()] == nullptr) {
+            mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
+        }
+
+        switch (change.SurfaceChange_case()) {
+            case SurfaceChange::SurfaceChangeCase::kPosition:
+                status = setPosition(change.id(), change.position());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kSize:
+                status = setSize(change.id(), change.size());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kAlpha:
+                status = setAlpha(change.id(), change.alpha());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kLayer:
+                status = setLayer(change.id(), change.layer());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kCrop:
+                status = setCrop(change.id(), change.crop());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kMatrix:
+                status = setMatrix(change.id(), change.matrix());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kFinalCrop:
+                status = setFinalCrop(change.id(), change.final_crop());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
+                status = setOverrideScalingMode(change.id(), change.override_scaling_mode());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
+                status = setTransparentRegionHint(change.id(), change.transparent_region_hint());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kLayerStack:
+                status = setLayerStack(change.id(), change.layer_stack());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
+                status = setHiddenFlag(change.id(), change.hidden_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
+                status = setOpaqueFlag(change.id(), change.opaque_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kSecureFlag:
+                status = setSecureFlag(change.id(), change.secure_flag());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
+                waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock);
+                status = setDeferredTransaction(change.id(), change.deferred_transaction());
+                break;
+            default:
+                status = NO_ERROR;
+                break;
+        }
+
+        if (status != NO_ERROR) {
+            ALOGE("SET TRANSACTION FAILED");
+            return status;
+        }
+    }
+    return status;
+}
+
+void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) {
+    for (const DisplayChange& change : displayChanges) {
+        ALOGV("Doing display transaction");
+        std::unique_lock<std::mutex> lock(mDisplayLock);
+        if (mDisplays[change.id()] == nullptr) {
+            mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
+        }
+
+        switch (change.DisplayChange_case()) {
+            case DisplayChange::DisplayChangeCase::kSurface:
+                setDisplaySurface(change.id(), change.surface());
+                break;
+            case DisplayChange::DisplayChangeCase::kLayerStack:
+                setDisplayLayerStack(change.id(), change.layer_stack());
+                break;
+            case DisplayChange::DisplayChangeCase::kSize:
+                setDisplaySize(change.id(), change.size());
+                break;
+            case DisplayChange::DisplayChangeCase::kProjection:
+                setDisplayProjection(change.id(), change.projection());
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+status_t Replayer::setPosition(layer_id id, const PositionChange& pc) {
     ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
     return mLayers[id]->setPosition(pc.x(), pc.y());
 }
 
-status_t Replayer::setSize(uint32_t id, const SizeChange& sc) {
+status_t Replayer::setSize(layer_id id, const SizeChange& sc) {
     ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
     return mLayers[id]->setSize(sc.w(), sc.h());
 }
 
-status_t Replayer::setLayer(uint32_t id, const LayerChange& lc) {
+status_t Replayer::setLayer(layer_id id, const LayerChange& lc) {
     ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
     return mLayers[id]->setLayer(lc.layer());
 }
 
-status_t Replayer::setAlpha(uint32_t id, const AlphaChange& ac) {
+status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) {
     ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
     return mLayers[id]->setAlpha(ac.alpha());
 }
 
-status_t Replayer::setCrop(uint32_t id, const CropChange& cc) {
+status_t Replayer::setCrop(layer_id id, const CropChange& cc) {
     ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
             cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
             cc.rectangle().bottom());
@@ -349,7 +477,7 @@
     return mLayers[id]->setCrop(r);
 }
 
-status_t Replayer::setFinalCrop(uint32_t id, const FinalCropChange& fcc) {
+status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) {
     ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
             fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(),
             fcc.rectangle().bottom());
@@ -358,18 +486,18 @@
     return mLayers[id]->setFinalCrop(r);
 }
 
-status_t Replayer::setMatrix(uint32_t id, const MatrixChange& mc) {
+status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) {
     ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
             mc.dtdx(), mc.dsdy(), mc.dtdy());
     return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
 }
 
-status_t Replayer::setOverrideScalingMode(uint32_t id, const OverrideScalingModeChange& osmc) {
+status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) {
     ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
     return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode());
 }
 
-status_t Replayer::setTransparentRegionHint(uint32_t id, const TransparentRegionHintChange& trhc) {
+status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) {
     ALOGV("Setting Transparent Region Hint");
     Region re = Region();
 
@@ -381,33 +509,33 @@
     return mLayers[id]->setTransparentRegionHint(re);
 }
 
-status_t Replayer::setLayerStack(uint32_t id, const LayerStackChange& lsc) {
+status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) {
     ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
     return mLayers[id]->setLayerStack(lsc.layer_stack());
 }
 
-status_t Replayer::setHiddenFlag(uint32_t id, const HiddenFlagChange& hfc) {
+status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) {
     ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
-    uint32_t flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
+    layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
 
     return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden);
 }
 
-status_t Replayer::setOpaqueFlag(uint32_t id, const OpaqueFlagChange& ofc) {
+status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) {
     ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
-    uint32_t flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
+    layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
 
     return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque);
 }
 
-status_t Replayer::setSecureFlag(uint32_t id, const SecureFlagChange& sfc) {
+status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) {
     ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
-    uint32_t flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
+    layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
 
     return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure);
 }
 
-status_t Replayer::setDeferredTransaction(uint32_t id, const DeferredTransactionChange& dtc) {
+status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) {
     ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, "
           "frame_number=%llu",
             id, dtc.layer_id(), dtc.frame_number());
@@ -421,7 +549,32 @@
     return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number());
 }
 
-status_t Replayer::createSurfaceControl(const Create& create, const std::shared_ptr<Event>& event) {
+void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) {
+    sp<IGraphicBufferProducer> outProducer;
+    sp<IGraphicBufferConsumer> outConsumer;
+    BufferQueue::createBufferQueue(&outProducer, &outConsumer);
+
+    SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer);
+}
+
+void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) {
+    SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+}
+
+void Replayer::setDisplaySize(display_id id, const SizeChange& sc) {
+    SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h());
+}
+
+void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) {
+    Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
+            pc.viewport().bottom());
+    Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
+
+    SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+}
+
+status_t Replayer::createSurfaceControl(
+        const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
     event->readyToExecute();
 
     ALOGV("Creating Surface Control: ID: %d", create.id());
@@ -437,7 +590,7 @@
     auto& layer = mLayers[create.id()];
     layer = surfaceControl;
 
-    mColors[create.id()] = HSVToRGB(HSV(rand() % 360, 1, 1));
+    mColors[create.id()] = HSV(rand() % 360, 1, 1);
 
     mLayerCond.notify_all();
 
@@ -451,7 +604,7 @@
 }
 
 status_t Replayer::deleteSurfaceControl(
-        const Delete& delete_, const std::shared_ptr<Event>& event) {
+        const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) {
     ALOGV("Deleting %d Surface Control", delete_.id());
     event->readyToExecute();
 
@@ -459,13 +612,15 @@
 
     mLayersPendingRemoval.push_back(delete_.id());
 
-    auto iterator = mBufferQueueSchedulers.find(delete_.id());
+    const auto& iterator = mBufferQueueSchedulers.find(delete_.id());
     if (iterator != mBufferQueueSchedulers.end()) {
         (*iterator).second->stopScheduling();
     }
 
     std::lock_guard<std::mutex> lock2(mLayerLock);
-    mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+    if (mLayers[delete_.id()] != nullptr) {
+        mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle());
+    }
 
     return NO_ERROR;
 }
@@ -496,6 +651,35 @@
     return NO_ERROR;
 }
 
+void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
+    ALOGV("Creating display");
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock(mDisplayLock);
+    sp<IBinder> display = SurfaceComposerClient::createDisplay(
+            String8(create.name().c_str()), create.is_secure());
+    mDisplays[create.id()] = display;
+
+    mDisplayCond.notify_all();
+
+    ALOGV("Done creating display");
+}
+
+void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
+    ALOGV("Delete display");
+    event->readyToExecute();
+
+    std::lock_guard<std::mutex> lock(mDisplayLock);
+    SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
+    mDisplays.erase(delete_.id());
+}
+
+void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
+    ALOGV("Updating power mode");
+    event->readyToExecute();
+    SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
+}
+
 void Replayer::waitUntilTimestamp(int64_t timestamp) {
     ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
     std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index ff951d1..f757fc3 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -40,16 +40,21 @@
 namespace android {
 
 const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
-const auto RAND_COLOR_SEED = 1000;
+const auto RAND_COLOR_SEED = 700;
 const auto DEFAULT_THREADS = 3;
 
-typedef uint32_t layer_id;
+typedef int32_t layer_id;
+typedef int32_t display_id;
+
+typedef google::protobuf::RepeatedPtrField<SurfaceChange> SurfaceChanges;
+typedef google::protobuf::RepeatedPtrField<DisplayChange> DisplayChanges;
 
 class Replayer {
   public:
     Replayer(const std::string& filename, bool replayManually = false,
-            int numThreads = DEFAULT_THREADS);
-    Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS);
+            int numThreads = DEFAULT_THREADS, bool wait = true, nsecs_t stopHere = -1);
+    Replayer(const Trace& trace, bool replayManually = false, int numThreads = DEFAULT_THREADS,
+            bool wait = true, nsecs_t stopHere = -1);
 
     status_t replay();
 
@@ -62,24 +67,37 @@
     status_t dispatchEvent(int index);
 
     status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event);
-    status_t createSurfaceControl(const Create& create, const std::shared_ptr<Event>& event);
-    status_t deleteSurfaceControl(const Delete& delete_, const std::shared_ptr<Event>& event);
+    status_t createSurfaceControl(const SurfaceCreation& create,
+            const std::shared_ptr<Event>& event);
+    status_t deleteSurfaceControl(const SurfaceDeletion& delete_,
+            const std::shared_ptr<Event>& event);
     status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event);
+    void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event);
+    void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event);
+    void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event);
 
-    status_t setPosition(uint32_t id, const PositionChange& pc);
-    status_t setSize(uint32_t id, const SizeChange& sc);
-    status_t setAlpha(uint32_t id, const AlphaChange& ac);
-    status_t setLayer(uint32_t id, const LayerChange& lc);
-    status_t setCrop(uint32_t id, const CropChange& cc);
-    status_t setFinalCrop(uint32_t id, const FinalCropChange& fcc);
-    status_t setMatrix(uint32_t id, const MatrixChange& mc);
-    status_t setOverrideScalingMode(uint32_t id, const OverrideScalingModeChange& osmc);
-    status_t setTransparentRegionHint(uint32_t id, const TransparentRegionHintChange& trgc);
-    status_t setLayerStack(uint32_t id, const LayerStackChange& lsc);
-    status_t setHiddenFlag(uint32_t id, const HiddenFlagChange& hfc);
-    status_t setOpaqueFlag(uint32_t id, const OpaqueFlagChange& ofc);
-    status_t setSecureFlag(uint32_t id, const SecureFlagChange& sfc);
-    status_t setDeferredTransaction(uint32_t id, const DeferredTransactionChange& dtc);
+    status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange);
+    void doDisplayTransaction(const DisplayChanges& displayChange);
+
+    status_t setPosition(layer_id id, const PositionChange& pc);
+    status_t setSize(layer_id id, const SizeChange& sc);
+    status_t setAlpha(layer_id id, const AlphaChange& ac);
+    status_t setLayer(layer_id id, const LayerChange& lc);
+    status_t setCrop(layer_id id, const CropChange& cc);
+    status_t setFinalCrop(layer_id id, const FinalCropChange& fcc);
+    status_t setMatrix(layer_id id, const MatrixChange& mc);
+    status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc);
+    status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc);
+    status_t setLayerStack(layer_id id, const LayerStackChange& lsc);
+    status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc);
+    status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc);
+    status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc);
+    status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc);
+
+    void setDisplaySurface(display_id id, const DispSurfaceChange& dsc);
+    void setDisplayLayerStack(display_id id, const LayerStackChange& lsc);
+    void setDisplaySize(display_id id, const SizeChange& sc);
+    void setDisplayProjection(display_id id, const ProjectionChange& pc);
 
     void doDeleteSurfaceControls();
     void waitUntilTimestamp(int64_t timestamp);
@@ -93,15 +111,20 @@
     int64_t mCurrentTime = 0;
     int32_t mNumThreads = DEFAULT_THREADS;
 
+    Increment mCurrentIncrement;
+
     std::string mLastInput;
 
     static atomic_bool sReplayingManually;
     bool mWaitingForNextVSync;
+    bool mWaitForTimeStamps;
+    nsecs_t mStopTimeStamp;
+    bool mHasStopped;
 
     std::mutex mLayerLock;
     std::condition_variable mLayerCond;
     std::unordered_map<layer_id, sp<SurfaceControl>> mLayers;
-    std::unordered_map<layer_id, RGB> mColors;
+    std::unordered_map<layer_id, HSV> mColors;
 
     std::mutex mPendingLayersLock;
     std::vector<layer_id> mLayersPendingRemoval;
@@ -109,6 +132,10 @@
     std::mutex mBufferQueueSchedulerLock;
     std::unordered_map<layer_id, std::shared_ptr<BufferQueueScheduler>> mBufferQueueSchedulers;
 
+    std::mutex mDisplayLock;
+    std::condition_variable mDisplayCond;
+    std::unordered_map<display_id, sp<IBinder>> mDisplays;
+
     sp<SurfaceComposerClient> mComposerClient;
     std::queue<std::shared_ptr<Event>> mPendingIncrements;
 };