Merge "SF: Track starting layer state with transaction tracing"
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a648797..21f3872 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4407,6 +4407,16 @@
         return result;
     }
 
+    int parentId = -1;
+    // We can safely promote the layer in binder thread because we have a strong reference
+    // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
+    sp<Layer> parentSp = parent.promote();
+    if (parentSp != nullptr) {
+        parentId = parentSp->getSequence();
+    }
+    mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+                                     args.flags, parentId);
+
     setTransactionFlags(eTransactionNeeded);
     *outLayerId = layer->sequence;
     return result;
@@ -6642,6 +6652,7 @@
     if (!layer->isRemovedFromCurrentState()) {
         mScheduler->deregisterLayer(layer);
     }
+    mTransactionTracing.onLayerRemoved(layer->getSequence());
 }
 
 void SurfaceFlinger::onLayerUpdate() {
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 84890ee..d136e0b 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -53,6 +53,7 @@
     mEnabled = false;
     LayersTraceFileProto fileProto = createTraceFileProto();
     mBuffer->writeToFile(fileProto, FILE_NAME);
+    mBuffer->reset();
     return true;
 }
 
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
index 63a2786..281cd19 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -39,26 +39,28 @@
     void setSize(size_t newSize) { mSizeInBytes = newSize; }
     EntryProto& front() { return mStorage.front(); }
     const EntryProto& front() const { return mStorage.front(); }
+    const EntryProto& back() const { return mStorage.back(); }
 
-    void reset(size_t newSize) {
+    void reset() {
         // use the swap trick to make sure memory is released
-        std::queue<EntryProto>().swap(mStorage);
-        mSizeInBytes = newSize;
+        std::deque<EntryProto>().swap(mStorage);
         mUsedInBytes = 0U;
     }
-    void flush(FileProto& fileProto) {
-        fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
-        while (!mStorage.empty()) {
-            auto entry = fileProto.add_entry();
-            entry->Swap(&mStorage.front());
-            mStorage.pop();
+
+    void writeToProto(FileProto& fileProto) {
+        fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
+                                           fileProto.entry().size());
+        for (const EntryProto& entry : mStorage) {
+            EntryProto* entryProto = fileProto.add_entry();
+            *entryProto = entry;
         }
     }
 
     status_t writeToFile(FileProto& fileProto, std::string filename) {
         ATRACE_CALL();
+        writeToProto(fileProto);
         std::string output;
-        if (!writeToString(fileProto, &output)) {
+        if (!fileProto.SerializeToString(&output)) {
             ALOGE("Could not serialize proto.");
             return UNKNOWN_ERROR;
         }
@@ -72,13 +74,6 @@
         return NO_ERROR;
     }
 
-    bool writeToString(FileProto& fileProto, std::string* outString) {
-        ATRACE_CALL();
-        flush(fileProto);
-        reset(mSizeInBytes);
-        return fileProto.SerializeToString(outString);
-    }
-
     std::vector<EntryProto> emplace(EntryProto&& proto) {
         std::vector<EntryProto> replacedEntries;
         size_t protoSize = static_cast<size_t>(proto.ByteSize());
@@ -88,10 +83,10 @@
             }
             mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
             replacedEntries.emplace_back(mStorage.front());
-            mStorage.pop();
+            mStorage.pop_front();
         }
         mUsedInBytes += protoSize;
-        mStorage.emplace();
+        mStorage.emplace_back();
         mStorage.back().Swap(&proto);
         return replacedEntries;
     }
@@ -112,7 +107,7 @@
 private:
     size_t mUsedInBytes = 0U;
     size_t mSizeInBytes = 0U;
-    std::queue<EntryProto> mStorage;
+    std::deque<EntryProto> mStorage;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index d1dc076..7e12313 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -43,7 +43,7 @@
 }
 
 proto::TransactionState TransactionProtoParser::toProto(
-        std::vector<std::pair<int32_t /* layerId */, TracingLayerState>> states) {
+        const std::unordered_map<int32_t /* layerId */, TracingLayerState> states) {
     proto::TransactionState proto;
     for (auto& [layerId, state] : states) {
         proto::LayerState layerProto = toProto(state, nullptr);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index e8a139f..619ee05 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -51,7 +51,7 @@
     static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
                                            DisplayHandleToIdFn getDisplayIdFn);
     static proto::TransactionState toProto(
-            std::vector<std::pair<int32_t /* layerId */, TracingLayerState>>);
+            const std::unordered_map<int32_t /* layerId */, TracingLayerState>);
 
     static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
 
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 758bd31..cf488c2 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -41,6 +41,7 @@
         return false;
     }
     mBuffer->setSize(mBufferSizeInBytes);
+    mStartingTimestamp = systemTime();
     mEnabled = true;
     {
         std::scoped_lock lock(mMainThreadLock);
@@ -68,9 +69,11 @@
     }
     mEnabled = false;
 
-    proto::TransactionTraceFile fileProto = createTraceFileProto();
-    mBuffer->writeToFile(fileProto, FILE_NAME);
+    writeToFileLocked();
+    mBuffer->reset();
     mQueuedTransactions.clear();
+    mStartingStates.clear();
+    mLayerHandles.clear();
     return true;
 }
 
@@ -84,7 +87,12 @@
     if (!mEnabled) {
         return STATUS_OK;
     }
+    return writeToFileLocked();
+}
+
+status_t TransactionTracing::writeToFileLocked() {
     proto::TransactionTraceFile fileProto = createTraceFileProto();
+    addStartingStateToProtoLocked(fileProto);
     return mBuffer->writeToFile(fileProto, FILE_NAME);
 }
 
@@ -105,8 +113,10 @@
     std::scoped_lock lock(mTraceLock);
     base::StringAppendF(&result, "Transaction tracing state: %s\n",
                         mEnabled ? "enabled" : "disabled");
-    base::StringAppendF(&result, "  queued transactions: %d\n",
-                        static_cast<uint32_t>(mQueuedTransactions.size()));
+    base::StringAppendF(&result,
+                        "  queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
+                        mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
+                        mStartingStates.size());
     mBuffer->dump(result);
 }
 
@@ -117,7 +127,10 @@
         return;
     }
     mQueuedTransactions[transaction.id] =
-            TransactionProtoParser::toProto(transaction, nullptr, nullptr);
+            TransactionProtoParser::toProto(transaction,
+                                            std::bind(&TransactionTracing::getLayerIdLocked, this,
+                                                      std::placeholders::_1),
+                                            nullptr);
 }
 
 void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
@@ -130,25 +143,14 @@
         committedTransactions.transactionIds.emplace_back(transaction.id);
     }
 
-    // Try to acquire the lock from main thread, but don't block if we cannot acquire the lock. Add
-    // it to pending transactions that we can collect later.
-    if (mMainThreadLock.try_lock()) {
-        // We got the lock! Collect any pending transactions and continue.
-        mCommittedTransactions.insert(mCommittedTransactions.end(),
-                                      std::make_move_iterator(mPendingTransactions.begin()),
-                                      std::make_move_iterator(mPendingTransactions.end()));
-        mPendingTransactions.clear();
-        mCommittedTransactions.emplace_back(committedTransactions);
-        mTransactionsAvailableCv.notify_one();
-        mMainThreadLock.unlock();
-    } else {
-        mPendingTransactions.emplace_back(committedTransactions);
-    }
+    mPendingTransactions.emplace_back(committedTransactions);
+    tryPushToTracingThread();
 }
 
 void TransactionTracing::loop() {
     while (true) {
         std::vector<CommittedTransactions> committedTransactions;
+        std::vector<int32_t> removedLayers;
         {
             std::unique_lock<std::mutex> lock(mMainThreadLock);
             base::ScopedLockAssertion assumeLocked(mMainThreadLock);
@@ -157,19 +159,22 @@
             });
             if (mDone) {
                 mCommittedTransactions.clear();
+                mRemovedLayers.clear();
                 break;
             }
+
+            removedLayers = std::move(mRemovedLayers);
+            mRemovedLayers.clear();
             committedTransactions = std::move(mCommittedTransactions);
             mCommittedTransactions.clear();
         } // unlock mMainThreadLock
 
-        addEntry(committedTransactions);
-
-        mTransactionsAddedToBufferCv.notify_one();
+        addEntry(committedTransactions, removedLayers);
     }
 }
 
-void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions) {
+void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+                                  const std::vector<int32_t>& removedLayers) {
     ATRACE_CALL();
     std::scoped_lock lock(mTraceLock);
     std::vector<proto::TransactionTraceEntry> removedEntries;
@@ -177,6 +182,15 @@
         proto::TransactionTraceEntry entryProto;
         entryProto.set_elapsed_realtime_nanos(entry.timestamp);
         entryProto.set_vsync_id(entry.vsyncId);
+        entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
+        for (auto& newLayer : mCreatedLayers) {
+            entryProto.mutable_added_layers()->Add(std::move(newLayer));
+        }
+        entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
+        for (auto& removedLayer : removedLayers) {
+            entryProto.mutable_removed_layers()->Add(removedLayer);
+        }
+        mCreatedLayers.clear();
         entryProto.mutable_transactions()->Reserve(
                 static_cast<int32_t>(entry.transactionIds.size()));
         for (const uint64_t& id : entry.transactionIds) {
@@ -188,16 +202,128 @@
                 ALOGE("Could not find transaction id %" PRIu64, id);
             }
         }
-        mBuffer->emplace(std::move(entryProto));
+        std::vector<proto::TransactionTraceEntry> entries = mBuffer->emplace(std::move(entryProto));
+        removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
+                              std::make_move_iterator(entries.end()));
+    }
+
+    for (const proto::TransactionTraceEntry& removedEntry : removedEntries) {
+        updateStartingStateLocked(removedEntry);
+    }
+    mTransactionsAddedToBufferCv.notify_one();
+}
+
+void TransactionTracing::flush(int64_t vsyncId) {
+    while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+        tryPushToTracingThread();
+    }
+    std::unique_lock<std::mutex> lock(mTraceLock);
+    base::ScopedLockAssertion assumeLocked(mTraceLock);
+    mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
+        return mBuffer->used() > 0 && mBuffer->back().vsync_id() >= vsyncId;
+    });
+}
+
+void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+                                      uint32_t flags, int parentId) {
+    std::scoped_lock lock(mTraceLock);
+    TracingLayerCreationArgs args{layerId, name, flags, parentId};
+    mLayerHandles[layerHandle] = layerId;
+    mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+}
+
+void TransactionTracing::onLayerRemoved(int32_t layerId) {
+    mPendingRemovedLayers.emplace_back(layerId);
+    tryPushToTracingThread();
+}
+
+void TransactionTracing::tryPushToTracingThread() {
+    // Try to acquire the lock from main thread.
+    if (mMainThreadLock.try_lock()) {
+        // We got the lock! Collect any pending transactions and continue.
+        mCommittedTransactions.insert(mCommittedTransactions.end(),
+                                      std::make_move_iterator(mPendingTransactions.begin()),
+                                      std::make_move_iterator(mPendingTransactions.end()));
+        mPendingTransactions.clear();
+        mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
+                              mPendingRemovedLayers.end());
+        mPendingRemovedLayers.clear();
+        mTransactionsAvailableCv.notify_one();
+        mMainThreadLock.unlock();
+    } else {
+        ALOGV("Couldn't get lock");
     }
 }
 
-void TransactionTracing::flush() {
-    std::unique_lock<std::mutex> lock(mMainThreadLock);
-    base::ScopedLockAssertion assumeLocked(mMainThreadLock);
-    mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
-        return mCommittedTransactions.empty();
-    });
+int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
+    if (layerHandle == nullptr) {
+        return -1;
+    }
+    auto it = mLayerHandles.find(layerHandle->localBinder());
+    return it == mLayerHandles.end() ? -1 : it->second;
+}
+
+void TransactionTracing::updateStartingStateLocked(
+        const proto::TransactionTraceEntry& removedEntry) {
+    // Keep track of layer starting state so we can reconstruct the layer state as we purge
+    // transactions from the buffer.
+    for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
+        TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
+        startingState.layerId = addedLayer.layer_id();
+        startingState.name = addedLayer.name();
+        startingState.layerCreationFlags = addedLayer.flags();
+        startingState.parentId = addedLayer.parent_id();
+    }
+
+    // Merge layer states to starting transaction state.
+    for (const proto::TransactionState& transaction : removedEntry.transactions()) {
+        for (const proto::LayerState& layerState : transaction.layer_changes()) {
+            auto it = mStartingStates.find(layerState.layer_id());
+            if (it == mStartingStates.end()) {
+                ALOGE("Could not find layer id %d", layerState.layer_id());
+                continue;
+            }
+            TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+        }
+    }
+
+    // Clean up stale starting states since the layer has been removed and the buffer does not
+    // contain any references to the layer.
+    for (const int32_t removedLayerId : removedEntry.removed_layers()) {
+        auto it = std::find_if(mLayerHandles.begin(), mLayerHandles.end(),
+                               [removedLayerId](auto& layer) {
+                                   return layer.second == removedLayerId;
+                               });
+        if (it != mLayerHandles.end()) {
+            mLayerHandles.erase(it);
+        }
+        mStartingStates.erase(removedLayerId);
+    }
+}
+
+void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
+    proto::TransactionTraceEntry* entryProto = proto.add_entry();
+    entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
+    entryProto->set_vsync_id(0);
+    entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+    for (auto& [layerId, state] : mStartingStates) {
+        TracingLayerCreationArgs args{layerId, state.name, state.layerCreationFlags,
+                                      state.parentId};
+        entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(args));
+    }
+
+    proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
+    transactionProto.set_vsync_id(0);
+    transactionProto.set_post_time(mStartingTimestamp);
+    entryProto->mutable_transactions()->Add(std::move(transactionProto));
+}
+
+proto::TransactionTraceFile TransactionTracing::writeToProto() {
+    std::scoped_lock<std::mutex> lock(mTraceLock);
+    proto::TransactionTraceFile proto = createTraceFileProto();
+    addStartingStateToProtoLocked(proto);
+    mBuffer->writeToProto(proto);
+    return proto;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index d92ab01..0aa22ed 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -62,6 +62,9 @@
     void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
     status_t writeToFile();
     void setBufferSize(size_t bufferSizeInBytes);
+    void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
+                      int parentId);
+    void onLayerRemoved(int layerId);
     void dump(std::string&) const;
     static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
     static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
@@ -78,6 +81,12 @@
     size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
     std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
             GUARDED_BY(mTraceLock);
+    nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
+    std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
+    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+            GUARDED_BY(mTraceLock);
+    std::unordered_map<int32_t /* layerId */, TracingLayerState> mStartingStates
+            GUARDED_BY(mTraceLock);
 
     // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
     // otherwise will push data to temporary container.
@@ -93,15 +102,25 @@
     };
     std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
     std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
-    proto::TransactionTraceFile createTraceFileProto() const;
 
+    std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
+    std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+
+    proto::TransactionTraceFile createTraceFileProto() const;
     void loop();
-    void addEntry(const std::vector<CommittedTransactions>& committedTransactions)
-            EXCLUDES(mTraceLock);
+    void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+                  const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+    int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
+    void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
+    void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
+    void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
+    status_t writeToFileLocked() REQUIRES(mTraceLock);
 
     // TEST
-    // Wait until all the committed transactions are added to the buffer.
-    void flush() EXCLUDES(mMainThreadLock);
+    // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+    void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
+    // Return buffer contents as trace file proto
+    proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index edeacfa..10222cc 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -42,8 +42,10 @@
     int64 elapsed_realtime_nanos = 1;
     int64 vsync_id = 2;
     repeated TransactionState transactions = 3;
-    repeated LayerCreationArgs new_layers = 4;
-    repeated DisplayState new_displays = 5;
+    repeated LayerCreationArgs added_layers = 4;
+    repeated int32 removed_layers = 5;
+    repeated DisplayState added_displays = 6;
+    repeated int32 removed_displays = 7;
 }
 
 message LayerCreationArgs {
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 2afa68a..ffe5671 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -28,11 +28,14 @@
 
 class TransactionTracingTest : public testing::Test {
 protected:
+    static constexpr size_t SMALL_BUFFER_SIZE = 1024;
     std::unique_ptr<android::TransactionTracing> mTracing;
-
     void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
 
-    void TearDown() override { mTracing.reset(); }
+    void TearDown() override {
+        mTracing->disable();
+        mTracing.reset();
+    }
 
     auto getCommittedTransactions() {
         std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
@@ -49,7 +52,7 @@
         return mTracing->mBuffer->used();
     }
 
-    auto flush() { return mTracing->flush(); }
+    auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
 
     auto bufferFront() {
         std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
@@ -61,12 +64,28 @@
         return mTracing->mThread.joinable();
     }
 
-    std::string writeToString() {
+    proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
+
+    auto getCreatedLayers() {
         std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
-        std::string output;
-        proto::TransactionTraceFile fileProto = mTracing->createTraceFileProto();
-        mTracing->mBuffer->writeToString(fileProto, &output);
-        return output;
+        return mTracing->mCreatedLayers;
+    }
+
+    auto getStartingStates() {
+        std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+        return mTracing->mStartingStates;
+    }
+
+    void queueAndCommitTransaction(int64_t vsyncId) {
+        TransactionState transaction;
+        transaction.id = static_cast<uint64_t>(vsyncId * 3);
+        transaction.originUid = 1;
+        transaction.originPid = 2;
+        mTracing->addQueuedTransaction(transaction);
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back(transaction);
+        mTracing->addCommittedTransactions(transactions, vsyncId);
+        flush(vsyncId);
     }
 
     // Test that we clean up the tracing thread and free any memory allocated.
@@ -76,6 +95,7 @@
         EXPECT_EQ(getCommittedTransactions().size(), 0u);
         EXPECT_EQ(getQueuedTransactions().size(), 0u);
         EXPECT_EQ(getUsedBufferSize(), 0u);
+        EXPECT_EQ(getStartingStates().size(), 0u);
     }
 
     void verifyEntry(const proto::TransactionTraceEntry& actualProto,
@@ -122,17 +142,150 @@
     std::vector<TransactionState> secondTransactionSet =
             std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
     mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
-    flush();
+    flush(secondTransactionSetVsyncId);
 
-    std::string protoString = writeToString();
-    proto::TransactionTraceFile proto;
-    proto.ParseFromString(protoString);
-    EXPECT_EQ(proto.entry().size(), 2);
-    verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId);
-    verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId);
+    proto::TransactionTraceFile proto = writeToProto();
+    EXPECT_EQ(proto.entry().size(), 3);
+    // skip starting entry
+    verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
+    verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
 
     mTracing->disable();
     verifyDisabledTracingState();
 }
 
+class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
+protected:
+    void SetUp() override {
+        TransactionTracingTest::SetUp();
+        mTracing->enable();
+        // add layers
+        mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+        const sp<IBinder> fakeLayerHandle = new BBinder();
+        mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+                               123 /* flags */, -1 /* parentId */);
+        const sp<IBinder> fakeChildLayerHandle = new BBinder();
+        mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), 2 /* layerId */, "child",
+                               456 /* flags */, mParentLayerId);
+
+        // add some layer transaction
+        {
+            TransactionState transaction;
+            transaction.id = 50;
+            ComposerState layerState;
+            layerState.state.surface = fakeLayerHandle;
+            layerState.state.what = layer_state_t::eLayerChanged;
+            layerState.state.z = 42;
+            transaction.states.add(layerState);
+            ComposerState childState;
+            childState.state.surface = fakeChildLayerHandle;
+            layerState.state.z = 43;
+            transaction.states.add(childState);
+            mTracing->addQueuedTransaction(transaction);
+
+            std::vector<TransactionState> transactions;
+            transactions.emplace_back(transaction);
+            VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
+            mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+            flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+        }
+
+        // add transactions that modify the layer state further so we can test that layer state
+        // gets merged
+        {
+            TransactionState transaction;
+            transaction.id = 51;
+            ComposerState layerState;
+            layerState.state.surface = fakeLayerHandle;
+            layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
+            layerState.state.z = 41;
+            layerState.state.x = 22;
+            transaction.states.add(layerState);
+            mTracing->addQueuedTransaction(transaction);
+
+            std::vector<TransactionState> transactions;
+            transactions.emplace_back(transaction);
+            VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
+            mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+            flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+        }
+
+        // remove child layer
+        mTracing->onLayerRemoved(2);
+        VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
+        queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
+
+        // remove layer
+        mTracing->onLayerRemoved(1);
+        queueAndCommitTransaction(++mVsyncId);
+    }
+
+    void TearDown() override {
+        mTracing->disable();
+        verifyDisabledTracingState();
+        TransactionTracingTest::TearDown();
+    }
+
+    int mParentLayerId = 1;
+    int64_t mVsyncId = 0;
+    int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+    int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+    int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingLayerHandlingTest, addStartingState) {
+    // add transactions until we drop the transaction with the first layer change
+    while (bufferFront().vsync_id() <= VSYNC_ID_FIRST_LAYER_CHANGE) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify we can still retrieve the layer change from the first entry containing starting
+    // states.
+    EXPECT_GT(proto.entry().size(), 0);
+    EXPECT_GT(proto.entry(0).transactions().size(), 0);
+    EXPECT_GT(proto.entry(0).added_layers().size(), 0);
+    EXPECT_GT(proto.entry(0).transactions(0).layer_changes().size(), 0);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
+    // add transactions until we drop the transaction with the second layer change
+    while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify starting states are updated correctly
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
+    // add transactions until we drop the transaction which removes the child layer
+    while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify the child layer has been removed from the trace
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
+    // add transactions until we drop the transaction with the second layer change
+    while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto::TransactionTraceFile proto = writeToProto();
+    // verify we have two starting states
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+
+    // Continue adding transactions until child layer is removed
+    while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+        queueAndCommitTransaction(++mVsyncId);
+    }
+    proto = writeToProto();
+    // verify we still have the parent layer state
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+    EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
 } // namespace android