Merge "Remove remove_input_channel_from_windowstate flag" into main
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 583ad01..127676b 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -94,9 +94,8 @@
     static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) {
         auto rpcServer = sp<RpcServer>::make(std::move(ctx));
 
-        // TODO(b/266741352): follow-up to prevent needing this in the future
-        // Trusty needs to be set to the latest stable version that is in prebuilts there.
-        LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0));
+        // By default we use the latest stable version.
+        LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION));
 
         return rpcServer;
     }
diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp
index 451383a..12e347e 100644
--- a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp
+++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp
@@ -27,6 +27,13 @@
 using android::sp;
 using android::wp;
 
+// The default behavior in trusty is to allow handles to be passed with tipc IPC.
+// We add mode NONE so that servers do not reject connections from clients who do
+// not change their default transport mode.
+static const std::vector<RpcSession::FileDescriptorTransportMode> TRUSTY_SERVER_SUPPORTED_FD_MODES =
+        {RpcSession::FileDescriptorTransportMode::TRUSTY,
+         RpcSession::FileDescriptorTransportMode::NONE};
+
 struct ARpcServerTrusty {
     sp<RpcServer> mRpcServer;
 
@@ -53,6 +60,8 @@
         return nullptr;
     }
 
+    rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES);
+
     rpcServer->setPerSessionRootObject(
             [cb, cbArgSp](wp<RpcSession> /*session*/, const void* addrPtr, size_t len) {
                 auto* aib = (*cb)(addrPtr, len, cbArgSp.get());
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 5ab31db..b5fa321 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -281,6 +281,7 @@
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
+        "TransactionState.cpp",
         "VsyncEventData.cpp",
         "view/Surface.cpp",
         "WindowInfosListenerReporter.cpp",
diff --git a/libs/gui/TransactionState.cpp b/libs/gui/TransactionState.cpp
new file mode 100644
index 0000000..9e09bc2
--- /dev/null
+++ b/libs/gui/TransactionState.cpp
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#define LOG_TAG "TransactionState"
+#include <gui/LayerState.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/TransactionState.h>
+#include <private/gui/ParcelUtils.h>
+#include <algorithm>
+
+namespace android {
+
+status_t TransactionState::writeToParcel(Parcel* parcel) const {
+    SAFE_PARCEL(parcel->writeUint64, mId);
+    SAFE_PARCEL(parcel->writeUint32, mFlags);
+    SAFE_PARCEL(parcel->writeInt64, mDesiredPresentTime);
+    SAFE_PARCEL(parcel->writeBool, mIsAutoTimestamp);
+    SAFE_PARCEL(parcel->writeParcelable, mFrameTimelineInfo);
+    SAFE_PARCEL(parcel->writeStrongBinder, mApplyToken);
+    SAFE_PARCEL(parcel->writeBool, mMayContainBuffer);
+    SAFE_PARCEL(parcel->writeBool, mLogCallPoints);
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mDisplayStates.size()));
+    for (auto const& displayState : mDisplayStates) {
+        displayState.write(*parcel);
+    }
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mComposerStates.size()));
+    for (auto const& composerState : mComposerStates) {
+        composerState.write(*parcel);
+    }
+
+    mInputWindowCommands.write(*parcel);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+    for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+        SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+        SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+    }
+
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+    for (auto mergedTransactionId : mMergedTransactionIds) {
+        SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+    }
+
+    SAFE_PARCEL(parcel->writeBool, mHasListenerCallbacks);
+    SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mListenerCallbacks.size()));
+    for (const auto& [listener, callbackIds] : mListenerCallbacks) {
+        SAFE_PARCEL(parcel->writeStrongBinder, listener);
+        SAFE_PARCEL(parcel->writeParcelableVector, callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+status_t TransactionState::readFromParcel(const Parcel* parcel) {
+    SAFE_PARCEL(parcel->readUint64, &mId);
+    SAFE_PARCEL(parcel->readUint32, &mFlags);
+    SAFE_PARCEL(parcel->readInt64, &mDesiredPresentTime);
+    SAFE_PARCEL(parcel->readBool, &mIsAutoTimestamp);
+    SAFE_PARCEL(parcel->readParcelable, &mFrameTimelineInfo);
+    SAFE_PARCEL(parcel->readNullableStrongBinder, &mApplyToken);
+    SAFE_PARCEL(parcel->readBool, &mMayContainBuffer);
+    SAFE_PARCEL(parcel->readBool, &mLogCallPoints);
+
+    uint32_t count;
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mDisplayStates.clear();
+    mDisplayStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        DisplayState displayState;
+        if (displayState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        mDisplayStates.emplace_back(std::move(displayState));
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mComposerStates.clear();
+    mComposerStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        ComposerState composerState;
+        if (composerState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        mComposerStates.emplace_back(std::move(composerState));
+    }
+
+    if (status_t status = mInputWindowCommands.read(*parcel) != NO_ERROR) {
+        return status;
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mUncacheBuffers.clear();
+    mUncacheBuffers.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        client_cache_t client_cache;
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        client_cache.token = tmpBinder;
+        SAFE_PARCEL(parcel->readUint64, &client_cache.id);
+        mUncacheBuffers.emplace_back(std::move(client_cache));
+    }
+
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize())
+    mMergedTransactionIds.clear();
+    mMergedTransactionIds.resize(count);
+    for (size_t i = 0; i < count; i++) {
+        SAFE_PARCEL(parcel->readUint64, &mMergedTransactionIds[i]);
+    }
+
+    SAFE_PARCEL(parcel->readBool, &mHasListenerCallbacks);
+    SAFE_PARCEL_READ_SIZE(parcel->readUint32, &count, parcel->dataSize());
+    mListenerCallbacks.clear();
+    mListenerCallbacks.reserve(count);
+    for (uint32_t i = 0; i < count; i++) {
+        sp<IBinder> tmpBinder;
+        SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+        std::vector<CallbackId> callbackIds;
+        SAFE_PARCEL(parcel->readParcelableVector, &callbackIds);
+        mListenerCallbacks.emplace_back(tmpBinder, callbackIds);
+    }
+
+    return NO_ERROR;
+}
+
+void TransactionState::merge(TransactionState&& other,
+                             const std::function<void(layer_state_t&)>& onBufferOverwrite) {
+    while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+                   MAX_MERGE_HISTORY_LENGTH - 1 &&
+           mMergedTransactionIds.size() > 0) {
+        mMergedTransactionIds.pop_back();
+    }
+    if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.end() - 1);
+    } else if (other.mMergedTransactionIds.size() > 0u) {
+        mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.begin(),
+                                     other.mMergedTransactionIds.end());
+    }
+    mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
+    for (auto const& otherState : other.mComposerStates) {
+        if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+                                   [&otherState](const auto& composerState) {
+                                       return composerState.state.surface ==
+                                               otherState.state.surface;
+                                   });
+            it != mComposerStates.end()) {
+            if (otherState.state.what & layer_state_t::eBufferChanged) {
+                onBufferOverwrite(it->state);
+            }
+            it->state.merge(otherState.state);
+        } else {
+            mComposerStates.push_back(otherState);
+        }
+    }
+
+    for (auto const& state : other.mDisplayStates) {
+        if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+                                   [&state](const auto& displayState) {
+                                       return displayState.token == state.token;
+                                   });
+            it != mDisplayStates.end()) {
+            it->merge(state);
+        } else {
+            mDisplayStates.push_back(state);
+        }
+    }
+
+    for (const auto& cacheId : other.mUncacheBuffers) {
+        mUncacheBuffers.push_back(cacheId);
+    }
+
+    mInputWindowCommands.merge(other.mInputWindowCommands);
+    // TODO(b/385156191) Consider merging desired present time.
+    mFlags |= other.mFlags;
+    mMayContainBuffer |= other.mMayContainBuffer;
+    mLogCallPoints |= other.mLogCallPoints;
+
+    // mApplyToken is explicitly not merged. Token should be set before applying the transactions to
+    // make synchronization decisions a bit simpler.
+    mergeFrameTimelineInfo(other.mFrameTimelineInfo);
+    other.clear();
+}
+
+// copied from FrameTimelineInfo::merge()
+void TransactionState::mergeFrameTimelineInfo(const FrameTimelineInfo& other) {
+    // When merging vsync Ids we take the oldest valid one
+    if (mFrameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
+        other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+        if (other.vsyncId > mFrameTimelineInfo.vsyncId) {
+            mFrameTimelineInfo = other;
+        }
+    } else if (mFrameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+        mFrameTimelineInfo = other;
+    }
+}
+
+void TransactionState::clear() {
+    mComposerStates.clear();
+    mDisplayStates.clear();
+    mListenerCallbacks.clear();
+    mHasListenerCallbacks = false;
+    mInputWindowCommands.clear();
+    mUncacheBuffers.clear();
+    mDesiredPresentTime = 0;
+    mIsAutoTimestamp = true;
+    mApplyToken = nullptr;
+    mFrameTimelineInfo = {};
+    mMergedTransactionIds.clear();
+    mFlags = 0;
+    mMayContainBuffer = false;
+    mLogCallPoints = false;
+}
+
+layer_state_t* TransactionState::getLayerState(const sp<SurfaceControl>& sc) {
+    auto handle = sc->getLayerStateHandle();
+    if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+                               [&handle](const auto& composerState) {
+                                   return composerState.state.surface == handle;
+                               });
+        it != mComposerStates.end()) {
+        return &it->state;
+    }
+
+    // we don't have it, add an initialized layer_state to our list
+    ComposerState s;
+    s.state.surface = handle;
+    s.state.layerId = sc->getLayerId();
+    mComposerStates.push_back(s);
+
+    return &mComposerStates.back().state;
+}
+
+DisplayState& TransactionState::getDisplayState(const sp<IBinder>& token) {
+    if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+                               [token](const auto& display) { return display.token == token; });
+        it != mDisplayStates.end()) {
+        return *it;
+    }
+
+    // If display state doesn't exist, add a new one.
+    DisplayState s;
+    s.token = token;
+    mDisplayStates.push_back(s);
+    return mDisplayStates.back();
+}
+
+}; // namespace android
diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h
new file mode 100644
index 0000000..4358227
--- /dev/null
+++ b/libs/gui/include/gui/TransactionState.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+#include <android/gui/FrameTimelineInfo.h>
+#include <binder/Parcelable.h>
+#include <gui/LayerState.h>
+
+namespace android {
+
+// Class to store all the transaction data and the parcelling logic
+class TransactionState {
+public:
+    explicit TransactionState() = default;
+    TransactionState(TransactionState const& other) = default;
+    status_t writeToParcel(Parcel* parcel) const;
+    status_t readFromParcel(const Parcel* parcel);
+    layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+    DisplayState& getDisplayState(const sp<IBinder>& token);
+
+    // Returns the current id of the transaction.
+    // The id is updated every time the transaction is applied.
+    uint64_t getId() const { return mId; }
+    std::vector<uint64_t> getMergedTransactionIds() const { return mMergedTransactionIds; }
+    void enableDebugLogCallPoints() { mLogCallPoints = true; }
+    void merge(TransactionState&& other,
+               const std::function<void(layer_state_t&)>& onBufferOverwrite);
+
+    // copied from FrameTimelineInfo::merge()
+    void mergeFrameTimelineInfo(const FrameTimelineInfo& other);
+    void clear();
+    bool operator==(const TransactionState& rhs) const = default;
+    bool operator!=(const TransactionState& rhs) const = default;
+
+    uint64_t mId = 0;
+    std::vector<uint64_t> mMergedTransactionIds;
+    uint32_t mFlags = 0;
+    // The vsync id provided by Choreographer.getVsyncId and the input event id
+    gui::FrameTimelineInfo mFrameTimelineInfo;
+    // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+    // to be presented. When it is not possible to present at exactly that time, it will be
+    // presented after the time has passed.
+    //
+    // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+    // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+    //
+    // Desired present times that are more than 1 second in the future may be ignored.
+    // When a desired present time has already passed, the transaction will be presented as soon
+    // as possible.
+    //
+    // Transactions from the same process are presented in the same order that they are applied.
+    // The desired present time does not affect this ordering.
+    int64_t mDesiredPresentTime = 0;
+    bool mIsAutoTimestamp = true;
+    // If not null, transactions will be queued up using this token otherwise a common token
+    // per process will be used.
+    sp<IBinder> mApplyToken;
+    // Indicates that the Transaction may contain buffers that should be cached. The reason this
+    // is only a guess is that buffers can be removed before cache is called. This is only a
+    // hint that at some point a buffer was added to this transaction before apply was called.
+    bool mMayContainBuffer = false;
+    // Prints debug logs when enabled.
+    bool mLogCallPoints = false;
+
+    std::vector<DisplayState> mDisplayStates;
+    std::vector<ComposerState> mComposerStates;
+    InputWindowCommands mInputWindowCommands;
+    std::vector<client_cache_t> mUncacheBuffers;
+    // Note: mHasListenerCallbacks can be true even if mListenerCallbacks is
+    // empty.
+    bool mHasListenerCallbacks = false;
+    std::vector<ListenerCallbacks> mListenerCallbacks;
+
+private:
+    // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+    // Ordered most recently merged to least recently merged.
+    static constexpr size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+};
+
+}; // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 87051a7..e20345d 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -90,6 +90,7 @@
         "testserver/TestServerClient.cpp",
         "testserver/TestServerHost.cpp",
         "TextureRenderer.cpp",
+        "TransactionState_test.cpp",
         "VsyncEventData_test.cpp",
         "WindowInfo_test.cpp",
     ],
diff --git a/libs/gui/tests/TransactionState_test.cpp b/libs/gui/tests/TransactionState_test.cpp
new file mode 100644
index 0000000..179b264
--- /dev/null
+++ b/libs/gui/tests/TransactionState_test.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2025 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 <gmock/gmock.h>
+
+#include <gtest/gtest.h>
+#include <unordered_map>
+#include "android/gui/FocusRequest.h"
+#include "binder/Binder.h"
+#include "binder/Parcel.h"
+#include "gtest/gtest.h"
+#include "gui/LayerState.h"
+#include "gui/WindowInfo.h"
+
+#include "gui/TransactionState.h"
+
+namespace android {
+
+void sprintf(std::string& out, const char* format, ...) {
+    va_list arg_list;
+    va_start(arg_list, format);
+
+    int len = vsnprintf(nullptr, 0, format, arg_list);
+    if (len < 0) {
+        va_end(arg_list);
+    }
+    std::string line(len, '\0');
+    int written = vsnprintf(line.data(), len + 1, format, arg_list);
+    if (written != len) {
+        va_end(arg_list);
+    }
+    line.pop_back();
+    out += line;
+    va_end(arg_list);
+}
+
+constexpr std::string dump_struct(auto& x) {
+    std::string s;
+#if __has_builtin(__builtin_dump_struct)
+    __builtin_dump_struct(&x, sprintf, s);
+#else
+    (void)x;
+#endif
+    return s;
+}
+
+void PrintTo(const TransactionState& state, ::std::ostream* os) {
+    *os << dump_struct(state);
+    *os << state.mFrameTimelineInfo.toString();
+    for (auto mergedId : state.mMergedTransactionIds) {
+        *os << mergedId << ",";
+    }
+}
+
+void PrintTo(const ComposerState& state, ::std::ostream* os) {
+    *os << dump_struct(state.state);
+    *os << state.state.getWindowInfo();
+}
+
+// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which
+// field did not compare ==.
+void Compare(const TransactionState& s1, const TransactionState& s2) {
+    EXPECT_EQ(s1.mId, s2.mId);
+    EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds);
+    EXPECT_EQ(s1.mFlags, s2.mFlags);
+    EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo);
+    EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime);
+    EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp);
+    EXPECT_EQ(s1.mApplyToken, s2.mApplyToken);
+    EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer);
+    EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints);
+    EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size());
+    EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates));
+    EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size());
+    EXPECT_EQ(s1.mComposerStates, s2.mComposerStates);
+    EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands);
+    EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers);
+    EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks);
+    EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size());
+    EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks);
+}
+
+std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) {
+    auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>();
+    for (size_t i = 0; i < maxSize; ++i) {
+        result->emplace(i, sp<BBinder>::make());
+    }
+    return result;
+}
+
+constexpr size_t kMaxComposerStates = 2;
+ComposerState createComposerStateForTest(size_t i) {
+    static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release();
+
+    ComposerState state;
+    state.state.what = layer_state_t::eFlagsChanged;
+    state.state.surface = sLayerHandle->at(i);
+    state.state.layerId = i;
+    state.state.flags = 20 * i;
+    return state;
+}
+
+constexpr size_t kMaxDisplayStates = 5;
+DisplayState createDisplayStateForTest(size_t i) {
+    static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release();
+
+    DisplayState displayState;
+    displayState.what = DisplayState::eFlagsChanged;
+    displayState.token = sDisplayTokens->at(i);
+    displayState.flags = 20 * i;
+    return displayState;
+}
+
+TransactionState createTransactionStateForTest() {
+    static sp<BBinder> sApplyToken = sp<BBinder>::make();
+
+    TransactionState state;
+    state.mId = 123;
+    state.mMergedTransactionIds.push_back(15);
+    state.mMergedTransactionIds.push_back(0);
+    state.mFrameTimelineInfo.vsyncId = 14;
+    state.mDesiredPresentTime = 11;
+    state.mIsAutoTimestamp = true;
+    state.mApplyToken = sApplyToken;
+    for (size_t i = 0; i < kMaxDisplayStates; i++) {
+        state.mDisplayStates.push_back(createDisplayStateForTest(i));
+    }
+    for (size_t i = 0; i < kMaxComposerStates; i++) {
+        state.mComposerStates.push_back(createComposerStateForTest(i));
+    }
+    static const auto* const sFocusRequestTokens = createTokenMap(5).release();
+    for (int i = 0; i < 5; i++) {
+        gui::FocusRequest request;
+        request.token = sFocusRequestTokens->at(i);
+        request.timestamp = i;
+        state.mInputWindowCommands.addFocusRequest(request);
+    }
+    static const auto* const sCacheToken = createTokenMap(5).release();
+    for (int i = 0; i < 5; i++) {
+        client_cache_t cache;
+        cache.token = sCacheToken->at(i);
+        cache.id = i;
+        state.mUncacheBuffers.emplace_back(std::move(cache));
+    }
+    static const auto* const sListenerCallbacks = []() {
+        auto* callbacks = new std::vector<ListenerCallbacks>();
+        for (int i = 0; i < 5; i++) {
+            callbacks->emplace_back(sp<BBinder>::make(),
+                                    std::unordered_set<CallbackId, CallbackIdHash>{});
+        }
+        return callbacks;
+    }();
+    state.mHasListenerCallbacks = true;
+    state.mListenerCallbacks = *sListenerCallbacks;
+    return state;
+}
+
+TransactionState createEmptyTransaction(uint64_t id) {
+    TransactionState state;
+    state.mId = id;
+    return state;
+}
+
+TEST(TransactionStateTest, parcel) {
+    TransactionState state = createTransactionStateForTest();
+    Parcel p;
+    state.writeToParcel(&p);
+    p.setDataPosition(0);
+    TransactionState parcelledState;
+    parcelledState.readFromParcel(&p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelDisplayState) {
+    DisplayState state = createDisplayStateForTest(0);
+    Parcel p;
+    state.write(p);
+    p.setDataPosition(0);
+    DisplayState parcelledState;
+    parcelledState.read(p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelLayerState) {
+    ComposerState state = createComposerStateForTest(0);
+    Parcel p;
+    state.write(p);
+    p.setDataPosition(0);
+    ComposerState parcelledState;
+    parcelledState.read(p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, parcelEmptyState) {
+    TransactionState state;
+    Parcel p;
+    state.writeToParcel(&p);
+    p.setDataPosition(0);
+    TransactionState parcelledState;
+    state.readFromParcel(&p);
+    EXPECT_EQ(state, parcelledState);
+};
+
+TEST(TransactionStateTest, mergeLayerState) {
+    ComposerState composerState = createComposerStateForTest(0);
+    ComposerState update;
+    update.state.surface = composerState.state.surface;
+    update.state.layerId = 0;
+    update.state.what = layer_state_t::eAlphaChanged;
+    update.state.color.a = .42;
+    composerState.state.merge(update.state);
+
+    ComposerState expectedMergedState = createComposerStateForTest(0);
+    expectedMergedState.state.what |= layer_state_t::eAlphaChanged;
+    expectedMergedState.state.color.a = .42;
+    EXPECT_EQ(composerState, expectedMergedState);
+};
+
+TEST(TransactionStateTest, merge) {
+    // Setup.
+    static constexpr uint64_t kUpdateTransactionId = 200;
+
+    TransactionState state = createTransactionStateForTest();
+
+    TransactionState update;
+    update.mId = kUpdateTransactionId;
+    {
+        ComposerState composerState;
+        composerState.state.surface = state.mComposerStates[0].state.surface;
+        composerState.state.what = layer_state_t::eAlphaChanged;
+        composerState.state.color.a = .42;
+        update.mComposerStates.push_back(composerState);
+    }
+    {
+        ComposerState composerState;
+        composerState.state.surface = state.mComposerStates[1].state.surface;
+        composerState.state.what = layer_state_t::eBufferChanged;
+        update.mComposerStates.push_back(composerState);
+    }
+    int32_t overrwiteLayerId = -1;
+    // Mutation.
+    state.merge(std::move(update),
+                [&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; });
+    // Assertions.
+    EXPECT_EQ(1, overrwiteLayerId);
+    EXPECT_EQ(update, createEmptyTransaction(update.getId()));
+
+    TransactionState expectedMergedState = createTransactionStateForTest();
+    expectedMergedState.mMergedTransactionIds
+            .insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId);
+    expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged;
+    expectedMergedState.mComposerStates.at(0).state.color.a = .42;
+    expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged;
+    auto inputCommands = expectedMergedState.mInputWindowCommands;
+
+    // desired present time is not merged.
+    expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime;
+
+    EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]);
+    EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands);
+    EXPECT_EQ(state, expectedMergedState);
+};
+
+TEST(TransactionStateTest, clear) {
+    TransactionState state = createTransactionStateForTest();
+    state.clear();
+    TransactionState emptyState = createEmptyTransaction(state.getId());
+    EXPECT_EQ(state, emptyState);
+};
+
+} // namespace android
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index e59eeb2..1c0c9e7 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -54,13 +54,6 @@
 }
 
 flag {
-  name: "enable_touchpad_typing_palm_rejection"
-  namespace: "input"
-  description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard"
-  bug: "301055381"
-}
-
-flag {
   name: "enable_v2_touchpad_typing_palm_rejection"
   namespace: "input"
   description: "In addition to touchpad palm rejection v1, v2 will also cancel ongoing move gestures while typing and add delay in re-enabling the tap to click."
@@ -120,13 +113,6 @@
 }
 
 flag {
-  name: "hide_pointer_indicators_for_secure_windows"
-  namespace: "input"
-  description: "Hide touch and pointer indicators if a secure window is present on display"
-  bug: "325252005"
-}
-
-flag {
   name: "enable_keyboard_classifier"
   namespace: "input"
   description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index b830072..f8ab830 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -535,9 +535,6 @@
 }
 
 void PointerChoreographer::onControllerAddedOrRemovedLocked() {
-    if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
-        return;
-    }
     bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
             !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
 
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 480e276..3255877 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -39,9 +39,6 @@
 
 namespace {
 
-// This will disable the tap to click while the user is typing on a physical keyboard
-const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
-
 // In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in
 // re-enabling the tap to click.
 const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 =
@@ -226,8 +223,7 @@
     if (!mIsHoverCancelled) {
         // handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click
         // for this case as subsequent handleButtonsChange may choose to ignore this tap.
-        if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
-            (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
+        if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
             enableTapToClick(when);
         }
     }
@@ -278,7 +274,7 @@
             // return early to prevent this tap
             return out;
         }
-    } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
+    } else if (mReaderContext.isPreventingTouchpadTaps()) {
         enableTapToClick(when);
         if (gesture.details.buttons.is_tap) {
             // return early to prevent this tap
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 914f5ab..35310a5 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -42,8 +42,6 @@
 
 namespace {
 
-const auto TOUCHPAD_PALM_REJECTION =
-        ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection);
 const auto TOUCHPAD_PALM_REJECTION_V2 =
         ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection);
 
@@ -1461,7 +1459,6 @@
 }
 
 TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
-                  REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
                   REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
     nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
 
@@ -1574,8 +1571,7 @@
     ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f))));
 }
 
-TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled,
-                  REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
     // Click should still produce button press/release events
     mReader->getContext()->setPreventingTouchpadTaps(true);
 
@@ -1644,8 +1640,7 @@
     ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
 }
 
-TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
-                  REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
     // initially disable tap-to-click
     mReader->getContext()->setPreventingTouchpadTaps(true);
 
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index dbd33c7..2b469c5 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -129,9 +129,6 @@
                                             mInjectedInitialWindowInfos};
 
     void SetUp() override {
-        // flag overrides
-        input_flags::hide_pointer_indicators_for_secure_windows(true);
-
         ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) {
             std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
             EXPECT_FALSE(pc->isPointerShown());
@@ -2109,10 +2106,7 @@
     pc->assertSkipScreenshotFlagNotChanged();
 }
 
-TEST_F_WITH_FLAGS(
-        PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
-                                            hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows) {
     mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
 
     // Add a first mouse device
@@ -3169,11 +3163,8 @@
 
 class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
 
-TEST_F_WITH_FLAGS(
-        PointerChoreographerWindowInfoListenerTest,
-        doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed,
-        REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
-                                            hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerWindowInfoListenerTest,
+       doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed) {
     sp<android::gui::WindowInfosListener> registeredListener;
     sp<android::gui::WindowInfosListener> localListenerCopy;
     {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index dea3290..efddc85 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -104,7 +104,7 @@
     void detectDisallowedCompositionTypeChange(
             aidl::android::hardware::graphics::composer3::Composition from,
             aidl::android::hardware::graphics::composer3::Composition to) const;
-    bool isClientCompositionForced(bool isPeekingThrough, bool isCached) const;
+    bool isClientCompositionForced(bool isPeekingThrough) const;
     void updateLuts(const LayerFECompositionState&,
                     const std::optional<std::vector<std::optional<LutProperties>>>& properties);
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b1bbcac..ea36011 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -876,8 +876,7 @@
                                             bool isPeekingThrough, bool skipLayer) {
     auto& outputDependentState = editState();
 
-    bool isCached = !skipLayer && outputDependentState.overrideInfo.buffer;
-    if (isClientCompositionForced(isPeekingThrough, isCached)) {
+    if (isClientCompositionForced(isPeekingThrough)) {
         // If we are forcing client composition, we need to tell the HWC
         requestedCompositionType = Composition::CLIENT;
     }
@@ -967,12 +966,9 @@
     }
 }
 
-bool OutputLayer::isClientCompositionForced(bool isPeekingThrough, bool isCached) const {
-    // If this layer was flattened into a CachedSet then it is not necessary for
-    // the GPU to compose it.
-    bool requiresClientDrawnRoundedCorners = !isCached && getLayerFE().hasRoundedCorners();
+bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const {
     return getState().forceClientComposition ||
-            (!isPeekingThrough && requiresClientDrawnRoundedCorners);
+            (!isPeekingThrough && getLayerFE().hasRoundedCorners());
 }
 
 void OutputLayer::applyDeviceCompositionTypeChange(Composition compositionType) {
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 07770f1..b396544 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -180,8 +180,7 @@
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
-    // TODO(b/241285876): Skip this for virtual displays.
-    if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+    if (!isVirtual() && (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON)) {
         if (mStagedBrightness && mBrightness != mStagedBrightness) {
             getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
             mBrightness = *mStagedBrightness;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index be37429..02522a3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -299,6 +299,8 @@
     Fps requestedRefreshRate;
     int32_t maxLayerPictureProfiles = 0;
     bool hasPictureProcessing = false;
+    hardware::graphics::composer::hal::PowerMode initialPowerMode{
+            hardware::graphics::composer::hal::PowerMode::OFF};
 
 private:
     static std::atomic<int32_t> sNextSequenceId;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index a80bdb4..964a970 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -540,7 +540,7 @@
         case Composition::INVALID:
             return 'i';
         case Composition::SOLID_COLOR:
-            return 'e';
+            return 'c';
         case Composition::CURSOR:
             return 'u';
         case Composition::SIDEBAND:
@@ -548,7 +548,7 @@
         case Composition::DISPLAY_DECORATION:
             return 'a';
         case Composition::REFRESH_RATE_INDICATOR:
-            return 'f';
+            return 'r';
         case Composition::CLIENT:
         case Composition::DEVICE:
             break;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9d759df..9cd2314 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -591,6 +591,8 @@
         return nullptr;
     }
 
+    ALOGD("Creating virtual display: %s", displayName.c_str());
+
     class DisplayToken : public BBinder {
         sp<SurfaceFlinger> flinger;
         virtual ~DisplayToken() {
@@ -615,6 +617,8 @@
     // TODO (b/314820005): separate as a different arg when creating the display.
     state.isProtected = isSecure;
     state.optimizationPolicy = optimizationPolicy;
+    // Virtual displays start in ON mode.
+    state.initialPowerMode = hal::PowerMode::ON;
     state.displayName = displayName;
     state.uniqueId = uniqueId;
     state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
@@ -636,6 +640,9 @@
         ALOGE("%s: Invalid operation on physical display", __func__);
         return INVALID_OPERATION;
     }
+
+    ALOGD("Destroying virtual display: %s", state.displayName.c_str());
+
     mCurrentState.displays.removeItemsAt(index);
     setTransactionFlags(eDisplayTransactionNeeded);
     return NO_ERROR;
@@ -3913,7 +3920,12 @@
             getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
     ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
-    creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+    if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+        creationArgs.initialPowerMode = state.initialPowerMode;
+    } else {
+        creationArgs.initialPowerMode =
+                state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+    }
 
     creationArgs.requestedRefreshRate = state.requestedRefreshRate;
 
@@ -3987,6 +3999,8 @@
         // Virtual displays without a surface are dormant:
         // they have external state (layer stack, projection,
         // etc.) but no internal state (i.e. a DisplayDevice).
+        ALOGD("Not adding dormant virtual display with token %p: %s", displayToken.unsafe_get(),
+              state.displayName.c_str());
         return;
     }
 
@@ -4150,7 +4164,7 @@
         if (currentState.physical) {
             const auto display = getDisplayDeviceLocked(displayToken);
             if (!mSkipPowerOnForQuiescent) {
-                setPowerModeInternal(display, hal::PowerMode::ON);
+                setPhysicalDisplayPowerMode(display, hal::PowerMode::ON);
             }
 
             if (display->getPhysicalId() == mActiveDisplayId) {
@@ -5641,7 +5655,7 @@
 
         // In case of a restart, ensure all displays are off.
         for (const auto& [id, display] : mPhysicalDisplays) {
-            setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
+            setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
         }
 
         // Power on all displays. The primary display is first, so becomes the active display. Also,
@@ -5650,13 +5664,14 @@
         // Additionally, do not turn on displays if the boot should be quiescent.
         if (!mSkipPowerOnForQuiescent) {
             for (const auto& [id, display] : mPhysicalDisplays) {
-                setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON);
+                setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::ON);
             }
         }
     }
 }
 
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
+void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display,
+                                                 hal::PowerMode mode) {
     if (display->isVirtual()) {
         // TODO(b/241285876): This code path should not be reachable, so enforce this at compile
         // time.
@@ -5806,17 +5821,33 @@
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
-                                               kMainThreadContext) {
+    auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
         mSkipPowerOnForQuiescent = false;
-        const auto display = getDisplayDeviceLocked(displayToken);
+        const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
         if (!display) {
-            ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
-                  displayToken.get());
+            Mutex::Autolock lock(mStateLock);
+            const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+            if (index >= 0) {
+                auto& state = mCurrentState.displays.editValueFor(displayToken);
+                if (state.isVirtual()) {
+                    ALOGD("Setting power mode %d for a dormant virtual display with token %p", mode,
+                          displayToken.get());
+                    state.initialPowerMode = static_cast<hal::PowerMode>(mode);
+                    return;
+                }
+            }
+            ALOGE("Failed to set power mode %d for display token %p", mode, displayToken.get());
         } else if (display->isVirtual()) {
-            ALOGW("Attempt to set power mode %d for virtual display", mode);
+            if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+                ALOGD("Setting power mode %d on virtual display %s", mode,
+                      display->getDisplayName().c_str());
+                display->setPowerMode(static_cast<hal::PowerMode>(mode));
+            } else {
+                ALOGW("Attempt to set power mode %d for virtual display", mode);
+            }
         } else {
-            setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
+            ftl::FakeGuard guard(mStateLock);
+            setPhysicalDisplayPowerMode(display, static_cast<hal::PowerMode>(mode));
         }
     });
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 39e237b..fc596d7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -733,7 +733,7 @@
     void applyActiveMode(PhysicalDisplayId) REQUIRES(kMainThreadContext);
 
     // Called on the main thread in response to setPowerMode()
-    void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
+    void setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock, kMainThreadContext);
 
     // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 8786f6e..13f6577 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -16,9 +16,9 @@
     ],
     shared_libs: [
         "libSurfaceFlingerProp",
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
         "libtracing_perfetto",
+        "server_configurable_flags",
     ],
     static_libs: [
         "librenderengine_includes",
@@ -37,11 +37,12 @@
         "libsurfaceflinger_common_defaults",
     ],
     static_libs: [
-        "libsurfaceflingerflags",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflingerflags",
     ],
 }
 
@@ -51,44 +52,47 @@
         "libsurfaceflinger_common_defaults",
     ],
     static_libs: [
-        "libsurfaceflingerflags_test",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflingerflags_test",
     ],
 }
 
 cc_defaults {
     name: "libsurfaceflinger_common_deps",
     shared_libs: [
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
         "libtracing_perfetto",
+        "server_configurable_flags",
     ],
     static_libs: [
-        "libsurfaceflinger_common",
-        "libsurfaceflingerflags",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflinger_common",
+        "libsurfaceflingerflags",
     ],
 }
 
 cc_defaults {
     name: "libsurfaceflinger_common_test_deps",
     shared_libs: [
-        "server_configurable_flags",
         "libaconfig_storage_read_api_cc",
         "libtracing_perfetto",
+        "server_configurable_flags",
     ],
     static_libs: [
-        "libsurfaceflinger_common_test",
-        "libsurfaceflingerflags_test",
         "aconfig_hardware_flags_c_lib",
+        "android.companion.virtualdevice.flags-aconfig-cc",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
+        "libsurfaceflinger_common_test",
+        "libsurfaceflingerflags_test",
     ],
 }
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 8d1b51b..42fa436 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -26,8 +26,9 @@
 #include <server_configurable_flags/get_flags.h>
 #include <cinttypes>
 
-#include <android_os.h>
+#include <android_companion_virtualdevice_flags.h>
 #include <android_hardware_flags.h>
+#include <android_os.h>
 #include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <com_android_server_display_feature_flags.h>
@@ -125,6 +126,7 @@
     DUMP_ACONFIG_FLAG(adpf_gpu_sf);
     DUMP_ACONFIG_FLAG(adpf_native_session_manager);
     DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
+    DUMP_ACONFIG_FLAG(correct_virtual_display_power_state);
     DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
     DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold);
     DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
@@ -307,6 +309,8 @@
 
 /// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(correct_virtual_display_power_state, "",
+                                   android::companion::virtualdevice::flags)
 
 /// Trunk stable readonly flags from outside SurfaceFlinger ///
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 603139e..dd7042d 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -60,6 +60,7 @@
     bool adpf_native_session_manager() const;
     bool adpf_use_fmq_channel() const;
     bool adpf_use_fmq_channel_fixed() const;
+    bool correct_virtual_display_power_state() const;
     bool graphite_renderengine_preview_rollout() const;
     bool increase_missed_frame_jank_threshold() const;
     bool refresh_rate_overlay_on_external_display() const;
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index cbcfe03..49c35e2 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -143,7 +143,7 @@
                                                                       kDisplay1Mode60->getId()));
 
     // TODO(b/241285191): Restore once VsyncSchedule::getPendingHardwareVsyncState is called by
-    // Scheduler::setDisplayPowerMode rather than SF::setPowerModeInternal.
+    // Scheduler::setDisplayPowerMode rather than SF::setPhysicalDisplayPowerMode.
 #if 0
     // Hardware VSYNC should be disabled for newly registered displays.
     EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId2, false)).Times(1);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 525a940..3f710fd 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -415,8 +415,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
 
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -446,8 +446,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
@@ -479,8 +479,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -522,7 +522,7 @@
     EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
 
     // Power off the display before the mode has been set.
-    mFlinger.setPowerModeInternal(mDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mDisplay, hal::PowerMode::OFF);
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -547,8 +547,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -565,7 +565,7 @@
     EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
 
     // Power off the outer display before the mode has been set.
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF);
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -582,8 +582,8 @@
     EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
     EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
 
-    mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
-    mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
 
     EXPECT_EQ(NO_ERROR,
               mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index 19f8deb..fde2749 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -38,30 +38,30 @@
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 
     // ...and should still be after powering on.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
 TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) {
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
 
     // The outer display should become the pacesetter after folding.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
 
     // The inner display should become the pacesetter after unfolding.
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
 TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) {
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
 
     // The inner display should stay the pacesetter if both are powered on.
     // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 
     // The outer display should become the pacesetter if designated.
@@ -74,20 +74,20 @@
 }
 
 TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) {
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
     // The outer display should become the pacesetter if the inner display powers off.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
 
     // The outer display should stay the pacesetter if both are powered on.
     // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
 
     // The inner display should become the pacesetter if the outer display powers off.
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF);
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
 }
 
@@ -114,8 +114,8 @@
             .Times(0);
 
     // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
-    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+    // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
 
     EXPECT_TRUE(mInnerDisplay->isPoweredOn());
     EXPECT_FALSE(mOuterDisplay->isPoweredOn());
@@ -133,10 +133,10 @@
             .Times(1);
 
     // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
-    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
     EXPECT_FALSE(mInnerDisplay->isPoweredOn());
     EXPECT_TRUE(mOuterDisplay->isPoweredOn());
@@ -154,9 +154,9 @@
             .Times(1);
 
     // The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
-    // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
     EXPECT_TRUE(mInnerDisplay->isPoweredOn());
     EXPECT_TRUE(mOuterDisplay->isPoweredOn());
@@ -173,8 +173,8 @@
     EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
             .Times(1);
 
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 }
 
 TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) {
@@ -192,10 +192,10 @@
     EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, false))
             .Times(1);
 
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
-    mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
 
-    mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
 
     // Other display is now the pacesetter.
     ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
similarity index 88%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
index fed7b2e..f2fbbdd 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
@@ -315,7 +315,7 @@
                          EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
                          TransitionVariant>;
 
-class SetPowerModeInternalTest : public DisplayTransactionTest {
+class SetPhysicalDisplayPowerModeTest : public DisplayTransactionTest {
 public:
     template <typename Case>
     void transitionDisplayCommon();
@@ -331,7 +331,7 @@
 struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
 
 template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
+void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() {
     // --------------------------------------------------------------------
     // Preconditions
 
@@ -353,7 +353,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE);
+    mFlinger.setPhysicalDisplayPowerMode(display, Case::Transition::TARGET_POWER_MODE);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -361,7 +361,7 @@
     Case::Transition::verifyPostconditions(this);
 }
 
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfNoChange) {
     using Case = SimplePrimaryDisplayCase;
 
     // --------------------------------------------------------------------
@@ -378,7 +378,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+    mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::ON);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -386,7 +386,7 @@
     EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfVirtualDisplay) {
     using Case = HwcVirtualDisplayCase;
 
     // --------------------------------------------------------------------
@@ -408,7 +408,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+    mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::OFF);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -416,88 +416,88 @@
     EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
     transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnExternalDisplay) {
     SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffExternalDisplay) {
     SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
     SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
     SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
     SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
 }
 
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index bb377ba..a3ee08f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -462,9 +462,9 @@
     }
 
     // Allow reading display state without locking, as if called on the SF main thread.
-    auto setPowerModeInternal(const sp<DisplayDevice>& display,
-                              hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
-        return mFlinger->setPowerModeInternal(display, mode);
+    auto setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display,
+                                     hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->setPhysicalDisplayPowerMode(display, mode);
     }
 
     auto renderScreenImpl(const sp<DisplayDevice> display, const Rect sourceCrop,