Merge "Add listener callback to keep track of tracing state in SCC"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 2519ffa..3184843 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -99,7 +99,9 @@
 
 /* Tracing categories */
 static const TracingCategory k_categories[] = {
-    { "gfx",        "Graphics",                 ATRACE_TAG_GRAPHICS, { } },
+    { "gfx",        "Graphics",                 ATRACE_TAG_GRAPHICS, {
+        { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
+    } },
     { "input",      "Input",                    ATRACE_TAG_INPUT, { } },
     { "view",       "View System",              ATRACE_TAG_VIEW, { } },
     { "webview",    "WebView",                  ATRACE_TAG_WEBVIEW, { } },
@@ -241,6 +243,7 @@
         { OPT,      "events/kmem/ion_heap_grow/enable" },
         { OPT,      "events/kmem/ion_heap_shrink/enable" },
         { OPT,      "events/ion/ion_stat/enable" },
+        { OPT,      "events/gpu_mem/gpu_mem_total/enable" },
     } },
     { "thermal",  "Thermal event", 0, {
         { REQ,      "events/thermal/thermal_temperature/enable" },
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8e00969..ad0a14e 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -246,6 +246,8 @@
     /* Return a new object that has a duplicate of this channel's fd. */
     std::unique_ptr<InputChannel> dup() const;
 
+    void copyTo(InputChannel& outChannel) const;
+
     status_t readFromParcel(const android::Parcel* parcel) override;
     status_t writeToParcel(android::Parcel* parcel) const override;
 
@@ -277,6 +279,8 @@
     }
 
 private:
+    base::unique_fd dupFd() const;
+
     std::string mName;
     android::base::unique_fd mFd;
 
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index ee010a3..886f1f7 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -96,7 +96,7 @@
     // are included in the movement.
     // The positions array contains position information for each pointer in order by
     // increasing id.  Its size should be equal to the number of one bits in idBits.
-    void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
     void addMovement(const MotionEvent* event);
@@ -149,7 +149,7 @@
     virtual void clear() = 0;
     virtual void clearPointers(BitSet32 idBits) = 0;
     virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions) = 0;
+                             const std::vector<VelocityTracker::Position>& positions) = 0;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
 };
 
@@ -180,8 +180,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -223,8 +223,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -257,8 +257,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -292,8 +292,8 @@
 
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
-    virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-            const VelocityTracker::Position* positions);
+    void addMovement(nsecs_t eventTime, BitSet32 idBits,
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index f44ce0c..a4f4441 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -93,7 +93,7 @@
     //
     // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit
     // ownership. Making this operator private to avoid double-ownership.
-#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 || defined(__ANDROID_APEX__)
    private:
 #else
     [[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]]
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index ceb6ade..81a5f02 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -590,6 +590,15 @@
                 f.pad(stringify!($interface))
             }
         }
+
+        // Convert a &dyn $interface to Box<dyn $interface>
+        impl std::borrow::ToOwned for dyn $interface {
+            type Owned = Box<dyn $interface>;
+            fn to_owned(&self) -> Self::Owned {
+                self.as_binder().into_interface()
+                    .expect(concat!("Error cloning interface ", stringify!($interface)))
+            }
+        }
     };
 }
 
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 4d306e7..ac79583 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -68,12 +68,10 @@
 public:
     InputSurface(const sp<SurfaceControl> &sc, int width, int height) {
         mSurfaceControl = sc;
-        std::unique_ptr<InputChannel> clientChannel;
-        InputChannel::openInputChannelPair("testchannels", mServerChannel, clientChannel);
-        mClientChannel = std::move(clientChannel);
 
         mInputFlinger = getInputFlinger();
-        mInputFlinger->registerInputChannel(*mServerChannel);
+        mClientChannel = std::make_shared<InputChannel>();
+        mInputFlinger->createInputChannel("testchannels", mClientChannel.get());
 
         populateInputInfo(width, height);
 
@@ -155,7 +153,7 @@
         EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
     }
 
-    ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel->getConnectionToken()); }
+    ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
 
     void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
                     const sp<SurfaceControl>&)> transactionBody) {
@@ -192,7 +190,7 @@
     }
 
     void populateInputInfo(int width, int height) {
-        mInputInfo.token = mServerChannel->getConnectionToken();
+        mInputInfo.token = mClientChannel->getConnectionToken();
         mInputInfo.name = "Test info";
         mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL;
         mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION;
@@ -219,7 +217,6 @@
     }
 public:
     sp<SurfaceControl> mSurfaceControl;
-    std::unique_ptr<InputChannel> mServerChannel;
     std::shared_ptr<InputChannel> mClientChannel;
     sp<IInputFlinger> mInputFlinger;
 
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 79e15c1..85df405 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -377,22 +377,16 @@
 }
 
 std::unique_ptr<InputChannel> InputChannel::dup() const {
-    android::base::unique_fd newFd(::dup(getFd()));
-    if (!newFd.ok()) {
-        ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
-              strerror(errno));
-        const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
-        // If this process is out of file descriptors, then throwing that might end up exploding
-        // on the other side of a binder call, which isn't really helpful.
-        // Better to just crash here and hope that the FD leak is slow.
-        // Other failures could be client errors, so we still propagate those back to the caller.
-        LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
-                            getName().c_str());
-        return nullptr;
-    }
+    base::unique_fd newFd(dupFd());
     return InputChannel::create(getName(), std::move(newFd), getConnectionToken());
 }
 
+void InputChannel::copyTo(InputChannel& outChannel) const {
+    outChannel.mName = getName();
+    outChannel.mFd = dupFd();
+    outChannel.mToken = getConnectionToken();
+}
+
 status_t InputChannel::writeToParcel(android::Parcel* parcel) const {
     if (parcel == nullptr) {
         ALOGE("%s: Null parcel", __func__);
@@ -415,6 +409,23 @@
     return mToken;
 }
 
+base::unique_fd InputChannel::dupFd() const {
+    android::base::unique_fd newFd(::dup(getFd()));
+    if (!newFd.ok()) {
+        ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(),
+              strerror(errno));
+        const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+        // If this process is out of file descriptors, then throwing that might end up exploding
+        // on the other side of a binder call, which isn't really helpful.
+        // Better to just crash here and hope that the FD leak is slow.
+        // Other failures could be client errors, so we still propagate those back to the caller.
+        LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
+                            getName().c_str());
+        return {};
+    }
+    return newFd;
+}
+
 // --- InputPublisher ---
 
 InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {}
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index bcf55b0..2c04d42 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -66,7 +66,7 @@
         if (deltaY) {
             mRawPosition.y += *deltaY;
         }
-        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition);
+        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
 
         float vx, vy;
         float scale = mParameters.scale;
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 7c28ac5..d74c520 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -193,7 +193,11 @@
     mStrategy->clearPointers(idBits);
 }
 
-void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
+void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
+                                  const std::vector<VelocityTracker::Position>& positions) {
+    LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
+                        "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+                        idBits.count(), positions.size());
     while (idBits.count() > MAX_POINTERS) {
         idBits.clearLastMarkedBit();
     }
@@ -286,7 +290,8 @@
     }
 
     nsecs_t eventTime;
-    Position positions[pointerCount];
+    std::vector<Position> positions;
+    positions.resize(pointerCount);
 
     size_t historySize = event->getHistorySize();
     for (size_t h = 0; h < historySize; h++) {
@@ -346,8 +351,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -758,8 +764,9 @@
     mPointerIdBits.value &= ~idBits.value;
 }
 
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     uint32_t index = 0;
     for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
         uint32_t id = iterIdBits.clearFirstMarkedBit();
@@ -876,8 +883,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void LegacyVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (++mIndex == HISTORY_SIZE) {
         mIndex = 0;
     }
@@ -990,8 +998,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-        const VelocityTracker::Position* positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl
index 0cdf0bb..1771d19 100644
--- a/libs/input/android/os/IInputFlinger.aidl
+++ b/libs/input/android/os/IInputFlinger.aidl
@@ -30,8 +30,8 @@
     // shouldn't be a concern.
     oneway void setInputWindows(in InputWindowInfo[] inputHandles,
             in @nullable ISetInputWindowsListener setInputWindowsListener);
-    void registerInputChannel(in InputChannel channel);
-    void unregisterInputChannel(in IBinder connectionToken);
+    InputChannel createInputChannel(in @utf8InCpp String name);
+    void removeInputChannel(in IBinder connectionToken);
     /**
      * Sets focus to the window identified by the token. This must be called
      * after updating any input window handles.
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 7cfe207..5438082 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -389,11 +389,25 @@
         return BAD_VALUE;
     }
     auto canvas = surface->getCanvas();
+    canvas->save();
 
+    // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+    // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+    // displays might have different scaling when compared to the physical screen.
+    canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
+    const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+            static_cast<SkScalar>(display.clip.width());
+    const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+            static_cast<SkScalar>(display.clip.height());
+    canvas->scale(scaleX, scaleY);
     canvas->clipRect(SkRect::MakeLTRB(display.clip.left, display.clip.top, display.clip.right,
                                       display.clip.bottom));
     canvas->drawColor(0, SkBlendMode::kSrc);
     for (const auto& layer : layers) {
+        SkPaint paint;
+        const auto& bounds = layer->geometry.boundaries;
+        const auto dest = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
             const auto& item = layer->source.buffer;
@@ -408,20 +422,38 @@
                                                                  : kUnpremul_SkAlphaType);
                 mImageCache.insert({item.buffer->getId(), image});
             }
-            const auto& bounds = layer->geometry.boundaries;
-            SkRect dest = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
-            canvas->drawImageRect(image, dest, nullptr);
+
+            SkMatrix matrix;
+            if (layer->geometry.roundedCornersRadius > 0) {
+                const auto roundedRect = getRoundedRect(layer);
+                matrix.setTranslate(roundedRect.getBounds().left() - dest.left(),
+                                    roundedRect.getBounds().top() - dest.top());
+            } else {
+                matrix.setIdentity();
+            }
+            paint.setShader(image->makeShader(matrix));
         } else {
             ATRACE_NAME("DrawColor");
-            SkPaint paint;
             const auto color = layer->source.solidColor;
             paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
         }
+
+        // Layers have a local transform matrix that should be applied to them.
+        canvas->save();
+        canvas->concat(getSkM44(layer->geometry.positionTransform));
+
+        if (layer->geometry.roundedCornersRadius > 0) {
+            canvas->drawRRect(getRoundedRect(layer), paint);
+        } else {
+            canvas->drawRect(dest, paint);
+        }
+        canvas->restore();
     }
     {
         ATRACE_NAME("flush surface");
         surface->flush();
     }
+    canvas->restore();
 
     if (drawFence != nullptr) {
         *drawFence = flush();
@@ -448,6 +480,20 @@
     return NO_ERROR;
 }
 
+inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
+    const auto& crop = layer->geometry.roundedCornersCrop;
+    const auto rect = SkRect::MakeLTRB(crop.left, crop.top, crop.right, crop.bottom);
+    const auto cornerRadius = layer->geometry.roundedCornersRadius;
+    return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
+}
+
+inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) {
+    return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0],
+                 matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1],
+                 matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2],
+                 matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
+}
+
 size_t SkiaGLRenderEngine::getMaxTextureSize() const {
     return mGrContext->maxTextureSize();
 }
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index b8387b2..7103bbd 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -60,6 +60,8 @@
                                        Protection protection);
     static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
                                                          int hwcFormat, Protection protection);
+    inline SkRRect getRoundedRect(const LayerSettings* layer);
+    inline SkM44 getSkM44(const mat4& matrix);
 
     base::unique_fd flush();
     bool waitFence(base::unique_fd fenceFd);
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 9a434e5..a197b3b 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -111,7 +111,7 @@
     void dump(std::string& result, const char* name, const char* prefix = "") const;
     void dump(const char* name, const char* prefix = "") const;
 
-    static RotationFlags toRotationFlags(Rotation);
+    static constexpr RotationFlags toRotationFlags(Rotation);
 
 private:
     struct mat33 {
@@ -136,7 +136,7 @@
     *os << out;
 }
 
-inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
+inline constexpr Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
     switch (rotation) {
         case ROTATION_0:
             return ROT_0;
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 1bcaab4..9a9bca1 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -44,11 +44,11 @@
     defaults: ["libgpuservice_defaults"],
     cflags: [
         "-fvisibility=hidden",
-        "-fwhole-program-vtables", // requires ThinLTO
     ],
     lto: {
         thin: true,
     },
+    whole_program_vtables: true, // Requires ThinLTO
 }
 
 filegroup {
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 8af9bcb..3d99589 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -31,6 +31,25 @@
 
 namespace android {
 
+static int32_t exceptionCodeFromStatusT(status_t status) {
+    switch (status) {
+        case OK:
+            return binder::Status::EX_NONE;
+        case INVALID_OPERATION:
+            return binder::Status::EX_UNSUPPORTED_OPERATION;
+        case BAD_VALUE:
+        case BAD_TYPE:
+        case NAME_NOT_FOUND:
+            return binder::Status::EX_ILLEGAL_ARGUMENT;
+        case NO_INIT:
+            return binder::Status::EX_ILLEGAL_STATE;
+        case PERMISSION_DENIED:
+            return binder::Status::EX_SECURITY;
+        default:
+            return binder::Status::EX_TRANSACTION_FAILED;
+    }
+}
+
 InputManager::InputManager(
         const sp<InputReaderPolicyInterface>& readerPolicy,
         const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
@@ -119,7 +138,7 @@
 }
 
 // Used by tests only.
-binder::Status InputManager::registerInputChannel(const InputChannel& channel) {
+binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int uid = ipc->getCallingUid();
     if (uid != AID_SHELL && uid != AID_ROOT) {
@@ -128,12 +147,17 @@
         return binder::Status::ok();
     }
 
-    mDispatcher->registerInputChannel(channel.dup());
+    base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
+    if (!channel) {
+        return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
+                                                 channel.error().message().c_str());
+    }
+    (*channel)->copyTo(*outChannel);
     return binder::Status::ok();
 }
 
-binder::Status InputManager::unregisterInputChannel(const sp<IBinder>& connectionToken) {
-    mDispatcher->unregisterInputChannel(connectionToken);
+binder::Status InputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
+    mDispatcher->removeInputChannel(connectionToken);
     return binder::Status::ok();
 }
 
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 35d2d58..49bea13 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -108,8 +108,8 @@
             const std::vector<InputWindowInfo>& handles,
             const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
 
-    binder::Status registerInputChannel(const InputChannel& channel) override;
-    binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+    binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+    binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
     binder::Status setFocusedWindow(const FocusRequest&) override;
 
 private:
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index b645d69..46bc055 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -129,17 +129,14 @@
 protected:
     explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
           : mDispatcher(dispatcher) {
-        std::unique_ptr<InputChannel> serverChannel, clientChannel;
-        InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-        mServerChannel = std::move(serverChannel);
-        mClientChannel = std::move(clientChannel);
+        mClientChannel = *mDispatcher->createInputChannel(name);
         mConsumer = std::make_unique<InputConsumer>(mClientChannel);
     }
 
     virtual ~FakeInputReceiver() {}
 
     sp<InputDispatcher> mDispatcher;
-    std::shared_ptr<InputChannel> mServerChannel, mClientChannel;
+    std::shared_ptr<InputChannel> mClientChannel;
     std::unique_ptr<InputConsumer> mConsumer;
     PreallocatedInputEventFactory mEventFactory;
 };
@@ -152,14 +149,12 @@
     FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
                      const sp<InputDispatcher>& dispatcher, const std::string name)
           : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
-        mDispatcher->registerInputChannel(mServerChannel);
-
         inputApplicationHandle->updateInfo();
         mInfo.applicationInfo = *inputApplicationHandle->getInfo();
     }
 
     virtual bool updateInfo() override {
-        mInfo.token = mServerChannel->getConnectionToken();
+        mInfo.token = mClientChannel->getConnectionToken();
         mInfo.name = "FakeWindowHandle";
         mInfo.type = InputWindowInfo::Type::APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a10da66..8b8105d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -28,8 +28,8 @@
 // Log debug messages about the dispatch cycle.
 #define DEBUG_DISPATCH_CYCLE 0
 
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
+// Log debug messages about channel creation
+#define DEBUG_CHANNEL_CREATION 0
 
 // Log debug messages about input event injection.
 #define DEBUG_INJECTION 0
@@ -351,6 +351,16 @@
     }
 }
 
+static status_t openInputChannelPair(const std::string& name,
+                                     std::shared_ptr<InputChannel>& serverChannel,
+                                     std::unique_ptr<InputChannel>& clientChannel) {
+    std::unique_ptr<InputChannel> uniqueServerChannel;
+    status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
+
+    serverChannel = std::move(uniqueServerChannel);
+    return result;
+}
+
 const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
     switch (result) {
         case InputDispatcher::FocusResult::OK:
@@ -412,7 +422,7 @@
 
     while (!mConnectionsByFd.empty()) {
         sp<Connection> connection = mConnectionsByFd.begin()->second;
-        unregisterInputChannel(connection->inputChannel->getConnectionToken());
+        removeInputChannel(connection->inputChannel->getConnectionToken());
     }
 }
 
@@ -4403,67 +4413,72 @@
     }
 }
 
-status_t InputDispatcher::registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) {
-#if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(
+        const std::string& name) {
+#if DEBUG_CHANNEL_CREATION
+    ALOGD("channel '%s' ~ createInputChannel", name.c_str());
 #endif
 
+    std::shared_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> clientChannel;
+    status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+
+    if (result) {
+        return base::Error(result) << "Failed to open input channel pair with name " << name;
+    }
+
     { // acquire lock
         std::scoped_lock _l(mLock);
-        sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
-        if (existingConnection != nullptr) {
-            ALOGW("Attempted to register already registered input channel '%s'",
-                  inputChannel->getName().c_str());
-            return BAD_VALUE;
-        }
+        sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator);
 
-        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
-
-        int fd = inputChannel->getFd();
+        int fd = serverChannel->getFd();
         mConnectionsByFd[fd] = connection;
-        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+        mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     } // release lock
 
     // Wake the looper because some connections have changed.
     mLooper->wake();
-    return OK;
+    return clientChannel;
 }
 
-status_t InputDispatcher::registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
-                                               int32_t displayId, bool isGestureMonitor) {
+base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
+        int32_t displayId, bool isGestureMonitor, const std::string& name) {
+    std::shared_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> clientChannel;
+    status_t result = openInputChannelPair(name, serverChannel, clientChannel);
+    if (result) {
+        return base::Error(result) << "Failed to open input channel pair with name " << name;
+    }
+
     { // acquire lock
         std::scoped_lock _l(mLock);
 
         if (displayId < 0) {
-            ALOGW("Attempted to register input monitor without a specified display.");
-            return BAD_VALUE;
+            return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
+                                          << " without a specified display.";
         }
 
-        if (inputChannel->getConnectionToken() == nullptr) {
-            ALOGW("Attempted to register input monitor without an identifying token.");
-            return BAD_VALUE;
-        }
+        sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator);
 
-        sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
-
-        const int fd = inputChannel->getFd();
+        const int fd = serverChannel->getFd();
         mConnectionsByFd[fd] = connection;
-        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
+        mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel;
 
         auto& monitorsByDisplay =
                 isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
-        monitorsByDisplay[displayId].emplace_back(inputChannel);
+        monitorsByDisplay[displayId].emplace_back(serverChannel);
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     }
+
     // Wake the looper because some connections have changed.
     mLooper->wake();
-    return OK;
+    return clientChannel;
 }
 
-status_t InputDispatcher::unregisterInputChannel(const sp<IBinder>& connectionToken) {
+status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
     { // acquire lock
         std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index f3b3dda..c021f65 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -116,12 +116,12 @@
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
                                     const sp<IBinder>& toToken) override;
 
-    virtual status_t registerInputChannel(
-            const std::shared_ptr<InputChannel>& inputChannel) override;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+            const std::string& name) override;
     virtual void setFocusedWindow(const FocusRequest&) override;
-    virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
-                                          int32_t displayId, bool isGestureMonitor) override;
-    virtual status_t unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+            int32_t displayId, bool isGestureMonitor, const std::string& name) override;
+    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
     virtual status_t pilferPointers(const sp<IBinder>& token) override;
 
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index c3d50ea..67d9a06 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -18,6 +18,7 @@
 #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H
 
 #include <InputListener.h>
+#include <android-base/result.h>
 #include <android/FocusRequest.h>
 #include <android/os/ISetInputWindowsListener.h>
 #include <input/InputApplication.h>
@@ -155,13 +156,16 @@
      */
     virtual void setFocusedWindow(const FocusRequest&) = 0;
 
-    /* Registers input channels that may be used as targets for input events.
+    /**
+     * Creates an input channel that may be used as targets for input events.
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const std::shared_ptr<InputChannel>& inputChannel) = 0;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
+            const std::string& name) = 0;
 
-    /* Registers input channels to be used to monitor input events.
+    /**
+     * Creates an input channel to be used to monitor input events.
      *
      * Each monitor must target a specific display and will only receive input events sent to that
      * display. If the monitor is a gesture monitor, it will only receive pointer events on the
@@ -169,14 +173,14 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputMonitor(const std::shared_ptr<InputChannel>& inputChannel,
-                                          int32_t displayId, bool gestureMonitor) = 0;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+            int32_t displayId, bool gestureMonitor, const std::string& name) = 0;
 
-    /* Unregister input channels that will no longer receive input events.
+    /* Removes input channels that will no longer receive input events.
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t unregisterInputChannel(const sp<IBinder>& connectionToken) = 0;
+    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) = 0;
 
     /* Allows an input monitor steal the current pointer stream away from normal input windows.
      *
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 577309a..47773d9 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -48,8 +48,10 @@
                                    const sp<ISetInputWindowsListener>&) {
         return binder::Status::ok();
     }
-    binder::Status registerInputChannel(const InputChannel&) { return binder::Status::ok(); }
-    binder::Status unregisterInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
+    binder::Status createInputChannel(const std::string&, InputChannel*) {
+        return binder::Status::ok();
+    }
+    binder::Status removeInputChannel(const sp<IBinder>&) { return binder::Status::ok(); }
     binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); }
 
 private:
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index b00e870..e957826 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2624,14 +2624,14 @@
 
     // Update the velocity tracker.
     {
-        VelocityTracker::Position positions[MAX_POINTERS];
-        uint32_t count = 0;
-        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) {
+        std::vector<VelocityTracker::Position> positions;
+        for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            positions[count].x = pointer.x * mPointerXMovementScale;
-            positions[count].y = pointer.y * mPointerYMovementScale;
+            float x = pointer.x * mPointerXMovementScale;
+            float y = pointer.y * mPointerYMovementScale;
+            positions.push_back({x, y});
         }
         mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
                                                     positions);
diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl
index b5c5c9e..5c8a8da 100644
--- a/services/inputflinger/tests/IInputFlingerQuery.aidl
+++ b/services/inputflinger/tests/IInputFlingerQuery.aidl
@@ -26,4 +26,5 @@
     void getInputWindows(out InputWindowInfo[] inputHandles);
     void getInputChannels(out InputChannel[] channels);
     void getLastFocusRequest(out FocusRequest request);
+    void resetInputManager();
 }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ad6a602..6ef0028 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -548,10 +548,9 @@
 
 class FakeInputReceiver {
 public:
-    explicit FakeInputReceiver(const std::shared_ptr<InputChannel>& clientChannel,
-                               const std::string name)
+    explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
           : mName(name) {
-        mConsumer = std::make_unique<InputConsumer>(clientChannel);
+        mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel));
     }
 
     InputEvent* consume() {
@@ -701,11 +700,10 @@
                      int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
           : mName(name) {
         if (token == std::nullopt) {
-            std::unique_ptr<InputChannel> serverChannel, clientChannel;
-            InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-            mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
-            token = serverChannel->getConnectionToken();
-            dispatcher->registerInputChannel(std::move(serverChannel));
+            base::Result<std::unique_ptr<InputChannel>> channel =
+                    dispatcher->createInputChannel(name);
+            token = (*channel)->getConnectionToken();
+            mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
         }
 
         inputApplicationHandle->updateInfo();
@@ -1653,10 +1651,9 @@
 public:
     FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
                         int32_t displayId, bool isGestureMonitor = false) {
-        std::unique_ptr<InputChannel> serverChannel, clientChannel;
-        InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
-        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(clientChannel), name);
-        dispatcher->registerInputMonitor(std::move(serverChannel), displayId, isGestureMonitor);
+        base::Result<std::unique_ptr<InputChannel>> channel =
+                dispatcher->createInputMonitor(displayId, isGestureMonitor, name);
+        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
     }
 
     sp<IBinder> getToken() { return mInputReceiver->getToken(); }
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index 3aef1e4..c368e79 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -135,7 +135,6 @@
 
 public:
     TestInputManager(){};
-    void checkFdFlags(const android::base::unique_fd& fd);
 
     binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles);
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels);
@@ -147,10 +146,12 @@
             const std::vector<InputWindowInfo>& handles,
             const sp<ISetInputWindowsListener>& setInputWindowsListener) override;
 
-    binder::Status registerInputChannel(const InputChannel& channel) override;
-    binder::Status unregisterInputChannel(const sp<IBinder>& connectionToken) override;
+    binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
+    binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override;
     binder::Status setFocusedWindow(const FocusRequest&) override;
 
+    void reset();
+
 private:
     mutable Mutex mLock;
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay;
@@ -164,6 +165,7 @@
     binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override;
     binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override;
     binder::Status getLastFocusRequest(FocusRequest*) override;
+    binder::Status resetInputManager() override;
 
 private:
     sp<android::TestInputManager> mManager;
@@ -182,6 +184,11 @@
     return mManager->getLastFocusRequest(request);
 }
 
+binder::Status TestInputQuery::resetInputManager() {
+    mManager->reset();
+    return binder::Status::ok();
+}
+
 binder::Status SetInputWindowsListener::onSetInputWindowsFinished() {
     if (mCbFunc != nullptr) {
         mCbFunc();
@@ -204,23 +211,21 @@
     return binder::Status::ok();
 }
 
-void TestInputManager::checkFdFlags(const android::base::unique_fd& fd) {
-    const int result = fcntl(fd, F_GETFL);
-    EXPECT_NE(result, -1);
-    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
-}
-
-binder::Status TestInputManager::registerInputChannel(const InputChannel& channel) {
+binder::Status TestInputManager::createInputChannel(const std::string& name,
+                                                    InputChannel* outChannel) {
     AutoMutex _l(mLock);
-    // check Fd flags
-    checkFdFlags(channel.getFd());
+    std::unique_ptr<InputChannel> serverChannel;
+    std::unique_ptr<InputChannel> clientChannel;
+    InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 
-    mInputChannels.push_back(channel.dup());
+    clientChannel->copyTo(*outChannel);
+
+    mInputChannels.emplace_back(std::move(serverChannel));
 
     return binder::Status::ok();
 }
 
-binder::Status TestInputManager::unregisterInputChannel(const sp<IBinder>& connectionToken) {
+binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) {
     AutoMutex _l(mLock);
 
     auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(),
@@ -271,6 +276,12 @@
     return binder::Status::ok();
 }
 
+void TestInputManager::reset() {
+    mHandlesPerDisplay.clear();
+    mInputChannels.clear();
+    mFocusRequest = FocusRequest();
+}
+
 void InputFlingerServiceTest::SetUp() {
     mSetInputWindowsListener = new SetInputWindowsListener([&]() {
         std::unique_lock<std::mutex> lock(mLock);
@@ -316,7 +327,9 @@
     InitializeInputFlinger();
 }
 
-void InputFlingerServiceTest::TearDown() {}
+void InputFlingerServiceTest::TearDown() {
+    mQuery->resetInputManager();
+}
 
 void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const {
     EXPECT_EQ(mInfo, info);
@@ -367,45 +380,33 @@
 }
 
 /**
- *  Test InputFlinger service interface registerInputChannel
+ *  Test InputFlinger service interface createInputChannel
  */
-TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannel) {
-    std::unique_ptr<InputChannel> serverChannel, clientChannel;
+TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) {
+    // Test that the unblocked file descriptor flag is kept across processes over binder
+    // transactions.
 
-    InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
-    mService->registerInputChannel(*serverChannel);
+    InputChannel channel;
+    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
+
+    const base::unique_fd& fd = channel.getFd();
+    ASSERT_TRUE(fd.ok());
+
+    const int result = fcntl(fd, F_GETFL);
+    EXPECT_NE(result, -1);
+    EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK);
+}
+
+TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) {
+    InputChannel channel;
+    ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk());
 
     std::vector<::android::InputChannel> channels;
     mQuery->getInputChannels(&channels);
     ASSERT_EQ(channels.size(), 1UL);
-    EXPECT_EQ(channels[0], *serverChannel);
+    EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken());
 
-    mService->unregisterInputChannel(serverChannel->getConnectionToken());
-    mQuery->getInputChannels(&channels);
-    EXPECT_EQ(channels.size(), 0UL);
-}
-
-/**
- *  Test InputFlinger service interface registerInputChannel with invalid cases
- */
-TEST_F(InputFlingerServiceTest, InputWindow_RegisterInputChannelInvalid) {
-    std::unique_ptr<InputChannel> serverChannel, clientChannel;
-    InputChannel::openInputChannelPair("testchannels", serverChannel, clientChannel);
-
-    std::vector<::android::InputChannel> channels;
-    mQuery->getInputChannels(&channels);
-    EXPECT_EQ(channels.size(), 0UL);
-
-    mService->registerInputChannel(InputChannel());
-    mService->unregisterInputChannel(clientChannel->getConnectionToken());
-
-    mService->registerInputChannel(*serverChannel);
-    mService->registerInputChannel(*clientChannel);
-    mQuery->getInputChannels(&channels);
-    EXPECT_EQ(channels.size(), 2UL);
-
-    mService->unregisterInputChannel(clientChannel->getConnectionToken());
-    mService->unregisterInputChannel(serverChannel->getConnectionToken());
+    mService->removeInputChannel(channel.getConnectionToken());
     mQuery->getInputChannels(&channels);
     EXPECT_EQ(channels.size(), 0UL);
 }
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 04e9a2d..7c08e11 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -102,11 +102,11 @@
     defaults: ["libsurfaceflinger_defaults"],
     cflags: [
         "-fvisibility=hidden",
-        "-fwhole-program-vtables", // requires ThinLTO
     ],
     lto: {
         thin: true,
     },
+    whole_program_vtables: true, // Requires ThinLTO
     // TODO(b/131771163): Fix broken fuzzer support with LTO.
     sanitize: {
         fuzzer: false,
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index d460d3d..4cbfdff 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -37,6 +37,7 @@
 #include "BufferLayerConsumer.h"
 #include "Client.h"
 #include "DisplayHardware/HWComposer.h"
+#include "FrameTimeline.h"
 #include "FrameTracker.h"
 #include "Layer.h"
 #include "LayerVector.h"
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 97ad212..33126ab 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
 #include "TimeStats/TimeStats.h"
 
 namespace android {
+using PresentState = frametimeline::SurfaceFrame::PresentState;
 
 BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
 
@@ -109,7 +110,7 @@
 
     Mutex::Autolock lock(mQueueItemLock);
 
-    const int64_t addedTime = mQueueItems[0].mTimestamp;
+    const int64_t addedTime = mQueueItems[0].item.mTimestamp;
 
     // Ignore timestamps more than a second in the future
     const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
@@ -136,7 +137,7 @@
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    if (mQueueItems[0].mIsDroppable) {
+    if (mQueueItems[0].item.mIsDroppable) {
         // Even though this buffer's fence may not have signaled yet, it could
         // be replaced by another buffer before it has a chance to, which means
         // that it's possible to get into a situation where a buffer is never
@@ -144,7 +145,7 @@
         return true;
     }
     const bool fenceSignaled =
-            mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+            mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
     if (!fenceSignaled) {
         mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
                                                     TimeStats::LatchSkipReason::LateAcquire);
@@ -159,12 +160,12 @@
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    return mQueueItems[0].mTimestamp <= expectedPresentTime;
+    return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
 }
 
 uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
-    uint64_t frameNumber = mQueueItems[0].mFrameNumber;
+    uint64_t frameNumber = mQueueItems[0].item.mFrameNumber;
 
     // The head of the queue will be dropped if there are signaled and timely frames behind it
     if (isRemovedFromCurrentState()) {
@@ -173,23 +174,23 @@
 
     for (int i = 1; i < mQueueItems.size(); i++) {
         const bool fenceSignaled =
-                mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+                mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
         if (!fenceSignaled) {
             break;
         }
 
         // We don't drop frames without explicit timestamps
-        if (mQueueItems[i].mIsAutoTimestamp) {
+        if (mQueueItems[i].item.mIsAutoTimestamp) {
             break;
         }
 
-        const nsecs_t desiredPresent = mQueueItems[i].mTimestamp;
+        const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp;
         if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
             desiredPresent > expectedPresentTime) {
             break;
         }
 
-        frameNumber = mQueueItems[i].mFrameNumber;
+        frameNumber = mQueueItems[i].item.mFrameNumber;
     }
 
     return frameNumber;
@@ -254,11 +255,11 @@
         Mutex::Autolock lock(mQueueItemLock);
         for (int i = 0; i < mQueueItems.size(); i++) {
             bool fenceSignaled =
-                    mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+                    mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
             if (!fenceSignaled) {
                 break;
             }
-            lastSignaledFrameNumber = mQueueItems[i].mFrameNumber;
+            lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber;
         }
     }
     const uint64_t maxFrameNumberToAcquire =
@@ -276,9 +277,13 @@
         // and return early
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
-            mQueueItems.removeAt(0);
+            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+            if (mQueueItems[0].surfaceFrame) {
+                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+                                                          PresentState::Dropped);
+            }
+            mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
         }
         return BAD_VALUE;
@@ -289,6 +294,12 @@
         // early.
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
+            for (auto& [item, surfaceFrame] : mQueueItems) {
+                if (surfaceFrame) {
+                    mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                                              PresentState::Dropped);
+                }
+            }
             mQueueItems.clear();
             mQueuedFrames = 0;
             mFlinger->mTimeStats->onDestroy(layerId);
@@ -312,19 +323,29 @@
 
         // Remove any stale buffers that have been dropped during
         // updateTexImage
-        while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
-            mQueueItems.removeAt(0);
+        while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+            if (mQueueItems[0].surfaceFrame) {
+                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+                                                          PresentState::Dropped);
+            }
+            mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
         }
 
-        uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
+        uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId();
         mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
         mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
                                                FrameTracer::FrameEvent::LATCH);
 
-        mQueueItems.removeAt(0);
+        if (mQueueItems[0].surfaceFrame) {
+            mQueueItems[0].surfaceFrame->setActualEndTime(
+                    mQueueItems[0].item.mFenceTime->getSignalTime());
+            mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
+                                                      PresentState::Presented);
+        }
+        mQueueItems.erase(mQueueItems.begin());
     }
 
     // Decrement the queued-frames count.  Signal another event if we
@@ -416,7 +437,11 @@
             }
         }
 
-        mQueueItems.push_back(item);
+        auto surfaceFrame =
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+        surfaceFrame->setActualQueueTime(systemTime());
+
+        mQueueItems.push_back({item, std::move(surfaceFrame)});
         mQueuedFrames++;
 
         // Wake up any pending callbacks
@@ -449,7 +474,12 @@
             ALOGE("Can't replace a frame on an empty queue");
             return;
         }
-        mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
+
+        auto surfaceFrame =
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+        surfaceFrame->setActualQueueTime(systemTime());
+        mQueueItems[mQueueItems.size() - 1].item = item;
+        mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
 
         // Wake up any pending callbacks
         mLastFrameNumberReceived = item.mFrameNumber;
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 7d06128..fc992f7 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -22,6 +22,10 @@
 
 namespace android {
 
+namespace frametimeline {
+class SurfaceFrame;
+}
+
 /*
  * A new BufferQueue and a new BufferLayerConsumer are created when the
  * BufferLayer is first referenced.
@@ -125,7 +129,14 @@
     // Local copy of the queued contents of the incoming BufferQueue
     mutable Mutex mQueueItemLock;
     Condition mQueueItemCondition;
-    Vector<BufferItem> mQueueItems;
+
+    struct BufferData {
+        BufferData(BufferItem item, std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame)
+              : item(item), surfaceFrame(std::move(surfaceFrame)) {}
+        BufferItem item;
+        std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame;
+    };
+    std::vector<BufferData> mQueueItems;
     std::atomic<uint64_t> mLastFrameNumberReceived{0};
 
     bool mAutoRefresh{false};
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 4863297..57dc60b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -95,8 +95,9 @@
         "tests/MockHWC2.cpp",
         "tests/MockHWComposer.cpp",
         "tests/MockPowerAdvisor.cpp",
-        "tests/OutputTest.cpp",
         "tests/OutputLayerTest.cpp",
+        "tests/OutputTest.cpp",
+        "tests/ProjectionSpaceTest.cpp",
         "tests/RenderSurfaceTest.cpp",
     ],
     static_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 6552c54..fc1adcc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -163,9 +163,8 @@
     virtual void setCompositionEnabled(bool) = 0;
 
     // Sets the projection state to use
-    virtual void setProjection(const ui::Transform&, uint32_t orientation,
-                               const Rect& orientedDisplaySpaceRect,
-                               const Rect& layerStackSpaceRect, const Rect& displaySpaceRect) = 0;
+    virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+                               const Rect& orientedDisplaySpaceRect) = 0;
     // Sets the bounds to use
     virtual void setDisplaySpaceSize(const ui::Size&) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 9d15665..7ca91d8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -38,14 +38,73 @@
 
     // Rect onto which content is projected.
     Rect content;
+
+    // The orientation of this space. This value is meaningful only in relation to the rotation
+    // of another projection space and it's used to determine the rotating transformation when
+    // mapping between the two.
+    // As a convention when using this struct orientation = 0 for the "oriented*" projection
+    // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+    // of the display space will become 90, while  the orientation of the layer stack space will
+    // remain the same.
+    ui::Rotation orientation = ui::ROTATION_0;
+
+    // Returns a transform which maps this.content into destination.content
+    // and also rotates according to this.orientation and destination.orientation
+    ui::Transform getTransform(const ProjectionSpace& destination) const {
+        ui::Rotation rotation = destination.orientation - orientation;
+
+        // Compute a transformation which rotates the destination in a way it has the same
+        // orientation as us.
+        const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
+        ui::Transform inverseRotatingTransform;
+        inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
+                                     destination.bounds.height());
+        // The destination content rotated so it has the same orientation as us.
+        Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+
+        // Compute translation from the source content to (0, 0).
+        const float sourceX = content.left;
+        const float sourceY = content.top;
+        ui::Transform sourceTranslation;
+        sourceTranslation.set(-sourceX, -sourceY);
+
+        // Compute scaling transform which maps source content to destination content, assuming
+        // they are both at (0, 0).
+        ui::Transform scale;
+        const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
+        const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+        scale.set(scaleX, 0, 0, scaleY);
+
+        // Compute translation from (0, 0) to the orientated destination content.
+        const float destX = orientedDestContent.left;
+        const float destY = orientedDestContent.top;
+        ui::Transform destTranslation;
+        destTranslation.set(destX, destY);
+
+        // Compute rotation transform.
+        const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
+        auto orientedDestWidth = destination.bounds.width();
+        auto orientedDestHeight = destination.bounds.height();
+        if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+            std::swap(orientedDestWidth, orientedDestHeight);
+        }
+        ui::Transform rotationTransform;
+        rotationTransform.set(orientationFlags, orientedDestWidth, orientedDestHeight);
+
+        // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
+        // Apply the logical translation, scale to physical size, apply the
+        // physical translation and finally rotate to the physical orientation.
+        return rotationTransform * destTranslation * scale * sourceTranslation;
+    }
 };
 
 } // namespace compositionengine
 
 inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
-    return android::base::StringPrintf("ProjectionSpace(bounds = %s, content = %s)",
-                                       to_string(space.bounds).c_str(),
-                                       to_string(space.content).c_str());
+    return android::base::
+            StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
+                         to_string(space.bounds).c_str(), to_string(space.content).c_str(),
+                         toCString(space.orientation));
 }
 
 // Defining PrintTo helps with Google Tests.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index e009894..6fe93bf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -38,9 +38,8 @@
     bool isValid() const override;
     std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
-    void setProjection(const ui::Transform&, uint32_t orientation,
-                       const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
-                       const Rect& displaySpaceRect) override;
+    void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+                       const Rect& orientedDisplaySpaceRect) override;
     void setDisplaySpaceSize(const ui::Size&) override;
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 462d952..f4d2b56 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -64,13 +64,11 @@
     uint32_t layerStackId{~0u};
 
     // The common space for all layers in the layer stack. layerStackSpace.content is the Rect
-    // which gets projected on the display. The content in this space is always in a single
-    // orientation.
+    // which gets projected on the display. The orientation of this space is always ROTATION_0.
     ProjectionSpace layerStackSpace;
 
     // Oriented physical display space. It will have the same size as displaySpace oriented to
-    // match the orientation of layerStackSpace. The content in this space is always in a single
-    // orientation.
+    // match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0.
     ProjectionSpace orientedDisplaySpace;
 
     // The space of the physical display. It is as big as the currently active display mode. The
@@ -80,9 +78,6 @@
     // Transformation from layerStackSpace to displaySpace
     ui::Transform transform;
 
-    // The physical orientation of the display, expressed as ui::Transform orientation flags.
-    uint32_t orientation{0};
-
     // If true, RenderEngine filtering should be enabled
     bool needsFiltering{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 5350611..19025c1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -36,8 +36,7 @@
     MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
-    MOCK_METHOD5(setProjection,
-                 void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&));
+    MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
     MOCK_METHOD1(setDisplaySpaceSize, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 816a09b..abb8769 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -105,25 +105,34 @@
     dirtyEntireOutput();
 }
 
-void Output::setProjection(const ui::Transform& transform, uint32_t orientation,
-                           const Rect& orientedDisplaySpaceRect, const Rect& layerStackSpaceRect,
-                           const Rect& displaySpaceRect) {
+void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
+                           const Rect& orientedDisplaySpaceRect) {
     auto& outputState = editState();
-    outputState.transform = transform;
-    outputState.orientation = orientation;
-    outputState.displaySpace.content = displaySpaceRect;
-    // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
-    outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
 
+    outputState.displaySpace.orientation = orientation;
+    // outputState.displaySpace.bounds should be already set from setDisplaySpaceSize().
+
+    // Compute the orientedDisplaySpace bounds
     ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
-    if (orientation == ui::Transform::ROT_90 || orientation == ui::Transform::ROT_270) {
+    if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
         std::swap(orientedSize.width, orientedSize.height);
     }
     outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
+    outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+
+    // Compute displaySpace.content
+    const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
+    ui::Transform rotation;
+    if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
+        const auto displaySize = outputState.displaySpace.bounds;
+        rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
+    }
+    outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
 
     outputState.layerStackSpace.content = layerStackSpaceRect;
     outputState.layerStackSpace.bounds = layerStackSpaceRect;
-    outputState.needsFiltering = transform.needsBilinearFiltering();
+    outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
+    outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
 
     dirtyEntireOutput();
 }
@@ -870,7 +879,8 @@
     renderengine::DisplaySettings clientCompositionDisplay;
     clientCompositionDisplay.physicalDisplay = outputState.displaySpace.content;
     clientCompositionDisplay.clip = outputState.layerStackSpace.content;
-    clientCompositionDisplay.orientation = outputState.orientation;
+    clientCompositionDisplay.orientation =
+            ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
     clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
             ? outputState.dataspace
             : ui::Dataspace::UNKNOWN;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0f53641..0faab6f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -133,7 +133,8 @@
          * the code below applies the primary display's inverse transform to the
          * buffer
          */
-        uint32_t invTransformOrient = outputState.orientation;
+        uint32_t invTransformOrient =
+                ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
         // calculate the inverse transform
         if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
             invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index d9fb098..dcfc162 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -21,6 +21,7 @@
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
+#include <log/log.h>
 
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
@@ -55,6 +56,22 @@
     return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
 }
 
+ui::Rotation toRotation(uint32_t rotationFlag) {
+    switch (rotationFlag) {
+        case ui::Transform::RotationFlags::ROT_0:
+            return ui::ROTATION_0;
+        case ui::Transform::RotationFlags::ROT_90:
+            return ui::ROTATION_90;
+        case ui::Transform::RotationFlags::ROT_180:
+            return ui::ROTATION_180;
+        case ui::Transform::RotationFlags::ROT_270:
+            return ui::ROTATION_270;
+        default:
+            LOG_FATAL("Unexpected rotation flag %d", rotationFlag);
+            return ui::Rotation(-1);
+    }
+}
+
 struct OutputLayerTest : public testing::Test {
     struct OutputLayer final : public impl::OutputLayer {
         OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
@@ -209,7 +226,7 @@
 
         mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.orientation = entry.display;
+        mOutputState.displaySpace.orientation = toRotation(entry.display);
 
         EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
     }
@@ -358,7 +375,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.orientation = entry.display;
+        mOutputState.displaySpace.orientation = toRotation(entry.display);
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -470,7 +487,7 @@
 
         mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
-        mOutputState.orientation = entry.display;
+        mOutputState.displaySpace.orientation = toRotation(entry.display);
         mOutputState.transform = ui::Transform{entry.display};
 
         const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -853,7 +870,7 @@
     // This test simulates a scenario where displayInstallOrientation is set to
     // ROT_90. This only has an effect on the transform; orientation stays 0 (see
     // DisplayDevice::setProjection).
-    mOutputState.orientation = TR_IDENT;
+    mOutputState.displaySpace.orientation = ui::ROTATION_0;
     mOutputState.transform = ui::Transform{TR_ROT_90};
     // Buffers are pre-rotated based on the transform hint (ROT_90); their
     // geomBufferTransform is set to the inverse transform.
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 23efd2d..c01f3e0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -236,19 +236,15 @@
  */
 
 TEST_F(OutputTest, setProjectionTriviallyWorks) {
-    const ui::Transform transform{ui::Transform::ROT_180};
-    const int32_t orientation = 123;
+    const ui::Rotation orientation = ui::ROTATION_90;
     const Rect frame{1, 2, 3, 4};
     const Rect viewport{5, 6, 7, 8};
-    const Rect destinationClip{13, 14, 15, 16};
 
-    mOutput->setProjection(transform, orientation, frame, viewport, destinationClip);
+    mOutput->setProjection(orientation, viewport, frame);
 
-    EXPECT_THAT(mOutput->getState().transform, transform);
-    EXPECT_EQ(orientation, mOutput->getState().orientation);
+    EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
     EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
     EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
-    EXPECT_EQ(destinationClip, mOutput->getState().displaySpace.content);
 }
 
 /*
@@ -2783,8 +2779,8 @@
         mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
         mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
         mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
-        mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
-        mOutput.mState.orientation = kDefaultOutputOrientation;
+        mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
+        mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
         mOutput.mState.dataspace = kDefaultOutputDataspace;
         mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
         mOutput.mState.isSecure = false;
@@ -2819,7 +2815,9 @@
     // Call this member function to start using the mini-DSL defined above.
     [[nodiscard]] auto verify() { return ExecuteState::make(this); }
 
-    static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+    static constexpr ui::Rotation kDefaultOutputOrientation = ui::ROTATION_0;
+    static constexpr uint32_t kDefaultOutputOrientationFlags =
+            ui::Transform::toRotationFlags(kDefaultOutputOrientation);
     static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
     static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
     static constexpr float kDefaultMaxLuminance = 0.9f;
@@ -3116,7 +3114,7 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3127,7 +3125,7 @@
             .andIfSkipColorTransform(false)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3139,7 +3137,7 @@
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
-                                            kDefaultOutputOrientation})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3151,7 +3149,7 @@
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace,
                                             kDefaultColorTransformMat, Region::INVALID_REGION,
-                                            kDefaultOutputOrientation})
+                                            kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3163,7 +3161,7 @@
             .andIfSkipColorTransform(true)
             .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
                                             kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
-                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+                                            Region::INVALID_REGION, kDefaultOutputOrientationFlags})
             .execute()
             .expectAFenceWasReturned();
 }
@@ -3409,8 +3407,9 @@
         mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
         mOutput.mState.layerStackSpace.content = kDisplayViewport;
         mOutput.mState.displaySpace.content = kDisplayDestinationClip;
-        mOutput.mState.transform = ui::Transform{kDisplayOrientation};
-        mOutput.mState.orientation = kDisplayOrientation;
+        mOutput.mState.transform =
+                ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
+        mOutput.mState.displaySpace.orientation = kDisplayOrientation;
         mOutput.mState.needsFiltering = false;
         mOutput.mState.isSecure = false;
 
@@ -3434,7 +3433,7 @@
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
     }
 
-    static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+    static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
     static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
 
     static const Rect kDisplayFrame;
@@ -3918,14 +3917,14 @@
     const Rect kPortraitFrame(0, 0, 1000, 2000);
     const Rect kPortraitViewport(0, 0, 2000, 1000);
     const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
-    const uint32_t kPortraitOrientation = TR_ROT_90;
+    const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
     constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
 
     mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
     mOutput.mState.layerStackSpace.content = kPortraitViewport;
     mOutput.mState.displaySpace.content = kPortraitDestinationClip;
-    mOutput.mState.transform = ui::Transform{kPortraitOrientation};
-    mOutput.mState.orientation = kPortraitOrientation;
+    mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
+    mOutput.mState.displaySpace.orientation = kPortraitOrientation;
     mOutput.mState.needsFiltering = false;
     mOutput.mState.isSecure = true;
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
new file mode 100644
index 0000000..704f5a8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <compositionengine/ProjectionSpace.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+// Returns a rectangular strip along the side of the given rect pointed by
+// rotation. E.g. if rotation is ROTATION_0, the srip will be along the top
+// side, if it is ROTATION_90 the stip will be along the right wall.
+// One of the dimensions of the strip will be 0 and the other one will match
+// the length of the corresponding side.
+// The strip will be contained inside the given rect.
+Rect getSideStrip(const Rect& rect, ui::Rotation rotation) {
+    int width, height;
+    if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
+        width = 0;
+        height = rect.height();
+    } else {
+        width = rect.width();
+        height = 0;
+    }
+
+    if (rotation == ui::ROTATION_0 || rotation == ui::ROTATION_270) {
+        return Rect(rect.left, rect.top, rect.left + width, rect.top + height);
+    }
+
+    if (rotation == ui::ROTATION_90) {
+        return Rect(rect.right, rect.top, rect.right + width, rect.top + height);
+    }
+
+    if (rotation == ui::ROTATION_180) {
+        return Rect(rect.left, rect.bottom, rect.left + width, rect.bottom + height);
+    }
+
+    return Rect::INVALID_RECT;
+}
+} // namespace
+
+TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
+    ProjectionSpace space;
+    space.content = Rect(100, 200);
+    space.bounds = Rect(100, 200);
+
+    const ui::Transform identity;
+    for (int rotation = 0; rotation <= 3; rotation++) {
+        space.orientation = ui::Rotation(rotation);
+        EXPECT_EQ(space.getTransform(space), identity);
+    }
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
+    ProjectionSpace source;
+    source.content = Rect(10, 10, 20, 20);
+    source.bounds = Rect(100, 200);
+
+    ProjectionSpace dest;
+    dest.content = Rect(10, 20, 30, 20);
+    dest.bounds = source.bounds;
+
+    const auto transform = source.getTransform(dest);
+    EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
+    ProjectionSpace source;
+    source.content = Rect(0, 0, 20, 20);
+    source.bounds = Rect(100, 200);
+
+    ProjectionSpace dest;
+    dest.content = Rect(0, 0, 40, 30);
+    dest.bounds = source.bounds;
+
+    const auto transform = source.getTransform(dest);
+    EXPECT_EQ(transform.transform(source.content), dest.content);
+}
+
+TEST(ProjectionSpaceTest, getSideStripTest) {
+    const Rect rect(10, 20, 40, 100);
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_0), Rect(10, 20, 40, 20));
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_90), Rect(40, 20, 40, 100));
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_180), Rect(10, 100, 40, 100));
+    EXPECT_EQ(getSideStrip(rect, ui::ROTATION_270), Rect(10, 20, 10, 100));
+}
+
+void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
+    const auto transform = source.getTransform(dest);
+    EXPECT_EQ(transform.transform(source.content), dest.content)
+            << "Source content doesn't map to dest content when projecting " << to_string(source)
+            << " onto " << to_string(dest);
+
+    // We take a strip at the top (according to the orientation) of each
+    // content rect and verify that transform maps between them. This way we
+    // verify that the transform is rotating properly.
+    // In the following example the strip is marked with asterisks:
+    //
+    //      *******                +-------*
+    //      |     |                |       *
+    //      |     |                |       *
+    //      +-----+                +-------*
+    // source(ROTATION_0)      dest (ROTATION_90)
+    const auto sourceStrip = getSideStrip(source.content, source.orientation);
+    const auto destStrip = getSideStrip(dest.content, dest.orientation);
+    ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
+    ASSERT_NE(destStrip, Rect::INVALID_RECT);
+    const auto mappedStrip = transform.transform(sourceStrip);
+    EXPECT_EQ(mappedStrip, destStrip)
+            << to_string(sourceStrip) << " maps to " << to_string(mappedStrip) << " instead of "
+            << to_string(destStrip) << " when projecting " << to_string(source) << " onto "
+            << to_string(dest);
+}
+
+TEST(ProjectionSpaceTest, getTransformWithOrienations) {
+    ProjectionSpace source;
+    source.bounds = Rect(12, 13, 678, 789);
+    source.content = Rect(40, 50, 234, 343);
+    ProjectionSpace dest;
+    dest.bounds = Rect(17, 18, 879, 564);
+    dest.content = Rect(43, 52, 432, 213);
+
+    for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
+        source.orientation = ui::Rotation(sourceRot);
+        for (int destRot = 0; destRot <= 3; destRot++) {
+            dest.orientation = ui::Rotation(destRot);
+            testTransform(source, dest);
+        }
+    }
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b53f88d..7df9b76 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -163,6 +163,10 @@
                                   Rect orientedDisplaySpaceRect) {
     mOrientation = orientation;
 
+    if (isPrimary()) {
+        sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
+    }
+
     const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
     const int displayWidth = displayBounds.width();
     const int displayHeight = displayBounds.height();
@@ -184,52 +188,11 @@
         }
     }
 
-    ui::Transform logicalTranslation, physicalTranslation, scale;
-    const float sourceWidth = layerStackSpaceRect.width();
-    const float sourceHeight = layerStackSpaceRect.height();
-    const float destWidth = orientedDisplaySpaceRect.width();
-    const float destHeight = orientedDisplaySpaceRect.height();
-    if (sourceWidth != destWidth || sourceHeight != destHeight) {
-        const float scaleX = destWidth / sourceWidth;
-        const float scaleY = destHeight / sourceHeight;
-        scale.set(scaleX, 0, 0, scaleY);
-    }
-
-    const float sourceX = layerStackSpaceRect.left;
-    const float sourceY = layerStackSpaceRect.top;
-    const float destX = orientedDisplaySpaceRect.left;
-    const float destY = orientedDisplaySpaceRect.top;
-    logicalTranslation.set(-sourceX, -sourceY);
-    physicalTranslation.set(destX, destY);
-
     // We need to take care of display rotation for globalTransform for case if the panel is not
     // installed aligned with device orientation.
     const auto transformOrientation = orientation + mPhysicalOrientation;
-    const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(transformOrientation);
-    ui::Transform rotation;
-    if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
-        rotation.set(transformOrientationFlags, displayWidth, displayHeight);
-    }
-
-    // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation.
-    // Apply the logical translation, scale to physical size, apply the
-    // physical translation and finally rotate to the physical orientation.
-    ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
-
-    Rect displaySpaceRect = globalTransform.transform(layerStackSpaceRect);
-    if (displaySpaceRect.isEmpty()) {
-        displaySpaceRect = displayBounds;
-    }
-    // Make sure the displaySpaceRect is contained in the display bounds
-    displaySpaceRect.intersect(displayBounds, &displaySpaceRect);
-
-    if (isPrimary()) {
-        sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
-    }
-
-    getCompositionDisplay()->setProjection(globalTransform, transformOrientationFlags,
-                                           orientedDisplaySpaceRect, layerStackSpaceRect,
-                                           displaySpaceRect);
+    getCompositionDisplay()->setProjection(transformOrientation, layerStackSpaceRect,
+                                           orientedDisplaySpaceRect);
 }
 
 ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c95440a..a12f4c7 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -20,6 +20,7 @@
 
 #include "FrameTimeline.h"
 #include <android-base/stringprintf.h>
+#include <utils/Trace.h>
 #include <cinttypes>
 
 namespace android::frametimeline::impl {
@@ -27,6 +28,7 @@
 using base::StringAppendF;
 
 int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+    ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mMutex);
     const int64_t assignedToken = mCurrentToken++;
     mPredictions[assignedToken] = predictions;
@@ -88,18 +90,34 @@
     return mActuals;
 }
 
-void SurfaceFrame::setActuals(frametimeline::TimelineItem&& actuals) {
+nsecs_t SurfaceFrame::getActualQueueTime() {
     std::lock_guard<std::mutex> lock(mMutex);
-    mActuals = actuals;
+    return mActualQueueTime;
 }
 
-void SurfaceFrame::setPresentTime(nsecs_t presentTime) {
+void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals.startTime = actualStartTime;
+}
+
+void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActualQueueTime = actualQueueTime;
+}
+void SurfaceFrame::setActualEndTime(nsecs_t actualEndTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mActuals.endTime = actualEndTime;
+}
+
+void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
     std::lock_guard<std::mutex> lock(mMutex);
     mActuals.presentTime = presentTime;
 }
 
 void SurfaceFrame::dump(std::string& result) {
     std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "Present State : %d\n", static_cast<int>(mPresentState));
+    StringAppendF(&result, "Prediction State : %d\n", static_cast<int>(mPredictionState));
     StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime);
     StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime);
     StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime);
@@ -120,6 +138,7 @@
 
 std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
         const std::string& layerName, std::optional<int64_t> token) {
+    ATRACE_CALL();
     if (!token) {
         return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
                                                     TimelineItem());
@@ -136,6 +155,7 @@
 void FrameTimeline::addSurfaceFrame(
         std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
         SurfaceFrame::PresentState state) {
+    ATRACE_CALL();
     surfaceFrame->setPresentState(state);
     std::unique_ptr<impl::SurfaceFrame> implSurfaceFrame(
             static_cast<impl::SurfaceFrame*>(surfaceFrame.release()));
@@ -144,18 +164,21 @@
 }
 
 void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+    ATRACE_CALL();
     const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
     std::lock_guard<std::mutex> lock(mMutex);
     if (!prediction) {
         mCurrentDisplayFrame->predictionState = PredictionState::Expired;
     } else {
         mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
+        mCurrentDisplayFrame->predictionState = PredictionState::Valid;
     }
     mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
 }
 
 void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
                                  const std::shared_ptr<FenceTime>& presentFence) {
+    ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mMutex);
     mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
     mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
@@ -179,7 +202,7 @@
             for (auto& surfaceFrame : displayFrame->surfaceFrames) {
                 if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
                     // Only presented SurfaceFrames need to be updated
-                    surfaceFrame->setPresentTime(signalTime);
+                    surfaceFrame->setActualPresentTime(signalTime);
                 }
             }
         }
@@ -204,6 +227,8 @@
     StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
     for (const auto& displayFrame : mDisplayFrames) {
         StringAppendF(&result, "---Display Frame---\n");
+        StringAppendF(&result, "Prediction State : %d\n",
+                      static_cast<int>(displayFrame->predictionState));
         StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n",
                       displayFrame->surfaceFlingerPredictions.startTime);
         StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n",
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 291e30e..a42c32c 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -51,7 +51,7 @@
 
     // Generates a token for the given set of predictions. Stores the predictions for 120ms and
     // destroys it later.
-    virtual int64_t generateTokenForPredictions(TimelineItem&& prediction);
+    virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
 };
 
 enum class PredictionState {
@@ -76,15 +76,18 @@
 
     virtual TimelineItem getPredictions() = 0;
     virtual TimelineItem getActuals() = 0;
+    virtual nsecs_t getActualQueueTime() = 0;
     virtual PresentState getPresentState() = 0;
     virtual PredictionState getPredictionState() = 0;
 
     virtual void setPresentState(PresentState state) = 0;
-    virtual void setActuals(TimelineItem&& actuals) = 0;
 
-    // There is no prediction for Queue time and it is not a part of TimelineItem. Set it
-    // separately.
+    // Actual timestamps of the app are set individually at different functions.
+    // Start time (if the app provides) and Queue time are accessible after queueing the frame,
+    // whereas End time is available only during latch.
+    virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
     virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
+    virtual void setActualEndTime(nsecs_t actualEndTime) = 0;
 };
 
 /*
@@ -94,7 +97,7 @@
 class FrameTimeline {
 public:
     virtual ~FrameTimeline() = default;
-    virtual TokenManager& getTokenManager() = 0;
+    virtual TokenManager* getTokenManager() = 0;
 
     // Create a new surface frame, set the predictions based on a token and return it to the caller.
     // Sets the PredictionState of SurfaceFrame.
@@ -115,6 +118,8 @@
     // that vsync.
     virtual void setSfPresent(nsecs_t sfPresentTime,
                               const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+    virtual void dump(std::string& result) = 0;
 };
 
 namespace impl {
@@ -151,14 +156,15 @@
 
     TimelineItem getPredictions() override { return mPredictions; };
     TimelineItem getActuals() override;
+    nsecs_t getActualQueueTime() override;
     PresentState getPresentState() override;
     PredictionState getPredictionState() override;
-    void setActuals(TimelineItem&& actuals) override;
-    void setActualQueueTime(nsecs_t actualQueueTime) override {
-        mActualQueueTime = actualQueueTime;
-    };
+
+    void setActualStartTime(nsecs_t actualStartTime) override;
+    void setActualQueueTime(nsecs_t actualQueueTime) override;
+    void setActualEndTime(nsecs_t actualEndTime) override;
     void setPresentState(PresentState state) override;
-    void setPresentTime(nsecs_t presentTime);
+    void setActualPresentTime(nsecs_t presentTime);
     void dump(std::string& result);
 
 private:
@@ -176,7 +182,7 @@
     FrameTimeline();
     ~FrameTimeline() = default;
 
-    frametimeline::TokenManager& getTokenManager() override { return mTokenManager; }
+    frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
     std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
             const std::string& layerName, std::optional<int64_t> token) override;
     void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
@@ -184,7 +190,7 @@
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
     void setSfPresent(nsecs_t sfPresentTime,
                       const std::shared_ptr<FenceTime>& presentFence) override;
-    void dump(std::string& result);
+    void dump(std::string& result) override;
 
 private:
     // Friend class for testing
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f77a828..5dad4a9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1024,6 +1024,7 @@
     // Can only be accessed with the SF state lock held.
     bool mChildrenChanged{false};
 
+    // Can only be accessed with the SF state lock held.
     std::optional<int64_t> mFrameTimelineVsyncId;
 
     // Window types from WindowManager.LayoutParams
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 77b2f42..3307388 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -40,6 +40,7 @@
 #include <utils/Trace.h>
 
 #include "EventThread.h"
+#include "FrameTimeline.h"
 #include "HwcStrongTypes.h"
 
 using namespace std::chrono_literals;
@@ -166,8 +167,10 @@
 namespace impl {
 
 EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+                         android::frametimeline::TokenManager* tokenManager,
                          InterceptVSyncsCallback interceptVSyncsCallback)
       : mVSyncSource(std::move(vsyncSource)),
+        mTokenManager(tokenManager),
         mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
         mThreadName(mVSyncSource->getName()) {
     mVSyncSource->setCallback(this);
@@ -292,9 +295,16 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     LOG_FATAL_IF(!mVSyncState);
-    // TODO(b/162890590): use TokenManager to populate vsyncId
+    const int64_t vsyncId = [&] {
+        if (mTokenManager != nullptr) {
+            return mTokenManager->generateTokenForPredictions(
+                    {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+        }
+        return static_cast<int64_t>(0);
+    }();
+
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
-                                       expectedVSyncTimestamp, deadlineTimestamp, /*vsyncId=*/0));
+                                       expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
     mCondition.notify_all();
 }
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index fa1ca64..80bd606 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -41,6 +41,10 @@
 class EventThreadTest;
 class SurfaceFlinger;
 
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
 // ---------------------------------------------------------------------------
 
 using ResyncCallback = std::function<void()>;
@@ -137,7 +141,8 @@
 public:
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
 
-    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
+    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*,
+                InterceptVSyncsCallback);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -185,6 +190,7 @@
                       nsecs_t deadlineTimestamp) override;
 
     const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
+    frametimeline::TokenManager* const mTokenManager;
 
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const char* const mThreadName;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 6067e69..641a0a3 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -39,8 +39,9 @@
     }
 }
 
-void MessageQueue::Handler::dispatchInvalidate(nsecs_t expectedVSyncTimestamp) {
+void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
     if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+        mVsyncId = vsyncId;
         mExpectedVSyncTime = expectedVSyncTimestamp;
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
     }
@@ -50,11 +51,11 @@
     switch (message.what) {
         case INVALIDATE:
             android_atomic_and(~eventMaskInvalidate, &mEventMask);
-            mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+            mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
         case REFRESH:
             android_atomic_and(~eventMaskRefresh, &mEventMask);
-            mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
+            mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
             break;
     }
 }
@@ -123,7 +124,8 @@
     while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                mHandler->dispatchInvalidate(buffer[i].vsync.expectedVSyncTimestamp);
+                mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
+                                             buffer[i].vsync.expectedVSyncTimestamp);
                 break;
             }
         }
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 132b416..e263b2f 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -79,13 +79,14 @@
         enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
         MessageQueue& mQueue;
         int32_t mEventMask;
+        std::atomic<int64_t> mVsyncId;
         std::atomic<nsecs_t> mExpectedVSyncTime;
 
     public:
         explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
         virtual void handleMessage(const Message& message);
         void dispatchRefresh();
-        void dispatchInvalidate(nsecs_t expectedVSyncTimestamp);
+        void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
     };
 
     friend class Handler;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5271ccc..9c145cc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -31,6 +31,7 @@
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include <FrameTimeline/FrameTimeline.h>
 #include <algorithm>
 #include <cinttypes>
 #include <cstdint>
@@ -212,11 +213,11 @@
 }
 
 Scheduler::ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, std::chrono::nanoseconds workDuration,
-        std::chrono::nanoseconds readyDuration,
+        const char* connectionName, frametimeline::TokenManager* tokenManager,
+        std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
     auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
-    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
+    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
                                                            std::move(interceptCallback));
     return createConnection(std::move(eventThread));
 }
@@ -332,6 +333,7 @@
 
         auto eventThread =
                 std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                    /*tokenManager=*/nullptr,
                                                     impl::EventThread::InterceptVSyncsCallback());
 
         mInjectorConnectionHandle = createConnection(std::move(eventThread));
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0b5c9d2..da25f5c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -50,6 +50,10 @@
 class VSyncTracker;
 } // namespace scheduler
 
+namespace frametimeline {
+class TokenManager;
+} // namespace frametimeline
+
 struct ISchedulerCallback {
     virtual void setVsyncEnabled(bool) = 0;
     virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
@@ -70,7 +74,7 @@
     ~Scheduler();
 
     using ConnectionHandle = scheduler::ConnectionHandle;
-    ConnectionHandle createConnection(const char* connectionName,
+    ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
                                       std::chrono::nanoseconds workDuration,
                                       std::chrono::nanoseconds readyDuration,
                                       impl::EventThread::InterceptVSyncsCallback);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4b91da8..bca27e3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -331,7 +331,7 @@
         mInterceptor(mFactory.createSurfaceInterceptor(this)),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(std::make_unique<FrameTracer>()),
-        mFrameTimeline(std::make_shared<frametimeline::impl::FrameTimeline>()),
+        mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>()),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -1710,11 +1710,11 @@
                                                           : stats.vsyncTime + stats.vsyncPeriod;
 }
 
-void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
-            onMessageInvalidate(expectedVSyncTime);
+            onMessageInvalidate(vsyncId, expectedVSyncTime);
             break;
         }
         case MessageQueue::REFRESH: {
@@ -1724,7 +1724,7 @@
     }
 }
 
-void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
+void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
 
     const nsecs_t frameStart = systemTime();
@@ -1850,6 +1850,8 @@
     {
         ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
 
+        mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
+
         refreshNeeded = handleMessageTransaction();
         refreshNeeded |= handleMessageInvalidate();
         if (mTracingEnabled) {
@@ -1977,6 +1979,9 @@
     postFrame();
     postComposition();
 
+    mFrameTimeline->setSfPresent(systemTime(),
+                                 std::make_shared<FenceTime>(mPreviousPresentFences[0]));
+
     const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
 
     mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
@@ -2864,12 +2869,12 @@
     mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
     mAppConnectionHandle =
-            mScheduler->createConnection("app",
+            mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/configs.late.appWorkDuration,
                                          /*readyDuration=*/configs.late.sfWorkDuration,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf",
+            mScheduler->createConnection("sf", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/configs.late.sfWorkDuration,
                                          /*readyDuration=*/0ns, [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
@@ -4240,6 +4245,7 @@
                 {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
                 {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
                 {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
+                {"--frametimeline"s, dumper([this](std::string& s) { mFrameTimeline->dump(s); })},
         };
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d6a75c3..3b4d5d4 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -303,7 +303,7 @@
     // called on the main thread by MessageQueue when an internal message
     // is received
     // TODO: this should be made accessible only to MessageQueue
-    void onMessageReceived(int32_t what, nsecs_t expectedVSyncTime);
+    void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime);
 
     renderengine::RenderEngine& getRenderEngine() const;
 
@@ -674,7 +674,7 @@
 
     // Handle the INVALIDATE message queue event, latching new buffers and applying
     // incoming transactions
-    void onMessageInvalidate(nsecs_t expectedVSyncTime);
+    void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -1125,7 +1125,7 @@
 
     const std::shared_ptr<TimeStats> mTimeStats;
     const std::unique_ptr<FrameTracer> mFrameTracer;
-    const std::shared_ptr<frametimeline::FrameTimeline> mFrameTimeline;
+    const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline;
     bool mUseHwcVirtualDisplays = false;
     // If blurs should be enabled on this device.
     bool mSupportsBlur = false;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b42d9de..b750d6b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -1481,7 +1481,7 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
+        EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
@@ -1493,7 +1493,7 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
+        EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
         // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
         // size width and height swapped
@@ -1508,7 +1508,7 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
+        EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
         EXPECT_EQ(false, compositionState.needsFiltering);
@@ -1519,7 +1519,7 @@
         EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
                                 mHardwareDisplaySize.height),
                   compositionState.transform);
-        EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
+        EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
         EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
         // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
         // size width and height swapped
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index ae94f16..f680bdb 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -141,6 +141,7 @@
 
 void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
     mThread = std::make_unique<impl::EventThread>(std::move(source),
+                                                  /*tokenManager=*/nullptr,
                                                   mInterceptVSyncCallRecorder.getInvocable());
 
     // EventThread should register itself as VSyncSource callback.
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 16bea47..98b20e8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -323,7 +323,9 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
+    auto onMessageReceived(int32_t what) {
+        return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime());
+    }
 
     auto renderScreenImplLocked(const RenderArea& renderArea,
                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,