summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/input/InputTransport.h2
-rw-r--r--libs/binder/Parcel.cpp2
-rw-r--r--libs/binder/tests/binderParcelUnitTest.cpp11
-rw-r--r--libs/dumputils/dump_utils.cpp1
-rw-r--r--libs/gui/BLASTBufferQueue.cpp6
-rw-r--r--libs/gui/ISurfaceComposer.cpp121
-rw-r--r--libs/gui/Surface.cpp34
-rw-r--r--libs/gui/SurfaceComposerClient.cpp335
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h2
-rw-r--r--libs/gui/include/gui/BufferQueueProducer.h5
-rw-r--r--libs/gui/include/gui/Choreographer.h6
-rw-r--r--libs/gui/include/gui/DisplayEventDispatcher.h14
-rw-r--r--libs/gui/include/gui/ISurfaceComposer.h9
-rw-r--r--libs/gui/include/gui/Surface.h3
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h66
-rw-r--r--libs/gui/include/gui/TransactionState.h6
-rw-r--r--libs/gui/include/gui/WindowInfosListenerReporter.h3
-rw-r--r--libs/gui/tests/Surface_test.cpp64
-rw-r--r--libs/input/InputConsumerNoResampling.cpp5
-rw-r--r--libs/input/InputTransport.cpp28
-rw-r--r--libs/input/input_flags.aconfig17
-rw-r--r--libs/input/tests/InputChannel_test.cpp148
-rw-r--r--libs/ui/include/ui/DisplayIdentification.h17
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp49
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h5
-rw-r--r--services/inputflinger/dispatcher/TouchState.cpp4
-rw-r--r--services/inputflinger/dispatcher/TouchState.h2
-rw-r--r--services/inputflinger/dispatcher/TouchedWindow.cpp4
-rw-r--r--services/inputflinger/dispatcher/TouchedWindow.h7
-rw-r--r--services/inputflinger/dispatcher/include/InputDispatcherInterface.h2
-rw-r--r--services/inputflinger/include/InputReaderBase.h3
-rw-r--r--services/inputflinger/reader/EventHub.cpp220
-rw-r--r--services/inputflinger/reader/InputDevice.cpp6
-rw-r--r--services/inputflinger/reader/InputReader.cpp11
-rw-r--r--services/inputflinger/reader/include/EventHub.h13
-rw-r--r--services/inputflinger/reader/include/InputDevice.h6
-rw-r--r--services/inputflinger/reader/include/InputReader.h2
-rw-r--r--services/inputflinger/tests/FakeEventHub.cpp8
-rw-r--r--services/inputflinger/tests/FakeEventHub.h1
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp170
-rw-r--r--services/inputflinger/tests/InputReader_test.cpp16
-rw-r--r--services/inputflinger/tests/InterfaceMocks.h1
-rw-r--r--services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp5
-rw-r--r--services/inputflinger/tests/fuzzers/MapperHelpers.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h3
-rw-r--r--services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h1
-rw-r--r--services/surfaceflinger/CompositionEngine/src/Display.cpp8
-rw-r--r--services/surfaceflinger/Display/DisplayModeController.cpp48
-rw-r--r--services/surfaceflinger/Display/DisplayModeController.h20
-rw-r--r--services/surfaceflinger/DisplayDevice.cpp11
-rw-r--r--services/surfaceflinger/DisplayDevice.h11
-rw-r--r--services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp16
-rw-r--r--services/surfaceflinger/DisplayHardware/AidlComposerHal.h1
-rw-r--r--services/surfaceflinger/DisplayHardware/ComposerHal.h7
-rw-r--r--services/surfaceflinger/DisplayHardware/HWC2.cpp5
-rw-r--r--services/surfaceflinger/DisplayHardware/HWC2.h4
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp15
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.h4
-rw-r--r--services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp21
-rw-r--r--services/surfaceflinger/DisplayHardware/HidlComposerHal.h1
-rw-r--r--services/surfaceflinger/FrontEnd/LayerHierarchy.cpp57
-rw-r--r--services/surfaceflinger/FrontEnd/LayerHierarchy.h19
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp23
-rw-r--r--services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h7
-rw-r--r--services/surfaceflinger/LayerProtoHelper.cpp23
-rw-r--r--services/surfaceflinger/LayerProtoHelper.h4
-rw-r--r--services/surfaceflinger/QueuedTransactionState.h42
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp34
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.h24
-rw-r--r--services/surfaceflinger/Scheduler/VsyncConfiguration.cpp11
-rw-r--r--services/surfaceflinger/Scheduler/VsyncConfiguration.h2
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp401
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h27
-rw-r--r--services/surfaceflinger/Tracing/TransactionProtoParser.cpp2
-rw-r--r--services/surfaceflinger/common/FlagManager.cpp2
-rw-r--r--services/surfaceflinger/common/include/common/FlagManager.h1
-rw-r--r--services/surfaceflinger/surfaceflinger_flags_new.aconfig10
-rw-r--r--services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp6
-rw-r--r--services/surfaceflinger/tests/IPC_test.cpp10
l---------services/surfaceflinger/tests/end2end/.clang-format1
-rw-r--r--services/surfaceflinger/tests/end2end/.clang-tidy380
-rw-r--r--services/surfaceflinger/tests/end2end/.clangd20
-rw-r--r--services/surfaceflinger/tests/end2end/Android.bp68
-rw-r--r--services/surfaceflinger/tests/end2end/AndroidTest.xml39
-rw-r--r--services/surfaceflinger/tests/end2end/OWNERS7
-rw-r--r--services/surfaceflinger/tests/end2end/README.md75
-rw-r--r--services/surfaceflinger/tests/end2end/main.cpp55
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h29
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp82
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/TestService.h76
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp123
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h61
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp106
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h59
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp181
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h70
-rw-r--r--services/surfaceflinger/tests/end2end/tests/.clang-tidy32
-rw-r--r--services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp39
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp29
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp30
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h14
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp116
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp2
-rw-r--r--services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp35
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h2
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h2
-rw-r--r--services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h2
108 files changed, 2944 insertions, 1116 deletions
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 0cd87201fb..279a4ae35c 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -454,4 +454,6 @@ private:
InputVerifier mInputVerifier;
};
+std::ostream& operator<<(std::ostream& out, const InputMessage& msg);
+
} // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 777c22a63e..2c37624304 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -542,7 +542,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) {
return BAD_VALUE;
}
- if ((mDataSize+len) > mDataCapacity) {
+ if ((mDataPos + len) > mDataCapacity) {
// grow data
err = growData(len);
if (err != NO_ERROR) {
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 6259d9d2d2..a71da3f384 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -197,6 +197,17 @@ TEST(Parcel, AppendPlainDataPartial) {
ASSERT_EQ(2, p2.readInt32());
}
+TEST(Parcel, AppendWithBadDataPos) {
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeInt32(1);
+ Parcel p2;
+ p2.setDataCapacity(8);
+ p2.setDataPosition(10000);
+
+ EXPECT_EQ(android::BAD_VALUE, p2.appendFrom(&p1, 0, 8));
+}
+
TEST(Parcel, HasBinders) {
sp<IBinder> b1 = sp<BBinder>::make();
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 43ee33eb86..2d18d80678 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -50,7 +50,6 @@ static const char* native_processes_to_dump[] = {
// Native processes to dump on debuggable builds.
static const char* debuggable_native_processes_to_dump[] = {
- "/system/bin/keystore2",
"/system/bin/vold",
NULL,
};
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 1aae13c1f4..5b0f21de91 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -1032,7 +1032,7 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti
// Apply the transaction since we have already acquired the desired frame.
t->setApplyToken(mApplyToken).apply();
} else {
- mPendingTransactions.emplace_back(frameNumber, *t);
+ mPendingTransactions.emplace_back(frameNumber, std::move(*t));
// Clear the transaction so it can't be applied elsewhere.
t->clear();
}
@@ -1050,8 +1050,8 @@ void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) {
void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
uint64_t frameNumber) {
auto mergeTransaction =
- [&t, currentFrameNumber = frameNumber](
- std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+ [t, currentFrameNumber = frameNumber](
+ std::pair<uint64_t, SurfaceComposerClient::Transaction>& pendingTransaction) {
auto& [targetFrameNumber, transaction] = pendingTransaction;
if (currentFrameNumber < targetFrameNumber) {
return false;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 269936858a..ae4b74e03b 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/SchedulingPolicy.h>
+#include <gui/TransactionState.h>
#include <private/gui/ParcelUtils.h>
#include <stdint.h>
#include <sys/types.h>
@@ -60,54 +61,12 @@ public:
virtual ~BpSurfaceComposer();
- status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
- const std::vector<uint64_t>& mergedTransactionIds) override {
+ status_t setTransactionState(TransactionState&& state) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(state.writeToParcel, &data);
- frameTimelineInfo.writeToParcel(&data);
-
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
- for (const auto& s : state) {
- SAFE_PARCEL(s.write, data);
- }
-
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
- for (const auto& d : displays) {
- SAFE_PARCEL(d.write, data);
- }
-
- SAFE_PARCEL(data.writeUint32, flags);
- SAFE_PARCEL(data.writeStrongBinder, applyToken);
- SAFE_PARCEL(commands.write, data);
- SAFE_PARCEL(data.writeInt64, desiredPresentTime);
- SAFE_PARCEL(data.writeBool, isAutoTimestamp);
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
- for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
- SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
- SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
- }
- SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
-
- SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
- SAFE_PARCEL(data.writeStrongBinder, listener);
- SAFE_PARCEL(data.writeParcelableVector, callbackIds);
- }
-
- SAFE_PARCEL(data.writeUint64, transactionId);
-
- SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(mergedTransactionIds.size()));
- for (auto mergedTransactionId : mergedTransactionIds) {
- SAFE_PARCEL(data.writeUint64, mergedTransactionId);
- }
-
- if (flags & ISurfaceComposer::eOneWay) {
+ if (state.mFlags & ISurfaceComposer::eOneWay) {
return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
data, &reply, IBinder::FLAG_ONEWAY);
} else {
@@ -132,75 +91,9 @@ status_t BnSurfaceComposer::onTransact(
case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- FrameTimelineInfo frameTimelineInfo;
- frameTimelineInfo.readFromParcel(&data);
-
- uint32_t count = 0;
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- Vector<ComposerState> state;
- state.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- ComposerState s;
- SAFE_PARCEL(s.read, data);
- state.add(s);
- }
-
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- DisplayState d;
- Vector<DisplayState> displays;
- displays.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(d.read, data);
- displays.add(d);
- }
-
- uint32_t stateFlags = 0;
- SAFE_PARCEL(data.readUint32, &stateFlags);
- sp<IBinder> applyToken;
- SAFE_PARCEL(data.readStrongBinder, &applyToken);
- InputWindowCommands inputWindowCommands;
- SAFE_PARCEL(inputWindowCommands.read, data);
-
- int64_t desiredPresentTime = 0;
- bool isAutoTimestamp = true;
- SAFE_PARCEL(data.readInt64, &desiredPresentTime);
- SAFE_PARCEL(data.readBool, &isAutoTimestamp);
-
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- std::vector<client_cache_t> uncacheBuffers(count);
- sp<IBinder> tmpBinder;
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
- uncacheBuffers[i].token = tmpBinder;
- SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
- }
-
- bool hasListenerCallbacks = false;
- SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
-
- std::vector<ListenerCallbacks> listenerCallbacks;
- int32_t listenersSize = 0;
- SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
- for (int32_t i = 0; i < listenersSize; i++) {
- SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
- std::vector<CallbackId> callbackIds;
- SAFE_PARCEL(data.readParcelableVector, &callbackIds);
- listenerCallbacks.emplace_back(tmpBinder, callbackIds);
- }
-
- uint64_t transactionId = -1;
- SAFE_PARCEL(data.readUint64, &transactionId);
-
- SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
- std::vector<uint64_t> mergedTransactions(count);
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(data.readUint64, &mergedTransactions[i]);
- }
-
- return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
- std::move(inputWindowCommands), desiredPresentTime,
- isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId, mergedTransactions);
+ TransactionState state;
+ SAFE_PARCEL(state.readFromParcel, &data);
+ return setTransactionState(std::move(state));
}
case GET_SCHEDULING_POLICY: {
gui::SchedulingPolicy policy;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 63dcfbcb9b..83c6b57289 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2230,17 +2230,47 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
return NO_ERROR;
}
+int Surface::isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const {
+ ATRACE_CALL();
+
+ if (buffer == nullptr) {
+ ALOGE("%s: Bad input, buffer was null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (outIsOwned == nullptr) {
+ ALOGE("%s: Bad input, output was null", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ int slot = this->getSlotFromBufferLocked(buffer->getNativeBuffer());
+ if (slot == BAD_VALUE) {
+ ALOGV("%s: Buffer %" PRIu64 " is not owned", __FUNCTION__, buffer->getId());
+ *outIsOwned = false;
+ return NO_ERROR;
+ } else if (slot < 0) {
+ ALOGV("%s: Buffer %" PRIu64 " look up failed (%d)", __FUNCTION__, buffer->getId(), slot);
+ *outIsOwned = false;
+ return slot;
+ }
+
+ *outIsOwned = true;
+ return NO_ERROR;
+}
+
int Surface::attachBuffer(ANativeWindowBuffer* buffer)
{
ATRACE_CALL();
- ALOGV("Surface::attachBuffer");
+ sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
+
+ ALOGV("Surface::attachBuffer bufferId=%" PRIu64, graphicBuffer->getId());
Mutex::Autolock lock(mMutex);
if (mReportRemovedBuffers) {
mRemovedBuffers.clear();
}
- sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer));
uint32_t priorGeneration = graphicBuffer->mGenerationNumber;
graphicBuffer->mGenerationNumber = mGenerationNumber;
int32_t attachedSlot = -1;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 60a2bf7b88..786bc06f64 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -824,34 +824,24 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) {
// ---------------------------------------------------------------------------
SurfaceComposerClient::Transaction::Transaction() {
- mId = generateId();
+ mState.mId = generateId();
mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
- : mId(other.mId),
- mFlags(other.mFlags),
- mMayContainBuffer(other.mMayContainBuffer),
- mDesiredPresentTime(other.mDesiredPresentTime),
- mIsAutoTimestamp(other.mIsAutoTimestamp),
- mFrameTimelineInfo(other.mFrameTimelineInfo),
- mApplyToken(other.mApplyToken) {
- mDisplayStates = other.mDisplayStates;
- mComposerStates = other.mComposerStates;
- mInputWindowCommands = other.mInputWindowCommands;
- mListenerCallbacks = other.mListenerCallbacks;
- mTransactionCompletedListener = TransactionCompletedListener::getInstance();
-}
+SurfaceComposerClient::Transaction::Transaction(Transaction&& other)
+ : mTransactionCompletedListener(TransactionCompletedListener::getInstance()),
+ mState(std::move(other.mState)),
+ mListenerCallbacks(std::move(other.mListenerCallbacks)) {}
void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid);
- for (auto& composerState : mComposerStates) {
+ for (auto& composerState : mState.mComposerStates) {
composerState.state.sanitize(permissions);
}
- if (!mInputWindowCommands.empty() &&
+ if (!mState.mInputWindowCommands.empty() &&
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
ALOGE("Only privileged callers are allowed to send input commands.");
- mInputWindowCommands.clear();
+ mState.mInputWindowCommands.clear();
}
}
@@ -866,31 +856,10 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
- const uint64_t transactionId = parcel->readUint64();
- const uint32_t flags = parcel->readUint32();
- const int64_t desiredPresentTime = parcel->readInt64();
- const bool isAutoTimestamp = parcel->readBool();
- const bool logCallPoints = parcel->readBool();
- FrameTimelineInfo frameTimelineInfo;
- frameTimelineInfo.readFromParcel(parcel);
-
- sp<IBinder> applyToken;
- parcel->readNullableStrongBinder(&applyToken);
- size_t count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- Vector<DisplayState> displayStates;
- displayStates.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- DisplayState displayState;
- if (displayState.read(*parcel) == BAD_VALUE) {
- return BAD_VALUE;
- }
- displayStates.add(displayState);
- }
+ TransactionState tmpState;
+ SAFE_PARCEL(tmpState.readFromParcel, parcel);
- count = static_cast<size_t>(parcel->readUint32());
+ size_t count = static_cast<size_t>(parcel->readUint32());
if (count > parcel->dataSize()) {
return BAD_VALUE;
}
@@ -919,57 +888,8 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
}
}
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- Vector<ComposerState> composerStates;
- composerStates.setCapacity(count);
- for (size_t i = 0; i < count; i++) {
- ComposerState composerState;
- if (composerState.read(*parcel) == BAD_VALUE) {
- return BAD_VALUE;
- }
- composerStates.add(composerState);
- }
-
- InputWindowCommands inputWindowCommands;
- inputWindowCommands.read(*parcel);
-
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- std::vector<client_cache_t> uncacheBuffers(count);
- for (size_t i = 0; i < count; i++) {
- sp<IBinder> tmpBinder;
- SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
- uncacheBuffers[i].token = tmpBinder;
- SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
- }
-
- count = static_cast<size_t>(parcel->readUint32());
- if (count > parcel->dataSize()) {
- return BAD_VALUE;
- }
- std::vector<uint64_t> mergedTransactionIds(count);
- for (size_t i = 0; i < count; i++) {
- SAFE_PARCEL(parcel->readUint64, &mergedTransactionIds[i]);
- }
-
- // Parsing was successful. Update the object.
- mId = transactionId;
- mFlags = flags;
- mDesiredPresentTime = desiredPresentTime;
- mIsAutoTimestamp = isAutoTimestamp;
- mFrameTimelineInfo = frameTimelineInfo;
- mDisplayStates = std::move(displayStates);
- mListenerCallbacks = listenerCallbacks;
- mComposerStates = std::move(composerStates);
- mInputWindowCommands = inputWindowCommands;
- mApplyToken = applyToken;
- mUncacheBuffers = std::move(uncacheBuffers);
- mMergedTransactionIds = std::move(mergedTransactionIds);
+ mState = std::move(tmpState);
+ mListenerCallbacks = std::move(listenerCallbacks);
return NO_ERROR;
}
@@ -987,17 +907,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
- parcel->writeUint64(mId);
- parcel->writeUint32(mFlags);
- parcel->writeInt64(mDesiredPresentTime);
- parcel->writeBool(mIsAutoTimestamp);
- parcel->writeBool(mLogCallPoints);
- mFrameTimelineInfo.writeToParcel(parcel);
- parcel->writeStrongBinder(mApplyToken);
- parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
- for (auto const& displayState : mDisplayStates) {
- displayState.write(*parcel);
- }
+ SAFE_PARCEL(mState.writeToParcel, parcel);
parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
@@ -1012,24 +922,6 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
}
}
- 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);
- }
-
return NO_ERROR;
}
@@ -1054,50 +946,8 @@ void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
- 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) {
- releaseBufferIfOverwriting(it->state);
- }
- it->state.merge(otherState.state);
- } else {
- mComposerStates.add(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.add(state);
- }
- }
-
+ mState.merge(std::move(other.mState),
+ std::bind(&Transaction::releaseBufferIfOverwriting, this, std::placeholders::_1));
for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
@@ -1121,50 +971,21 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr
}
}
- for (const auto& cacheId : other.mUncacheBuffers) {
- mUncacheBuffers.push_back(cacheId);
- }
-
- mInputWindowCommands.merge(other.mInputWindowCommands);
-
- mMayContainBuffer |= other.mMayContainBuffer;
- mFlags |= other.mFlags;
- mApplyToken = other.mApplyToken;
-
- mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
-
- mLogCallPoints |= other.mLogCallPoints;
- if (mLogCallPoints) {
- ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
- "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
- }
-
other.clear();
return *this;
}
void SurfaceComposerClient::Transaction::clear() {
- mComposerStates.clear();
- mDisplayStates.clear();
+ mState.clear();
mListenerCallbacks.clear();
- mInputWindowCommands.clear();
- mUncacheBuffers.clear();
- mMayContainBuffer = false;
- mDesiredPresentTime = 0;
- mIsAutoTimestamp = true;
- mFrameTimelineInfo = {};
- mApplyToken = nullptr;
- mMergedTransactionIds.clear();
- mLogCallPoints = false;
- mFlags = 0;
}
-uint64_t SurfaceComposerClient::Transaction::getId() {
- return mId;
+uint64_t SurfaceComposerClient::Transaction::getId() const {
+ return mState.mId;
}
std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() {
- return mMergedTransactionIds;
+ return mState.mMergedTransactionIds;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -1173,12 +994,13 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
client_cache_t uncacheBuffer;
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
- Vector<ComposerState> composerStates;
- Vector<DisplayState> displayStates;
- status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
- ISurfaceComposer::eOneWay,
- Transaction::getDefaultApplyToken(), {}, systemTime(),
- true, {uncacheBuffer}, false, {}, generateId(), {});
+ TransactionState state;
+ state.mId = generateId();
+ state.mApplyToken = Transaction::getDefaultApplyToken();
+ state.mUncacheBuffers.emplace_back(std::move(uncacheBuffer));
+ state.mFlags = ISurfaceComposer::eOneWay;
+ state.mDesiredPresentTime = systemTime();
+ status_t status = sf->setTransactionState(std::move(state));
if (status != NO_ERROR) {
ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
strerror(-status));
@@ -1186,12 +1008,12 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
- if (!mMayContainBuffer) {
+ if (!mState.mMayContainBuffer) {
return;
}
size_t count = 0;
- for (auto& cs : mComposerStates) {
+ for (auto& cs : mState.mComposerStates) {
layer_state_t* s = &cs.state;
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
@@ -1219,7 +1041,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() {
std::optional<client_cache_t> uncacheBuffer;
cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
if (uncacheBuffer) {
- mUncacheBuffers.push_back(*uncacheBuffer);
+ mState.mUncacheBuffers.emplace_back(*uncacheBuffer);
}
}
s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
@@ -1288,8 +1110,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
/*callbackContext=*/nullptr);
}
- bool hasListenerCallbacks = !mListenerCallbacks.empty();
- std::vector<ListenerCallbacks> listenerCallbacks;
+ mState.mHasListenerCallbacks = !mListenerCallbacks.empty();
// For every listener with registered callbacks
for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -1298,7 +1119,8 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
}
if (surfaceControls.empty()) {
- listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+ mState.mListenerCallbacks.emplace_back(IInterface::asBinder(listener),
+ std::move(callbackIds));
} else {
// If the listener has any SurfaceControls set on this Transaction update the surface
// state
@@ -1310,7 +1132,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
}
std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
s->what |= layer_state_t::eHasListenerCallbacksChanged;
- s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
+ s->listeners.emplace_back(IInterface::asBinder(listener), std::move(callbacks));
}
}
}
@@ -1322,25 +1144,21 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
ALOGE("Transaction attempted to set synchronous and one way at the same time"
" this is an invalid request. Synchronous will win for safety");
} else {
- mFlags |= ISurfaceComposer::eOneWay;
+ mState.mFlags |= ISurfaceComposer::eOneWay;
}
}
// If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set
// it is equivalent for none
uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
- if ((mFlags & wakeupFlags) == wakeupFlags) {
- mFlags &= ~(wakeupFlags);
+ if ((mState.mFlags & wakeupFlags) == wakeupFlags) {
+ mState.mFlags &= ~(wakeupFlags);
}
- sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
+ if (!mState.mApplyToken) mState.mApplyToken = getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- status_t binderStatus =
- sf->setTransactionState(mFrameTimelineInfo, mComposerStates, mDisplayStates, mFlags,
- applyToken, mInputWindowCommands, mDesiredPresentTime,
- mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, mId, mMergedTransactionIds);
- mId = generateId();
+ status_t binderStatus = sf->setTransactionState(std::move(mState));
+ mState.mId = generateId();
// Clear the current states and flags
clear();
@@ -1349,8 +1167,8 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
syncCallback->wait();
}
- if (mLogCallPoints) {
- ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
+ if (mState.mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", getId());
}
mStatus = NO_ERROR;
@@ -1385,7 +1203,7 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction
}
void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
- mLogCallPoints = true;
+ mState.mLogCallPoints = true;
}
// ---------------------------------------------------------------------------
@@ -1443,34 +1261,19 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
- mFlags |= ISurfaceComposer::eAnimation;
+ mState.mFlags |= ISurfaceComposer::eAnimation;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
- mFlags |= ISurfaceComposer::eEarlyWakeupStart;
+ mState.mFlags |= ISurfaceComposer::eEarlyWakeupStart;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
- mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
+ mState.mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
}
layer_state_t* SurfaceComposerClient::Transaction::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.add(s);
-
- return &mComposerStates.editItemAt(mComposerStates.size() - 1).state;
+ return mState.getLayerState(sc);
}
void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
@@ -1846,8 +1649,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe
setReleaseBufferCallback(bufferData.get(), callback);
}
- if (mIsAutoTimestamp) {
- mDesiredPresentTime = systemTime();
+ if (mState.mIsAutoTimestamp) {
+ mState.mDesiredPresentTime = systemTime();
}
s->what |= layer_state_t::eBufferChanged;
s->bufferData = std::move(bufferData);
@@ -1865,7 +1668,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe
const std::vector<SurfaceControlStats>&) {},
nullptr);
- mMayContainBuffer = true;
+ mState.mMayContainBuffer = true;
return *this;
}
@@ -2041,8 +1844,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSideb
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
nsecs_t desiredPresentTime) {
- mDesiredPresentTime = desiredPresentTime;
- mIsAutoTimestamp = false;
+ mState.mDesiredPresentTime = desiredPresentTime;
+ mState.mIsAutoTimestamp = false;
return *this;
}
@@ -2131,14 +1934,14 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
- mInputWindowCommands.addFocusRequest(request);
+ mState.mInputWindowCommands.addFocusRequest(request);
return *this;
}
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
- mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
+ mState.mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
return *this;
}
@@ -2315,7 +2118,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixed
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
const FrameTimelineInfo& frameTimelineInfo) {
- mergeFrameTimelineInfo(mFrameTimelineInfo, frameTimelineInfo);
+ mState.mergeFrameTimelineInfo(frameTimelineInfo);
return *this;
}
@@ -2354,7 +2157,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrust
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
const sp<IBinder>& applyToken) {
- mApplyToken = applyToken;
+ mState.mApplyToken = applyToken;
return *this;
}
@@ -2482,17 +2285,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setConte
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::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.add(s);
- return mDisplayStates.editItemAt(mDisplayStates.size() - 1);
+ return mState.getDisplayState(token);
}
status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token,
@@ -2545,20 +2338,6 @@ void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token
s.what |= DisplayState::eDisplaySizeChanged;
}
-// copied from FrameTimelineInfo::merge()
-void SurfaceComposerClient::Transaction::mergeFrameTimelineInfo(FrameTimelineInfo& t,
- const FrameTimelineInfo& other) {
- // When merging vsync Ids we take the oldest valid one
- if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
- other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- if (other.vsyncId > t.vsyncId) {
- t = other;
- }
- } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- t = other;
- }
-}
-
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index db1b9fb8eb..c69b0a79ad 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -284,7 +284,7 @@ private:
std::function<void(SurfaceComposerClient::Transaction*)> mTransactionReadyCallback
GUARDED_BY(mMutex);
SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
- std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
+ std::vector<std::pair<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 50abadb922..6a1e9f6f03 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -36,8 +36,6 @@ class BufferQueueProducer : public BnGraphicBufferProducer {
public:
friend class BufferQueue; // Needed to access binderDied
- explicit BufferQueueProducer(const sp<BufferQueueCore>& core,
- bool consumerIsSurfaceFlinger = false);
~BufferQueueProducer() override;
// requestBuffer returns the GraphicBuffer for slot N.
@@ -219,6 +217,9 @@ public:
#endif
protected:
+ explicit BufferQueueProducer(const sp<BufferQueueCore>& core,
+ bool consumerIsSurfaceFlinger = false);
+ friend class sp<BufferQueueProducer>;
// see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
// total maximum buffer count for the buffer queue (dequeued AND acquired)
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount);
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 5862967d4a..b436af1eb3 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -79,8 +79,6 @@ public:
};
static Context gChoreographers;
- explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr)
- EXCLUDES(gChoreographers.lock);
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
@@ -113,6 +111,10 @@ public:
private:
Choreographer(const Choreographer&) = delete;
+ explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr)
+ EXCLUDES(gChoreographers.lock);
+ friend class sp<Choreographer>;
+ friend AChoreographer* AChoreographer_create();
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index b06ad077f4..cdf216c945 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -23,12 +23,6 @@ using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
class DisplayEventDispatcher : public LooperCallback {
public:
- explicit DisplayEventDispatcher(const sp<Looper>& looper,
- gui::ISurfaceComposer::VsyncSource vsyncSource =
- gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
- EventRegistrationFlags eventRegistration = {},
- const sp<IBinder>& layerHandle = nullptr);
-
status_t initialize();
void dispose();
status_t scheduleVsync();
@@ -38,6 +32,14 @@ public:
status_t getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) const;
protected:
+ explicit DisplayEventDispatcher(const sp<Looper>& looper,
+ gui::ISurfaceComposer::VsyncSource vsyncSource =
+ gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+ EventRegistrationFlags eventRegistration = {},
+ const sp<IBinder>& layerHandle = nullptr);
+
+ friend class sp<DisplayEventDispatcher>;
+
virtual ~DisplayEventDispatcher() = default;
private:
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9a422fd808..de553aef72 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -65,6 +65,7 @@ struct DisplayState;
struct InputWindowCommands;
class HdrCapabilities;
class Rect;
+class TransactionState;
using gui::FrameTimelineInfo;
using gui::IDisplayEventConnection;
@@ -105,13 +106,7 @@ public:
};
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
- virtual status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
- bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0;
+ virtual status_t setTransactionState(TransactionState&& state) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 755674d9e6..3cfbed11bf 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -442,6 +442,9 @@ public:
status_t detachBuffer(const sp<GraphicBuffer>& buffer);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // Sets outIsOwned to true if the given buffer is currently known to be owned by this Surface.
+ status_t isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const;
+
// Batch version of dequeueBuffer, cancelBuffer and queueBuffer
// Note that these batched operations are not supported when shared buffer mode is being used.
struct BatchBuffer {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 1ad9f9505d..5c348cb9c7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -52,6 +52,7 @@
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
#include <gui/SurfaceControl.h>
+#include <gui/TransactionState.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -442,61 +443,16 @@ public:
virtual ~PresentationCallbackRAII();
};
- class Transaction : public Parcelable {
+ class Transaction {
private:
static sp<IBinder> sApplyToken;
static std::mutex sApplyTokenMutex;
void releaseBufferIfOverwriting(const layer_state_t& state);
- static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
// Tracks registered callbacks
sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
- // Prints debug logs when enabled.
- bool mLogCallPoints = false;
- protected:
- Vector<ComposerState> mComposerStates;
- Vector<DisplayState> mDisplayStates;
- std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
- mListenerCallbacks;
- std::vector<client_cache_t> mUncacheBuffers;
-
- // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
- // Ordered most recently merged to least recently merged.
- static const size_t MAX_MERGE_HISTORY_LENGTH = 10u;
- std::vector<uint64_t> mMergedTransactionIds;
-
- uint64_t mId;
- uint32_t mFlags = 0;
-
- // 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;
-
- // 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;
-
- // The vsync id provided by Choreographer.getVsyncId and the input event id
- FrameTimelineInfo mFrameTimelineInfo;
-
- // If not null, transactions will be queued up using this token otherwise a common token
- // per process will be used.
- sp<IBinder> mApplyToken = nullptr;
+ TransactionState mState;
- InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
@@ -506,23 +462,29 @@ public:
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
+ protected:
+ // Accessed in tests.
+ explicit Transaction(Transaction const& other) = default;
+ std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+ mListenerCallbacks;
+
public:
Transaction();
- virtual ~Transaction() = default;
- Transaction(Transaction const& other);
+ Transaction(Transaction&& other);
+ Transaction& operator=(Transaction&& other) = default;
// Factory method that creates a new Transaction instance from the parcel.
static std::unique_ptr<Transaction> createFromParcel(const Parcel* parcel);
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(const Parcel* parcel);
// Clears the contents of the transaction without applying it.
void clear();
// Returns the current id of the transaction.
// The id is updated every time the transaction is applied.
- uint64_t getId();
+ uint64_t getId() const;
std::vector<uint64_t> getMergedTransactionIds();
diff --git a/libs/gui/include/gui/TransactionState.h b/libs/gui/include/gui/TransactionState.h
index 4358227dae..79124f3ed7 100644
--- a/libs/gui/include/gui/TransactionState.h
+++ b/libs/gui/include/gui/TransactionState.h
@@ -26,7 +26,8 @@ namespace android {
class TransactionState {
public:
explicit TransactionState() = default;
- TransactionState(TransactionState const& other) = default;
+ TransactionState(TransactionState&& other) = default;
+ TransactionState& operator=(TransactionState&& other) = default;
status_t writeToParcel(Parcel* parcel) const;
status_t readFromParcel(const Parcel* parcel);
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
@@ -86,6 +87,9 @@ public:
std::vector<ListenerCallbacks> mListenerCallbacks;
private:
+ explicit TransactionState(TransactionState const& other) = default;
+ friend class TransactionApplicationTest;
+ friend class SurfaceComposerClient;
// 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;
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 684e21ad96..f9a3acee41 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -40,6 +40,9 @@ public:
void reconnect(const sp<gui::ISurfaceComposer>&);
private:
+ WindowInfosListenerReporter() = default;
+ friend class sp<WindowInfosListenerReporter>;
+
std::mutex mListenersMutex;
std::unordered_set<sp<gui::WindowInfosListener>, gui::SpHash<gui::WindowInfosListener>>
mWindowInfosListeners GUARDED_BY(mListenersMutex);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 61c93cae14..4fee11c2cb 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -648,16 +648,7 @@ public:
mSupportsPresent = supportsPresent;
}
- status_t setTransactionState(
- const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
- Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
- const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
- const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, uint64_t /*transactionId*/,
- const std::vector<uint64_t>& /*mergedTransactionIds*/) override {
- return NO_ERROR;
- }
+ status_t setTransactionState(TransactionState&&) override { return NO_ERROR; }
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -2699,4 +2690,57 @@ TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) {
EXPECT_EQ(128u, outputs.size());
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS)
+
+TEST_F(SurfaceTest, isBufferOwned) {
+ const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
+ auto [bufferItemConsumer, surface] = BufferItemConsumer::create(TEST_USAGE_FLAGS);
+
+ sp<SurfaceListener> listener = sp<StubSurfaceListener>::make();
+ ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener));
+
+ sp<GraphicBuffer> surfaceAttachableBuffer =
+ sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS);
+
+ //
+ // Attaching a buffer makes it owned.
+ //
+
+ bool isOwned;
+ EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned));
+ EXPECT_FALSE(isOwned);
+
+ EXPECT_EQ(OK, surface->attachBuffer(surfaceAttachableBuffer.get()));
+ EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned));
+ EXPECT_TRUE(isOwned);
+
+ //
+ // A dequeued buffer is always owned.
+ //
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned));
+ EXPECT_TRUE(isOwned);
+
+ //
+ // A detached buffer is no longer owned.
+ //
+
+ EXPECT_EQ(OK, surface->detachBuffer(buffer));
+ EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned));
+ EXPECT_FALSE(isOwned);
+
+ //
+ // It's not currently possible to verify whether or not a consumer has attached a buffer until
+ // it shows up on the Surface.
+ //
+
+ sp<GraphicBuffer> consumerAttachableBuffer =
+ sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS);
+
+ ASSERT_EQ(OK, bufferItemConsumer->attachBuffer(consumerAttachableBuffer));
+ EXPECT_EQ(OK, surface->isBufferOwned(consumerAttachableBuffer, &isOwned));
+ EXPECT_FALSE(isOwned);
+}
} // namespace android
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 6087461f6c..9578639e2b 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -171,11 +171,6 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim
return msg;
}
-std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
- out << ftl::enum_string(msg.header.type);
- return out;
-}
-
} // namespace
// --- InputConsumerNoResampling ---
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d388d48e8d..cb6f7f0707 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -436,16 +436,29 @@ android::base::Result<InputMessage> InputChannel::receiveMessage() {
if (error == EAGAIN || error == EWOULDBLOCK) {
return android::base::Error(WOULD_BLOCK);
}
- if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
- return android::base::Error(DEAD_OBJECT);
+ if (error == EPIPE) {
+ return android::base::ResultError("Got EPIPE", DEAD_OBJECT);
+ }
+ if (error == ENOTCONN) {
+ return android::base::ResultError("Got ENOTCONN", DEAD_OBJECT);
+ }
+ if (error == ECONNREFUSED) {
+ return android::base::ResultError("Got ECONNREFUSED", DEAD_OBJECT);
+ }
+ if (error == ECONNRESET) {
+ // This means that the client has closed the channel while there was
+ // still some data in the buffer. In most cases, subsequent reads
+ // would result in more data. However, that is not guaranteed, so we
+ // should not return WOULD_BLOCK here to try again.
+ return android::base::ResultError("Got ECONNRESET", DEAD_OBJECT);
}
return android::base::Error(-error);
}
if (nRead == 0) { // check for EOF
- ALOGD_IF(DEBUG_CHANNEL_MESSAGES,
- "channel '%s' ~ receive message failed because peer was closed", name.c_str());
- return android::base::Error(DEAD_OBJECT);
+ LOG_IF(INFO, DEBUG_CHANNEL_MESSAGES)
+ << "channel '" << name << "' ~ receive message failed because peer was closed";
+ return android::base::ResultError("::recv returned 0", DEAD_OBJECT);
}
if (!msg.isValid(nRead)) {
@@ -766,4 +779,9 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC
return android::base::Error(UNKNOWN_ERROR);
}
+std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
+ out << ftl::enum_string(msg.header.type);
+ return out;
+}
+
} // namespace android
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 983bbdee6e..a2de0093d1 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -113,6 +113,16 @@ flag {
}
flag {
+ name: "allow_transfer_of_entire_gesture"
+ namespace: "input"
+ description: "When calling 'transferTouchGesture', the entire gesture (including new POINTER_DOWN events from the same device) will be automatically transferred to the destination window"
+ bug: "397979572"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_keyboard_classifier"
namespace: "input"
description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
@@ -224,13 +234,6 @@ flag {
}
flag {
- name: "deprecate_uiautomation_input_injection"
- namespace: "input"
- description: "Deprecate UiAutomation#injectInputEvent and UiAutomation#injectInputEventToInputFilter test/hidden APIs"
- bug: "277261245"
-}
-
-flag {
name: "prevent_merging_input_pointer_devices"
namespace: "desktop_input"
description: "Prevent merging input sub-devices that provide pointer input streams"
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 25356cfcf0..9b582d99dc 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -20,6 +20,7 @@
#include <time.h>
#include <errno.h>
+#include <android-base/logging.h>
#include <binder/Binder.h>
#include <binder/Parcel.h>
#include <gtest/gtest.h>
@@ -43,6 +44,39 @@ bool operator==(const InputChannel& left, const InputChannel& right) {
return left.getName() == right.getName() &&
left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino;
}
+
+/**
+ * Read a message from the provided channel. Read will continue until there's data, so only call
+ * this if there's data in the channel, or it's closed. If there's no data, this will loop forever.
+ */
+android::base::Result<InputMessage> readMessage(InputChannel& channel) {
+ while (true) {
+ // Keep reading until we get something other than 'WOULD_BLOCK'
+ android::base::Result<InputMessage> result = channel.receiveMessage();
+ if (!result.ok() && result.error().code() == WOULD_BLOCK) {
+ // The data is not available yet.
+ continue; // try again
+ }
+ return result;
+ }
+}
+
+InputMessage createFinishedMessage(uint32_t seq) {
+ InputMessage finish{};
+ finish.header.type = InputMessage::Type::FINISHED;
+ finish.header.seq = seq;
+ finish.body.finished.handled = true;
+ return finish;
+}
+
+InputMessage createKeyMessage(uint32_t seq) {
+ InputMessage key{};
+ key.header.type = InputMessage::Type::KEY;
+ key.header.seq = seq;
+ key.body.key.action = AKEY_EVENT_ACTION_DOWN;
+ return key;
+}
+
} // namespace
class InputChannelTest : public testing::Test {
@@ -227,6 +261,120 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) {
}
}
+/**
+ * In this test, server writes 3 key events to the client. The client, upon receiving the first key,
+ * sends a "finished" signal back to server, and then closes the fd.
+ *
+ * Next, we check what the server receives.
+ *
+ * In most cases, the server will receive the finish event, and then an 'fd closed' event.
+ *
+ * However, sometimes, the 'finish' event will not be delivered to the server. This is communicated
+ * to the server via 'ECONNRESET', which the InputChannel converts into DEAD_OBJECT.
+ *
+ * The server needs to be aware of this behaviour and correctly clean up any state associated with
+ * the client, even if the client did not end up finishing some of the messages.
+ *
+ * This test is written to expose a behaviour on the linux side - occasionally, the
+ * last events written to the fd by the consumer are not delivered to the server.
+ *
+ * When tested on 2025 hardware, ECONNRESET was received approximately 1 out of 40 tries.
+ * In vast majority (~ 29999 / 30000) of cases, after receiving ECONNRESET, the server could still
+ * read the client data after receiving ECONNRESET.
+ */
+TEST_F(InputChannelTest, ReceiveAfterCloseMultiThreaded) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ status_t result =
+ InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ // Sender / publisher: publish 3 keys
+ InputMessage key1 = createKeyMessage(/*seq=*/1);
+ serverChannel->sendMessage(&key1);
+ // The client should close the fd after it reads this one, but we will send 2 more here.
+ InputMessage key2 = createKeyMessage(/*seq=*/2);
+ serverChannel->sendMessage(&key2);
+ InputMessage key3 = createKeyMessage(/*seq=*/3);
+ serverChannel->sendMessage(&key3);
+
+ std::thread consumer = std::thread([clientChannel = std::move(clientChannel)]() mutable {
+ // Read the first key
+ android::base::Result<InputMessage> firstKey = readMessage(*clientChannel);
+ if (!firstKey.ok()) {
+ FAIL() << "Did not receive the first key";
+ }
+
+ // Send finish
+ const InputMessage finish = createFinishedMessage(firstKey->header.seq);
+ clientChannel->sendMessage(&finish);
+ // Now close the fd
+ clientChannel.reset();
+ });
+
+ // Now try to read the finish message, even though client closed the fd
+ android::base::Result<InputMessage> response = readMessage(*serverChannel);
+ consumer.join();
+ if (response.ok()) {
+ ASSERT_EQ(response->header.type, InputMessage::Type::FINISHED);
+ } else {
+ // It's possible that after the client closes the fd, server will receive ECONNRESET.
+ // In those situations, this error code will be translated into DEAD_OBJECT by the
+ // InputChannel.
+ ASSERT_EQ(response.error().code(), DEAD_OBJECT);
+ // In most cases, subsequent attempts to read the client channel at this
+ // point would succeed. However, for simplicity, we exit here (since
+ // it's not guaranteed).
+ return;
+ }
+
+ // There should not be any more events from the client, since the client closed fd after the
+ // first key.
+ android::base::Result<InputMessage> noEvent = serverChannel->receiveMessage();
+ ASSERT_FALSE(noEvent.ok()) << "Got event " << *noEvent;
+}
+
+/**
+ * Similar test as above, but single-threaded.
+ */
+TEST_F(InputChannelTest, ReceiveAfterCloseSingleThreaded) {
+ std::unique_ptr<InputChannel> serverChannel, clientChannel;
+ status_t result =
+ InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel);
+ ASSERT_EQ(OK, result) << "should have successfully opened a channel pair";
+
+ // Sender / publisher: publish 3 keys
+ InputMessage key1 = createKeyMessage(/*seq=*/1);
+ serverChannel->sendMessage(&key1);
+ // The client should close the fd after it reads this one, but we will send 2 more here.
+ InputMessage key2 = createKeyMessage(/*seq=*/2);
+ serverChannel->sendMessage(&key2);
+ InputMessage key3 = createKeyMessage(/*seq=*/3);
+ serverChannel->sendMessage(&key3);
+
+ // Read the first key
+ android::base::Result<InputMessage> firstKey = readMessage(*clientChannel);
+ if (!firstKey.ok()) {
+ FAIL() << "Did not receive the first key";
+ }
+
+ // Send finish
+ const InputMessage finish = createFinishedMessage(firstKey->header.seq);
+ clientChannel->sendMessage(&finish);
+ // Now close the fd
+ clientChannel.reset();
+
+ // Now try to read the finish message, even though client closed the fd
+ android::base::Result<InputMessage> response = readMessage(*serverChannel);
+ ASSERT_FALSE(response.ok());
+ ASSERT_EQ(response.error().code(), DEAD_OBJECT);
+
+ // We can still read the finish event (but in practice, the expectation is that the server will
+ // not be doing this after getting DEAD_OBJECT).
+ android::base::Result<InputMessage> finishEvent = serverChannel->receiveMessage();
+ ASSERT_TRUE(finishEvent.ok());
+ ASSERT_EQ(finishEvent->header.type, InputMessage::Type::FINISHED);
+}
+
TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) {
std::unique_ptr<InputChannel> serverChannel, clientChannel;
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index 1e3449c004..201d5e9ff5 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -39,12 +39,29 @@ struct DetailedTimingDescriptor {
ui::Size physicalSizeInMm;
};
+// These values must match the ones in ScreenPartStatus.aidl file in the composer HAL
+enum class ScreenPartStatus : uint8_t {
+ /**
+ * Device cannot differentiate an original screen from a replaced screen.
+ */
+ UNSUPPORTED = 0,
+ /**
+ * Device has the original screen it was manufactured with.
+ */
+ ORIGINAL = 1,
+ /**
+ * Device has a replaced screen.
+ */
+ REPLACED = 2,
+};
+
struct DisplayIdentificationInfo {
PhysicalDisplayId id;
std::string name;
uint8_t port;
std::optional<DeviceProductInfo> deviceProductInfo;
std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
+ ScreenPartStatus screenPartStatus;
};
struct ExtensionBlock {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 391703d506..2908c61182 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2510,6 +2510,24 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
+ if (newTouchedWindowHandle != nullptr &&
+ maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // Check if this should be redirected to another window, in case this window previously
+ // called 'transferTouch' for this gesture.
+ const auto it =
+ std::find_if(tempTouchState.windows.begin(), tempTouchState.windows.end(),
+ [&](const TouchedWindow& touchedWindow) {
+ return touchedWindow.forwardingWindowToken ==
+ newTouchedWindowHandle->getToken() &&
+ touchedWindow.hasTouchingPointers(entry.deviceId);
+ });
+ if (it != tempTouchState.windows.end()) {
+ LOG(INFO) << "Forwarding pointer from " << newTouchedWindowHandle->getName()
+ << " to " << it->windowHandle->getName();
+ newTouchedWindowHandle = it->windowHandle;
+ }
+ }
+
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos);
if (newTouchedWindowHandle != nullptr) {
@@ -2550,7 +2568,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
isDownOrPointerDown
? std::make_optional(
entry.eventTime)
- : std::nullopt);
+ : std::nullopt,
+ /*forwardingWindowToken=*/nullptr);
if (!addResult.ok()) {
LOG(ERROR) << "Error while processing " << entry << " for "
<< windowHandle->getName();
@@ -2577,7 +2596,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
tempTouchState.addOrUpdateWindow(wallpaper,
InputTarget::DispatchMode::AS_IS,
wallpaperFlags, entry.deviceId, {pointer},
- entry.eventTime);
+ entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
}
}
}
@@ -2676,7 +2696,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
InputTarget::DispatchMode::SLIPPERY_ENTER,
targetFlags, entry.deviceId, {pointer},
- entry.eventTime);
+ entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
@@ -5833,7 +5854,7 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
}
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop) {
+ bool isDragDrop, bool transferEntireGesture) {
if (fromToken == toToken) {
LOG_IF(INFO, DEBUG_FOCUS) << "Trivial transfer to same window.";
return true;
@@ -5847,7 +5868,7 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s
"transferring touch from this window to another window",
traceContext.getTracker());
- auto result = mTouchStates.transferTouchGesture(fromToken, toToken);
+ auto result = mTouchStates.transferTouchGesture(fromToken, toToken, transferEntireGesture);
if (!result.has_value()) {
return false;
}
@@ -5891,7 +5912,8 @@ std::optional<std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<Pointe
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>,
std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>>
InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken,
- const sp<android::IBinder>& toToken) {
+ const sp<android::IBinder>& toToken,
+ bool transferEntireGesture) {
// Find the target touch state and touched window by fromToken.
auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken);
if (!touchStateWindowAndDisplay.has_value()) {
@@ -5934,8 +5956,12 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB
}
// Transferring touch focus using this API should not effect the focused window.
newTargetFlags |= InputTarget::Flags::NO_FOCUS_CHANGE;
+ sp<IBinder> forwardingWindowToken;
+ if (transferEntireGesture && com::android::input::flags::allow_transfer_of_entire_gesture()) {
+ forwardingWindowToken = fromToken;
+ }
state.addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags,
- deviceId, pointers, downTimeInTarget);
+ deviceId, pointers, downTimeInTarget, forwardingWindowToken);
// Synthesize cancel for old window and down for new window.
std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken);
@@ -6017,7 +6043,8 @@ bool InputDispatcher::transferTouchOnDisplay(const sp<IBinder>& destChannelToken
fromToken = from->getToken();
} // release lock
- return transferTouchGesture(fromToken, destChannelToken);
+ return transferTouchGesture(fromToken, destChannelToken, /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false);
}
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
@@ -7195,7 +7222,8 @@ void InputDispatcher::DispatcherTouchState::slipWallpaperTouch(
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointers, entry.eventTime);
+ deviceId, pointers, entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
}
}
@@ -7236,7 +7264,8 @@ InputDispatcher::DispatcherTouchState::transferWallpaperTouch(
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
- deviceId, pointers, downTimeInTarget);
+ deviceId, pointers, downTimeInTarget,
+ /*forwardingWindowToken=*/nullptr);
std::shared_ptr<Connection> wallpaperConnection =
mConnectionManager.getConnection(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index ad7e87e192..2e8f2ce04e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -127,7 +127,7 @@ public:
void setMaximumObscuringOpacityForTouch(float opacity) override;
bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop = false) override;
+ bool isDragDrop, bool transferEntireGesture) override;
bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken,
ui::LogicalDisplayId displayId) override;
@@ -440,7 +440,8 @@ private:
std::optional<
std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>,
std::list<CancellationArgs>, std::list<PointerDownArgs>>>
- transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
+ transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool transferEntireGesture);
base::Result<std::list<CancellationArgs>, status_t> pilferPointers(
const sp<IBinder>& token, const Connection& requestingConnection);
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2bf63beb05..f1fca0c317 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -74,7 +74,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow(
const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget) {
+ std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken) {
if (touchingPointers.empty()) {
LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
return android::base::Error();
@@ -88,6 +88,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow(
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags |= targetFlags;
+ touchedWindow.forwardingWindowToken = forwardingWindowToken;
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
@@ -103,6 +104,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow(
touchedWindow.windowHandle = windowHandle;
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags = targetFlags;
+ touchedWindow.forwardingWindowToken = forwardingWindowToken;
touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 451d91704c..20155f4396 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,7 @@ struct TouchState {
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget);
+ std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer, float x,
float y);
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index fa5be1a719..053a2e2441 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -331,9 +331,9 @@ std::string TouchedWindow::dump() const {
std::string out;
std::string deviceStates =
dumpMap(mDeviceStates, constToString, TouchedWindow::deviceStateToString);
- out += StringPrintf("name='%s', targetFlags=%s, mDeviceStates=%s\n",
+ out += StringPrintf("name='%s', targetFlags=%s, forwardingWindowToken=%p, mDeviceStates=%s\n",
windowHandle->getName().c_str(), targetFlags.string().c_str(),
- deviceStates.c_str());
+ forwardingWindowToken.get(), deviceStates.c_str());
return out;
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index c38681eef0..d27c597803 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -34,6 +34,11 @@ struct TouchedWindow {
InputTarget::DispatchMode dispatchMode = InputTarget::DispatchMode::AS_IS;
ftl::Flags<InputTarget::Flags> targetFlags;
+ // If another window has transferred touches to this window, and wants to continue sending the
+ // rest of the gesture to this window, store the token of the originating (transferred-from)
+ // window here.
+ sp<IBinder> forwardingWindowToken;
+
// Hovering
bool hasHoveringPointers() const;
bool hasHoveringPointers(DeviceId deviceId) const;
@@ -76,7 +81,7 @@ struct TouchedWindow {
};
std::vector<DeviceId> eraseHoveringPointersIf(
- std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition);
+ std::function<bool(const PointerProperties&, float x, float y)> condition);
private:
struct DeviceState {
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index ab039c34ef..b22ddca4e6 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -148,7 +148,7 @@ public:
* Returns true on success. False if the window did not actually have an active touch gesture.
*/
virtual bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop) = 0;
+ bool isDragDrop, bool transferEntireGesture) = 0;
/**
* Transfer a touch gesture to the provided channel, no matter where the current touch is.
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 608bec4a0c..c8432005a4 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -443,6 +443,9 @@ public:
/* Get the Bluetooth address of an input device, if known. */
virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
+ /* Gets the sysfs root path for this device. Returns an empty path if there is none. */
+ virtual std::filesystem::path getSysfsRootPath(int32_t deviceId) const = 0;
+
/* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 2fcb5d831f..559bc0aa7a 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -246,7 +246,7 @@ static nsecs_t processEventTimestamp(const struct input_event& event) {
/**
* Returns the sysfs root path of the input device.
*/
-static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) {
+static std::optional<std::filesystem::path> getSysfsRootForEvdevDevicePath(const char* devicePath) {
std::error_code errorCode;
// Stat the device path to get the major and minor number of the character file
@@ -1619,7 +1619,7 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) {
std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked(
const std::filesystem::path& devicePath, const std::shared_ptr<PropertyMap>& config) const {
const std::optional<std::filesystem::path> sysfsRootPathOpt =
- getSysfsRootPath(devicePath.c_str());
+ getSysfsRootForEvdevDevicePath(devicePath.c_str());
if (!sysfsRootPathOpt) {
return nullptr;
}
@@ -1897,58 +1897,89 @@ std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
break; // return to the caller before we actually rescan
}
- // Report any devices that had last been added/removed.
- for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
- std::unique_ptr<Device> device = std::move(*it);
- ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
- const int32_t deviceId = (device->id == mBuiltInKeyboardId)
- ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
- : device->id;
- events.push_back({
- .when = now,
- .deviceId = deviceId,
- .type = DEVICE_REMOVED,
- });
- it = mClosingDevices.erase(it);
- if (events.size() == EVENT_BUFFER_SIZE) {
- break;
+ handleSysfsNodeChangeNotificationsLocked();
+
+ // Use a do-while loop to ensure that we drain the closing and opening devices loop
+ // at least once, even if there are no devices to re-open.
+ do {
+ if (!mDeviceIdsToReopen.empty()) {
+ // If there are devices that need to be re-opened, ensure that we re-open them
+ // one at a time to send the DEVICE_REMOVED and DEVICE_ADDED notifications for
+ // each before moving on to the next. This is to avoid notifying all device
+ // removals and additions in one batch, which could cause additional unnecessary
+ // device added/removed notifications for merged InputDevices from InputReader.
+ const int32_t deviceId = mDeviceIdsToReopen.back();
+ mDeviceIdsToReopen.erase(mDeviceIdsToReopen.end() - 1);
+ if (auto it = mDevices.find(deviceId); it != mDevices.end()) {
+ ALOGI("Reopening input device: id=%d, name=%s", it->second->id,
+ it->second->identifier.name.c_str());
+ const auto path = it->second->path;
+ closeDeviceLocked(*it->second);
+ openDeviceLocked(path);
+ }
}
- }
- if (mNeedToScanDevices) {
- mNeedToScanDevices = false;
- scanDevicesLocked();
- }
-
- while (!mOpeningDevices.empty()) {
- std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
- mOpeningDevices.pop_back();
- ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
- const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
- events.push_back({
- .when = now,
- .deviceId = deviceId,
- .type = DEVICE_ADDED,
- });
-
- // Try to find a matching video device by comparing device names
- for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
- it++) {
- std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
- if (tryAddVideoDeviceLocked(*device, videoDevice)) {
- // videoDevice was transferred to 'device'
- it = mUnattachedVideoDevices.erase(it);
+ // Report any devices that had last been added/removed.
+ for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
+ std::unique_ptr<Device> device = std::move(*it);
+ ALOGV("Reporting device closed: id=%d, name=%s\n", device->id,
+ device->path.c_str());
+ const int32_t deviceId = (device->id == mBuiltInKeyboardId)
+ ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
+ : device->id;
+ events.push_back({
+ .when = now,
+ .deviceId = deviceId,
+ .type = DEVICE_REMOVED,
+ });
+ it = mClosingDevices.erase(it);
+ if (events.size() == EVENT_BUFFER_SIZE) {
break;
}
}
- auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
- if (!inserted) {
- ALOGW("Device id %d exists, replaced.", device->id);
+ if (mNeedToScanDevices) {
+ mNeedToScanDevices = false;
+ scanDevicesLocked();
}
- if (events.size() == EVENT_BUFFER_SIZE) {
- break;
+
+ while (!mOpeningDevices.empty()) {
+ std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
+ mOpeningDevices.pop_back();
+ ALOGV("Reporting device opened: id=%d, name=%s\n", device->id,
+ device->path.c_str());
+ const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+ events.push_back({
+ .when = now,
+ .deviceId = deviceId,
+ .type = DEVICE_ADDED,
+ });
+
+ // Try to find a matching video device by comparing device names
+ for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
+ it++) {
+ std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
+ // videoDevice was transferred to 'device'
+ it = mUnattachedVideoDevices.erase(it);
+ break;
+ }
+ }
+
+ auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
+ if (!inserted) {
+ ALOGW("Device id %d exists, replaced.", device->id);
+ }
+ if (events.size() == EVENT_BUFFER_SIZE) {
+ break;
+ }
}
+
+ // Perform this loop of re-opening devices so that we re-open one device at a time.
+ } while (!mDeviceIdsToReopen.empty());
+
+ if (events.size() == EVENT_BUFFER_SIZE) {
+ break;
}
// Grab the next input event.
@@ -2664,17 +2695,44 @@ status_t EventHub::disableDevice(int32_t deviceId) {
return device->disable();
}
+std::filesystem::path EventHub::getSysfsRootPath(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return {};
+ }
+
+ return device->associatedDevice ? device->associatedDevice->sysfsRootPath
+ : std::filesystem::path{};
+}
+
// TODO(b/274755573): Shift to uevent handling on native side and remove this method
// Currently using Java UEventObserver to trigger this which uses UEvent infrastructure that uses a
// NETLINK socket to observe UEvents. We can create similar infrastructure on Eventhub side to
// directly observe UEvents instead of triggering from Java side.
void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
- std::scoped_lock _l(mLock);
+ mChangedSysfsNodeNotifications.emplace(sysfsNodePath);
+}
+
+void EventHub::handleSysfsNodeChangeNotificationsLocked() {
+ // Use a set to de-dup any repeated notifications.
+ std::set<std::string> changedNodes;
+ while (true) {
+ auto node = mChangedSysfsNodeNotifications.popWithTimeout(std::chrono::nanoseconds(0));
+ if (!node.has_value()) break;
+ changedNodes.emplace(*node);
+ }
+ if (changedNodes.empty()) {
+ return;
+ }
// Testing whether a sysfs node changed involves several syscalls, so use a cache to avoid
// testing the same node multiple times.
+ // TODO(b/281822656): Notify InputReader separately when an AssociatedDevice changes,
+ // instead of needing to re-open all of Devices that are associated with it.
std::map<std::shared_ptr<const AssociatedDevice>, bool /*changed*/> testedDevices;
- auto isAssociatedDeviceChanged = [&testedDevices, &sysfsNodePath](const Device& dev) {
+ auto shouldReopenDevice = [&testedDevices, &changedNodes](const Device& dev) {
if (!dev.associatedDevice) {
return false;
}
@@ -2683,45 +2741,45 @@ void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
return testedIt->second;
}
// Cache miss
- if (sysfsNodePath.find(dev.associatedDevice->sysfsRootPath.string()) == std::string::npos) {
+ const bool anyNodesChanged =
+ std::any_of(changedNodes.begin(), changedNodes.end(), [&](const std::string& node) {
+ return node.find(dev.associatedDevice->sysfsRootPath.string()) !=
+ std::string::npos;
+ });
+ if (!anyNodesChanged) {
testedDevices.emplace(dev.associatedDevice, false);
return false;
}
auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath,
dev.associatedDevice->baseDevConfig);
const bool changed = *dev.associatedDevice != reloadedDevice;
+ if (changed) {
+ ALOGI("sysfsNodeChanged: Identified change in sysfs nodes for device: %s",
+ dev.identifier.name.c_str());
+ }
testedDevices.emplace(dev.associatedDevice, changed);
return changed;
};
- std::set<Device*> devicesToClose;
- std::set<std::string /*path*/> devicesToOpen;
-
- // Check in opening devices. If its associated device changed,
- // the device should be removed from mOpeningDevices and needs to be opened again.
- std::erase_if(mOpeningDevices, [&](const auto& dev) {
- if (isAssociatedDeviceChanged(*dev)) {
- devicesToOpen.emplace(dev->path);
- return true;
+ // Check in opening devices. These can be re-opened directly because we have not yet notified
+ // the Reader about these devices.
+ for (const auto& dev : mOpeningDevices) {
+ if (shouldReopenDevice(*dev)) {
+ ALOGI("Reopening input device from mOpeningDevices: id=%d, name=%s", dev->id,
+ dev->identifier.name.c_str());
+ const auto path = dev->path;
+ closeDeviceLocked(*dev); // The Device object is deleted by this function.
+ openDeviceLocked(path);
}
- return false;
- });
+ }
- // Check in already added device. If its associated device changed,
- // the device needs to be re-opened.
+ // Check in already added devices. Add them to the re-opening list so they can be
+ // re-opened serially.
for (const auto& [id, dev] : mDevices) {
- if (isAssociatedDeviceChanged(*dev)) {
- devicesToOpen.emplace(dev->path);
- devicesToClose.emplace(dev.get());
+ if (shouldReopenDevice(*dev)) {
+ mDeviceIdsToReopen.emplace_back(dev->id);
}
}
-
- for (auto* device : devicesToClose) {
- closeDeviceLocked(*device);
- }
- for (const auto& path : devicesToOpen) {
- openDeviceLocked(path);
- }
}
void EventHub::createVirtualKeyboardLocked() {
@@ -2817,9 +2875,23 @@ void EventHub::closeDeviceLocked(Device& device) {
releaseControllerNumberLocked(device.controllerNumber);
device.controllerNumber = 0;
device.close();
- mClosingDevices.push_back(std::move(mDevices[device.id]));
- mDevices.erase(device.id);
+ // Try to remove this device from mDevices.
+ if (auto it = mDevices.find(device.id); it != mDevices.end()) {
+ mClosingDevices.push_back(std::move(mDevices[device.id]));
+ mDevices.erase(device.id);
+ return;
+ }
+
+ // Try to remove this device from mOpeningDevices.
+ if (auto it = std::find_if(mOpeningDevices.begin(), mOpeningDevices.end(),
+ [&device](auto& d) { return d->id == device.id; });
+ it != mOpeningDevices.end()) {
+ mOpeningDevices.erase(it);
+ return;
+ }
+
+ LOG_ALWAYS_FATAL("%s: Device with id %d was not found!", __func__, device.id);
}
base::Result<void> EventHub::readNotifyLocked() {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 5e42d57f06..594dcba144 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -136,6 +136,8 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) {
} else {
dump += "<none>\n";
}
+ dump += StringPrintf(INDENT2 "SysfsRootPath: %s\n",
+ mSysfsRootPath.empty() ? "<none>" : mSysfsRootPath.c_str());
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: %s\n",
inputEventSourceToString(deviceInfo.getSources()).c_str());
@@ -195,6 +197,10 @@ void InputDevice::addEmptyEventHubDevice(int32_t eventHubId) {
DevicePair& devicePair = mDevices[eventHubId];
devicePair.second = createMappers(*devicePair.first, readerConfig);
+ if (mSysfsRootPath.empty()) {
+ mSysfsRootPath = devicePair.first->getSysfsRootPath();
+ }
+
// Must change generation to flag this device as changed
bumpGeneration();
return out;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 58df692b3e..74ef972848 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -917,8 +917,19 @@ bool InputReader::canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId di
return *associatedDisplayId == displayId;
}
+std::filesystem::path InputReader::getSysfsRootPath(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+
+ const InputDevice* device = findInputDeviceLocked(deviceId);
+ if (!device) {
+ return {};
+ }
+ return device->getSysfsRootPath();
+}
+
void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) {
mEventHub->sysfsNodeChanged(sysfsNodePath);
+ mEventHub->wake();
}
DeviceId InputReader::getLastUsedInputDeviceId() {
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index adbfdebfb0..9f3a57c265 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -31,6 +31,7 @@
#include <batteryservice/BatteryService.h>
#include <ftl/flags.h>
+#include <input/BlockingQueue.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/KeyCharacterMap.h>
@@ -398,6 +399,9 @@ public:
/* Disable an input device. Closes file descriptor to that device. */
virtual status_t disableDevice(int32_t deviceId) = 0;
+ /* Gets the sysfs root path for this device. Returns an empty path if there is none. */
+ virtual std::filesystem::path getSysfsRootPath(int32_t deviceId) const = 0;
+
/* Sysfs node changed. Reopen the Eventhub device if any new Peripheral like Light, Battery,
* etc. is detected. */
virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
@@ -613,6 +617,8 @@ public:
status_t disableDevice(int32_t deviceId) override final;
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override final;
+
void sysfsNodeChanged(const std::string& sysfsNodePath) override final;
bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override final;
@@ -775,6 +781,8 @@ private:
void addDeviceInputInotify();
void addDeviceInotify();
+ void handleSysfsNodeChangeNotificationsLocked() REQUIRES(mLock);
+
// Protect all internal state.
mutable std::mutex mLock;
@@ -807,6 +815,7 @@ private:
bool mNeedToReopenDevices;
bool mNeedToScanDevices;
std::vector<std::string> mExcludedDevices;
+ std::vector<int32_t> mDeviceIdsToReopen;
int mEpollFd;
int mINotifyFd;
@@ -824,6 +833,10 @@ private:
size_t mPendingEventCount;
size_t mPendingEventIndex;
bool mPendingINotify;
+
+ // The sysfs node change notifications that have been sent to EventHub.
+ // Enqueuing notifications does not require the lock to be held.
+ BlockingQueue<std::string> mChangedSysfsNodeNotifications;
};
} // namespace android
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4744dd0e4e..a1a8891940 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -81,6 +81,8 @@ public:
inline virtual KeyboardType getKeyboardType() const { return mKeyboardType; }
+ inline std::filesystem::path getSysfsRootPath() const { return mSysfsRootPath; }
+
bool isEnabled();
void dump(std::string& dump, const std::string& eventHubDevStr);
@@ -209,6 +211,7 @@ private:
bool mHasMic;
bool mDropUntilNextSync;
std::optional<bool> mShouldSmoothScroll;
+ std::filesystem::path mSysfsRootPath;
typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
@@ -471,6 +474,9 @@ public:
inline void setKeyboardType(KeyboardType keyboardType) {
return mDevice.setKeyboardType(keyboardType);
}
+ inline std::filesystem::path getSysfsRootPath() const {
+ return mEventHub->getSysfsRootPath(mId);
+ }
inline bool setKernelWakeEnabled(bool enabled) {
return mEventHub->setKernelWakeEnabled(mId, enabled);
}
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 6a259373df..9212d37966 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -118,6 +118,8 @@ public:
std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override;
+
void sysfsNodeChanged(const std::string& sysfsNodePath) override;
DeviceId getLastUsedInputDeviceId() override;
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index e72c440480..8987b99525 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -627,6 +627,14 @@ void FakeEventHub::setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath)
device->sysfsRootPath = sysfsRootPath;
}
+std::filesystem::path FakeEventHub::getSysfsRootPath(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ return {};
+ }
+ return device->sysfsRootPath;
+}
+
void FakeEventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
int32_t foundDeviceId = -1;
Device* foundDevice = nullptr;
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 143b93b245..1cd33c1c98 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -222,6 +222,7 @@ private:
std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override;
std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
int32_t deviceId, int32_t lightId) const override;
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override;
void sysfsNodeChanged(const std::string& sysfsNodePath) override;
void dump(std::string&) const override {}
void monitor() const override {}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f7dcd6c6e3..298ba4209a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1176,7 +1176,9 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) {
// Transfer touch from the middle window to the right window.
ASSERT_TRUE(mDispatcher->transferTouchGesture(middleForegroundWindow->getToken(),
- rightForegroundWindow->getToken()));
+ rightForegroundWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false));
middleForegroundWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
@@ -1208,6 +1210,148 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) {
}
/**
+ * If a window has requested touch to be transferred, only the current pointers should be
+ * transferred.
+ *
+ * Subsequent pointers should still go through the normal hit testing.
+ *
+ * In this test, we are invoking 'transferTouchGesture' with the parameter 'transferEntireGesture'
+ * set to true, but that value doesn't make any difference, since the flag is disabled. This test
+ * will be removed once that flag is fully rolled out.
+ */
+TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture_legacy) {
+ SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, false);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> topWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ sp<FakeWindowHandle> bottomWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Transfer touch from the top window to the bottom window.
+ // The actual value of parameter 'transferEntireGesture' doesn't matter, since the flag is off.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/true));
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // When the second pointer goes down, it will hit the top window, and should be delivered there
+ // as a new pointer.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}};
+ bottomWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers)));
+ bottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has requested touch to be transferred, the current pointers should be transferred.
+ *
+ * If the window did not request the "entire gesture" to be transferred, subsequent pointers should
+ * still go through the normal hit testing.
+ */
+TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture) {
+ SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> topWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ sp<FakeWindowHandle> bottomWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Transfer touch from the top window to the bottom window.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false));
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // When the second pointer goes down, it will hit the top window, and should be delivered there
+ // because "entire gesture" was not requested to be transferred when the original call to
+ // 'transferTouchGesture' was made.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}};
+ bottomWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers)));
+ bottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has requested touch to be transferred, all subsequent pointers from the same gesture
+ * should be transferred if 'transferEntireGesture' was set to 'true'.
+ *
+ * In this test, there are 2 windows - one above and one below.
+ * First pointer goes to the top window. Then top window calls 'transferTouch' upon receiving
+ * ACTION_DOWN and transfers touch to the bottom window.
+ * Subsequent pointers from the same gesture should still be forwarded to the bottom window,
+ * as long as they first land into the top window.
+ */
+TEST_F(InputDispatcherTest, TouchTransferSendsEntireGesture) {
+ SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> topWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ sp<FakeWindowHandle> bottomWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Transfer touch from the top window to the bottom window.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/true));
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // When the second pointer goes down, it will hit the top window, but since the top window has
+ // requested the whole gesture to be transferred, it should be redirected to the bottom window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60))
+ .build());
+
+ bottomWindow->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+}
+
+/**
* A single window that receives touch (on top), and a wallpaper window underneath it.
* The top window gets a multitouch gesture.
* Ensure that wallpaper gets the same gesture.
@@ -6608,7 +6752,8 @@ TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinate
// The pointer is transferred to the second window, and the second window receives it in the
// correct coordinate space.
- mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken());
+ mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(),
+ /*isDragDrop=*/false, /*transferEntireGesture=*/false);
firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400)));
}
@@ -7144,7 +7289,8 @@ INSTANTIATE_TEST_SUITE_P(
[&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> from,
sp<IBinder> to) {
return dispatcher->transferTouchGesture(from, to,
- /*isDragAndDrop=*/false);
+ /*isDragAndDrop=*/false,
+ /*transferEntireGesture=*/false);
}));
TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
@@ -7184,7 +7330,8 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
secondWindow->consumeMotionDown();
// Transfer touch to the second window
- mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken());
+ mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(),
+ /*isDragDrop=*/false, /*transferEntireGesture=*/false);
// The first window gets cancel and the new gets pointer down (it already saw down)
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionPointerDown(1, ui::LogicalDisplayId::DEFAULT,
@@ -7318,7 +7465,9 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
// Transfer touch
ASSERT_TRUE(mDispatcher->transferTouchGesture(firstWindowInPrimary->getToken(),
- secondWindowInPrimary->getToken()));
+ secondWindowInPrimary->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false));
// The first window gets cancel.
firstWindowInPrimary->consumeMotionCancel();
secondWindowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
@@ -12610,7 +12759,8 @@ protected:
// Transfer touch focus to the drag window
bool transferred =
mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(),
- /*isDragDrop=*/true);
+ /*isDragDrop=*/true,
+ /*transferEntireGesture=*/false);
if (transferred) {
targetWindow->consumeMotionCancel(dragStartDisplay);
mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
@@ -15182,7 +15332,9 @@ TEST_P(TransferOrDontTransferFixture, TouchDownAndMouseHover) {
if (GetParam()) {
// Call transferTouchGesture
const bool transferred =
- mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken());
+ mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false);
ASSERT_TRUE(transferred);
mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
@@ -15264,7 +15416,9 @@ TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevi
if (GetParam()) {
// Call transferTouchGesture
const bool transferred =
- mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken());
+ mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false);
ASSERT_TRUE(transferred);
mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 43d2378f61..d1d8192395 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -611,8 +611,10 @@ protected:
}
void addDevice(int32_t eventHubId, const std::string& name,
- ftl::Flags<InputDeviceClass> classes, const PropertyMap* configuration) {
+ ftl::Flags<InputDeviceClass> classes, const PropertyMap* configuration,
+ std::string sysfsRootPath = "") {
mFakeEventHub->addDevice(eventHubId, name, classes);
+ mFakeEventHub->setSysfsRootPath(eventHubId, sysfsRootPath);
if (configuration) {
mFakeEventHub->addConfigurationMap(eventHubId, configuration);
@@ -664,6 +666,18 @@ TEST_F(InputReaderTest, PolicyGetInputDevices) {
ASSERT_EQ(0U, inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, GetSysfsRootPath) {
+ constexpr std::string SYSFS_ROOT = "xyz";
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr, SYSFS_ROOT));
+
+ // Should also have received a notification describing the new input device.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+ InputDeviceInfo inputDevice = mFakePolicy->getInputDevices()[0];
+
+ ASSERT_EQ(SYSFS_ROOT, mReader->getSysfsRootPath(inputDevice.getId()).string());
+}
+
TEST_F(InputReaderTest, InputDeviceRecreatedOnSysfsNodeChanged) {
ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
mFakeEventHub->setSysfsRootPath(1, "xyz");
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index d4e4bb00f7..8eded2e279 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -181,6 +181,7 @@ public:
MOCK_METHOD(bool, isDeviceEnabled, (int32_t deviceId), (const, override));
MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override));
MOCK_METHOD(status_t, disableDevice, (int32_t deviceId), (override));
+ MOCK_METHOD(std::filesystem::path, getSysfsRootPath, (int32_t deviceId), (const, override));
MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override));
MOCK_METHOD(bool, setKernelWakeEnabled, (int32_t deviceId, bool enabled), (override));
};
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 6be922dfdb..0c8ba50776 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -165,6 +165,10 @@ public:
return reader->getBluetoothAddress(deviceId);
}
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const {
+ return reader->getSysfsRootPath(deviceId);
+ }
+
void sysfsNodeChanged(const std::string& sysfsNodePath) {
reader->sysfsNodeChanged(sysfsNodePath);
}
@@ -297,6 +301,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
},
[&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); },
+ [&]() -> void { reader->getSysfsRootPath(fdp->ConsumeIntegral<int32_t>()); },
})();
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 9a5903981b..f619c48f3f 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -296,6 +296,7 @@ public:
bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); }
status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override { return {}; }
void sysfsNodeChanged(const std::string& sysfsNodePath) override {}
bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override {
return mFdp->ConsumeBool();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 39748b8417..acd915464e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -37,6 +37,9 @@ public:
// Gets the DisplayId for the display
virtual DisplayId getId() const = 0;
+ // True if the display has a secure layer
+ virtual bool hasSecureLayers() const = 0;
+
// True if the display is secure
virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index ec87acc372..6ec7be885a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -68,7 +68,9 @@ public:
// compositionengine::Display overrides
DisplayId getId() const override;
+ bool hasSecureLayers() const override;
bool isSecure() const override;
+ void setSecure(bool secure) override;
bool isVirtual() const override;
void disconnect() override;
void createDisplayColorProfile(
@@ -76,7 +78,6 @@ public:
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
void applyDisplayBrightness(bool applyImmediately) override;
- void setSecure(bool secure) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 46cb95ef25..2d51b71b09 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -32,6 +32,7 @@ public:
virtual ~Display();
MOCK_CONST_METHOD0(getId, DisplayId());
+ MOCK_CONST_METHOD0(hasSecureLayers, bool());
MOCK_CONST_METHOD0(isSecure, bool());
MOCK_METHOD1(setSecure, void(bool));
MOCK_CONST_METHOD0(isVirtual, bool());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 5a546777f4..531cab6938 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -70,6 +70,14 @@ DisplayId Display::getId() const {
return asDisplayId(mIdVariant);
}
+bool Display::hasSecureLayers() const {
+ const auto layers = getOutputLayersOrderedByZ();
+ return std::any_of(layers.begin(), layers.end(), [](const auto& layer) {
+ const auto* state = layer->getLayerFE().getCompositionState();
+ return state && state->isSecure;
+ });
+}
+
bool Display::isSecure() const {
return getState().isSecure;
}
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index 87a677cd58..7c19885231 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -46,11 +46,21 @@ DisplayModeController::Display::Display(DisplaySnapshotRef snapshot,
renderRateFpsTrace(concatId("RenderRateFps")),
hasDesiredModeTrace(concatId("HasDesiredMode"), false) {}
+DisplayModeController::DisplayModeController() {
+ using namespace std::string_literals;
+ mSupportsHdcp = base::GetBoolProperty("debug.sf.hdcp_support"s, false);
+}
+
void DisplayModeController::registerDisplay(PhysicalDisplayId displayId,
DisplaySnapshotRef snapshotRef,
RefreshRateSelectorPtr selectorPtr) {
+ DisplayPtr displayPtr = std::make_unique<Display>(snapshotRef, selectorPtr);
+ // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced
+ displayPtr->setSecure(!supportsHdcp() ||
+ snapshotRef.get().connectionType() ==
+ ui::DisplayConnectionType::Internal);
std::lock_guard lock(mDisplayLock);
- mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr));
+ mDisplays.emplace_or_replace(displayId, std::move(displayPtr));
}
void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
@@ -58,11 +68,14 @@ void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
scheduler::RefreshRateSelector::Config config) {
const auto& snapshot = snapshotRef.get();
const auto displayId = snapshot.displayId();
-
+ DisplayPtr displayPtr =
+ std::make_unique<Display>(snapshotRef, snapshot.displayModes(), activeModeId, config);
+ // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced
+ displayPtr->setSecure(!supportsHdcp() ||
+ snapshotRef.get().connectionType() ==
+ ui::DisplayConnectionType::Internal);
std::lock_guard lock(mDisplayLock);
- mDisplays.emplace_or_replace(displayId,
- std::make_unique<Display>(snapshotRef, snapshot.displayModes(),
- activeModeId, config));
+ mDisplays.emplace_or_replace(displayId, std::move(displayPtr));
}
void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
@@ -304,5 +317,30 @@ auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId)
return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
}
+bool DisplayModeController::supportsHdcp() const {
+ return mSupportsHdcp && FlagManager::getInstance().hdcp_level_hal() &&
+ FlagManager::getInstance().hdcp_negotiation();
+}
+
+void DisplayModeController::startHdcpNegotiation(PhysicalDisplayId displayId) {
+ using aidl::android::hardware::drm::HdcpLevel;
+ using aidl::android::hardware::drm::HdcpLevels;
+ constexpr HdcpLevels kLevels = {.connectedLevel = HdcpLevel::HDCP_V2_1,
+ .maxLevel = HdcpLevel::HDCP_V2_3};
+
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+ if (displayPtr->hdcpState == HdcpState::Desired) {
+ const auto status = mComposerPtr->startHdcpNegotiation(displayId, kLevels);
+ displayPtr->hdcpState = (status == NO_ERROR) ? HdcpState::Enabled : HdcpState::Undesired;
+ }
+}
+
+void DisplayModeController::setSecure(PhysicalDisplayId displayId, bool secure) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+ displayPtr->setSecure(secure);
+}
+
#pragma clang diagnostic pop
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index af3e909bcf..f20434857a 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -46,7 +46,7 @@ class DisplayModeController {
public:
using ActiveModeListener = ftl::Function<void(PhysicalDisplayId, Fps vsyncRate, Fps renderFps)>;
- DisplayModeController() = default;
+ DisplayModeController();
void setHwComposer(HWComposer* composerPtr) { mComposerPtr = composerPtr; }
void setActiveModeListener(const ActiveModeListener& listener) {
@@ -109,7 +109,16 @@ public:
KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const
REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void setSecure(PhysicalDisplayId displayId, bool secure) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ bool supportsHdcp() const;
+
+ void startHdcpNegotiation(PhysicalDisplayId displayId) REQUIRES(kMainThreadContext);
+
private:
+ enum class HdcpState { Undesired, Desired, Enabled };
+
struct Display {
template <size_t N>
std::string concatId(const char (&)[N]) const;
@@ -120,6 +129,11 @@ private:
: Display(snapshot,
std::make_shared<scheduler::RefreshRateSelector>(std::move(modes),
activeModeId, config)) {}
+
+ void setSecure(bool secure) {
+ hdcpState = secure ? HdcpState::Undesired : HdcpState::Desired;
+ }
+
const DisplaySnapshotRef snapshot;
const RefreshRateSelectorPtr selectorPtr;
@@ -135,6 +149,8 @@ private:
bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false;
+
+ HdcpState hdcpState = HdcpState::Desired;
};
using DisplayPtr = std::unique_ptr<Display>;
@@ -153,6 +169,8 @@ private:
mutable std::mutex mDisplayLock;
ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayPtr> mDisplays GUARDED_BY(mDisplayLock);
+
+ bool mSupportsHdcp = false;
};
} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index de7d455fa4..bad5e2e3b5 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -50,6 +50,17 @@ namespace android {
namespace hal = hardware::graphics::composer::hal;
+namespace gui {
+inline std::string_view to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+ switch (optimizationPolicy) {
+ case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
+ return "optimizeForPower";
+ case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
+ return "optimizeForPerformance";
+ }
+}
+} // namespace gui
+
DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay)
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1b14145147..7d7c8adb7b 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -67,17 +67,6 @@ namespace display {
class DisplaySnapshot;
} // namespace display
-namespace gui {
-inline const char* to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
- switch (optimizationPolicy) {
- case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
- return "optimizeForPower";
- case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
- return "optimizeForPerformance";
- }
-}
-} // namespace gui
-
class DisplayDevice : public RefBase {
public:
constexpr static float sDefaultMinLumiance = 0.0;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index d547af98ea..8ead09ca19 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -328,9 +328,7 @@ std::string AidlComposer::dumpDebugInfo() {
std::string str;
// Use other thread to read pipe to prevent
// pipe is full, making HWC be blocked in writing.
- std::thread t([&]() {
- base::ReadFdToString(pipefds[0], &str);
- });
+ std::thread t([&]() { base::ReadFdToString(pipefds[0], &str); });
const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0);
// Close the write-end of the pipe to make sure that when reading from the
// read-end we will get eof instead of blocking forever
@@ -1720,6 +1718,18 @@ Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, Pictu
return error;
}
+Error AidlComposer::startHdcpNegotiation(Display display,
+ const aidl::android::hardware::drm::HdcpLevels& levels) {
+ const auto status =
+ mAidlComposerClient->startHdcpNegotiation(translate<int64_t>(display), levels);
+ if (!status.isOk()) {
+ ALOGE("startHdcpNegotiation failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ return Error::NONE;
+}
+
Error AidlComposer::getLuts(Display display, const std::vector<sp<GraphicBuffer>>& buffers,
std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) {
std::vector<aidl::android::hardware::graphics::composer3::Buffer> aidlBuffers;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 5fcc8b00ba..b84d39a2c3 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -250,6 +250,7 @@ public:
Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
Error setDisplayPictureProfileId(Display, PictureProfileId id) override;
Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override;
+ Error startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) override;
Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 018ee6e461..c55893106f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -229,14 +229,13 @@ public:
virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
Display display) = 0;
virtual Error getRenderIntents(Display display, ColorMode colorMode,
- std::vector<RenderIntent>* outRenderIntents) = 0;
+ std::vector<RenderIntent>* outRenderIntents) = 0;
virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0;
// Composer HAL 2.3
virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
std::vector<uint8_t>* outData) = 0;
- virtual Error setLayerColorTransform(Display display, Layer layer,
- const float* matrix) = 0;
+ virtual Error setLayerColorTransform(Display display, Layer layer, const float* matrix) = 0;
virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
Dataspace* outDataspace,
uint8_t* outComponentMask) = 0;
@@ -318,6 +317,8 @@ public:
virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0;
virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0;
virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0;
+ virtual Error startHdcpNegotiation(Display display,
+ const aidl::android::hardware::drm::HdcpLevels& levels) = 0;
virtual Error getLuts(Display display, const std::vector<sp<GraphicBuffer>>&,
std::vector<V3_0::Luts>*) = 0;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8d16a6bbb3..fd0bf7386b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -674,6 +674,11 @@ Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) {
return static_cast<Error>(error);
}
+Error Display::startHdcpNegotiation(const aidl::android::hardware::drm::HdcpLevels& levels) {
+ const auto error = mComposer.startHdcpNegotiation(mId, levels);
+ return static_cast<Error>(error);
+}
+
Error Display::getLuts(const std::vector<sp<GraphicBuffer>>& buffers,
std::vector<aidl::android::hardware::graphics::composer3::Luts>* outLuts) {
const auto error = mComposer.getLuts(mId, buffers, outLuts);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 7c1f8e3da0..3f518211c4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -203,6 +203,8 @@ public:
[[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0;
[[nodiscard]] virtual hal::Error setPictureProfileHandle(
const PictureProfileHandle& handle) = 0;
+ [[nodiscard]] virtual hal::Error startHdcpNegotiation(
+ const aidl::android::hardware::drm::HdcpLevels& levels) = 0;
[[nodiscard]] virtual hal::Error getLuts(
const std::vector<android::sp<android::GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0;
@@ -291,6 +293,8 @@ public:
hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override;
hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override;
hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override;
+ hal::Error startHdcpNegotiation(
+ const aidl::android::hardware::drm::HdcpLevels& levels) override;
hal::Error getLuts(const std::vector<android::sp<android::GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 14088a6428..758d924a53 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -560,7 +560,7 @@ status_t HWComposer::getDeviceCompositionChanges(
if (!hasChangesError(error)) {
RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
}
- if (state == 1) { //Present Succeeded.
+ if (state == 1) { // Present Succeeded.
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
error = hwcDisplay->getReleaseFences(&releaseFences);
displayData.releaseFences = std::move(releaseFences);
@@ -824,8 +824,8 @@ mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Datasp
RETURN_IF_INVALID_DISPLAY(displayId, {});
mat4 matrix;
- auto error = mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace,
- &matrix);
+ auto error =
+ mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, &matrix);
RETURN_IF_HWC_ERROR(error, displayId, {});
return matrix;
}
@@ -1054,6 +1054,15 @@ status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId,
return NO_ERROR;
}
+status_t HWComposer::startHdcpNegotiation(PhysicalDisplayId displayId,
+ const aidl::android::hardware::drm::HdcpLevels& levels) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto error = hwcDisplay->startHdcpNegotiation(levels);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
status_t HWComposer::getLuts(
PhysicalDisplayId displayId, const std::vector<sp<GraphicBuffer>>& buffers,
std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 472411cf99..fcecd23bca 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -327,6 +327,8 @@ public:
virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0;
virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
const PictureProfileHandle& handle) = 0;
+ virtual status_t startHdcpNegotiation(PhysicalDisplayId,
+ const aidl::android::hardware::drm::HdcpLevels&) = 0;
virtual status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0;
};
@@ -494,6 +496,8 @@ public:
int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override;
status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
const android::PictureProfileHandle& profile) override;
+ status_t startHdcpNegotiation(PhysicalDisplayId,
+ const aidl::android::hardware::drm::HdcpLevels&) override;
status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index a010353423..5e03f30304 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1230,15 +1230,16 @@ Error HidlComposer::getDisplayCapabilities(Display display,
translate<DisplayCapability>(tmpCaps);
});
} else {
- mClient_2_3
- ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
- error = static_cast<V2_4::Error>(tmpError);
- if (error != V2_4::Error::NONE) {
- return;
- }
+ mClient_2_3->getDisplayCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpCaps) {
+ error = static_cast<V2_4::Error>(tmpError);
+ if (error != V2_4::Error::NONE) {
+ return;
+ }
- *outCapabilities = translate<DisplayCapability>(tmpCaps);
- });
+ *outCapabilities =
+ translate<DisplayCapability>(tmpCaps);
+ });
}
return static_cast<Error>(error);
@@ -1460,6 +1461,10 @@ Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) {
return Error::UNSUPPORTED;
}
+Error HidlComposer::startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) {
return Error::UNSUPPORTED;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 86ca4b1782..d3874e4889 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -363,6 +363,7 @@ public:
Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
Error setDisplayPictureProfileId(Display, PictureProfileId) override;
Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override;
+ Error startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) override;
Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index da536b6660..00ec863bd9 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -54,7 +54,8 @@ LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnl
mChildren = hierarchy.mChildren;
}
-void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& traversalPath,
+void LayerHierarchy::traverse(const Visitor& visitor,
+ const LayerHierarchy::TraversalPath& traversalPath,
uint32_t depth) const {
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
"Cycle detected in LayerHierarchy::traverse. See "
@@ -70,14 +71,13 @@ void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalP
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(traversalPath.hasRelZLoop(), "Found relative z loop layerId:%d",
traversalPath.invalidRelativeRootId);
for (auto& [child, childVariant] : mChildren) {
- ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
- childVariant);
- child->traverse(visitor, traversalPath, depth + 1);
+ child->traverse(visitor, traversalPath.makeChild(child->mLayer->id, childVariant),
+ depth + 1);
}
}
void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
- LayerHierarchy::TraversalPath& traversalPath) const {
+ const LayerHierarchy::TraversalPath& traversalPath) const {
bool traverseThisLayer = (mLayer != nullptr);
for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
auto& [child, childVariant] = *it;
@@ -91,9 +91,7 @@ void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
if (childVariant == LayerHierarchy::Variant::Detached) {
continue;
}
- ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
- childVariant);
- child->traverseInZOrder(visitor, traversalPath);
+ child->traverseInZOrder(visitor, traversalPath.makeChild(child->mLayer->id, childVariant));
}
if (traverseThisLayer) {
@@ -568,42 +566,23 @@ std::string LayerHierarchy::TraversalPath::toString() const {
return ss.str();
}
-// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
-// of scope the TraversalPath is reset to its original state.
-LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
- uint32_t layerId,
- LayerHierarchy::Variant variant)
- : mTraversalPath(traversalPath), mParentPath(traversalPath) {
- // Update the traversal id with the child layer id and variant. Parent id and variant are
- // stored to reset the id upon destruction.
- traversalPath.id = layerId;
- traversalPath.variant = variant;
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::makeChild(
+ uint32_t layerId, LayerHierarchy::Variant variant) const {
+ TraversalPath child{*this};
+ child.id = layerId;
+ child.variant = variant;
if (LayerHierarchy::isMirror(variant)) {
- traversalPath.mirrorRootIds.emplace_back(mParentPath.id);
+ child.mirrorRootIds.emplace_back(id);
} else if (variant == LayerHierarchy::Variant::Relative) {
- if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
- layerId) != traversalPath.relativeRootIds.end()) {
- traversalPath.invalidRelativeRootId = layerId;
+ if (std::find(relativeRootIds.begin(), relativeRootIds.end(), layerId) !=
+ relativeRootIds.end()) {
+ child.invalidRelativeRootId = layerId;
}
- traversalPath.relativeRootIds.emplace_back(layerId);
+ child.relativeRootIds.emplace_back(layerId);
} else if (variant == LayerHierarchy::Variant::Detached) {
- traversalPath.detached = true;
+ child.detached = true;
}
-}
-LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
- // Reset the traversal id to its original parent state using the state that was saved in
- // the constructor.
- if (LayerHierarchy::isMirror(mTraversalPath.variant)) {
- mTraversalPath.mirrorRootIds.pop_back();
- } else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
- mTraversalPath.relativeRootIds.pop_back();
- }
- if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
- mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
- }
- mTraversalPath.id = mParentPath.id;
- mTraversalPath.variant = mParentPath.variant;
- mTraversalPath.detached = mParentPath.detached;
+ return child;
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 4fdbae1831..c8c6b4df15 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -104,6 +104,8 @@ public:
TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; }
+ TraversalPath makeChild(uint32_t layerId, LayerHierarchy::Variant variant) const;
+
bool operator==(const TraversalPath& other) const {
return id == other.id && mirrorRootIds == other.mirrorRootIds;
}
@@ -122,18 +124,6 @@ public:
}
};
- // Helper class to add nodes to an existing traversal id and removes the
- // node when it goes out of scope.
- class ScopedAddToTraversalPath {
- public:
- ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
- LayerHierarchy::Variant variantArg);
- ~ScopedAddToTraversalPath();
-
- private:
- TraversalPath& mTraversalPath;
- TraversalPath mParentPath;
- };
LayerHierarchy(RequestedLayerState* layer);
// Visitor function that provides the hierarchy node and a traversal id which uniquely
@@ -191,8 +181,9 @@ private:
void removeChild(LayerHierarchy*);
void sortChildrenByZOrder();
void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
- void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
- void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent,
+ void traverseInZOrder(const Visitor& visitor,
+ const LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, const LayerHierarchy::TraversalPath& parent,
uint32_t depth = 0) const;
void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
bool isLastChild, bool includeMirroredHierarchy) const;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 91b54af5be..e3526d9de0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -447,15 +447,14 @@ void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
if (args.root.getLayer()) {
// The hierarchy can have a root layer when used for screenshots otherwise, it will have
// multiple children.
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
- LayerHierarchy::Variant::Attached);
- updateSnapshotsInHierarchy(args, args.root, root, rootSnapshot, /*depth=*/0);
+ LayerHierarchy::TraversalPath childPath =
+ root.makeChild(args.root.getLayer()->id, LayerHierarchy::Variant::Attached);
+ updateSnapshotsInHierarchy(args, args.root, childPath, rootSnapshot, /*depth=*/0);
} else {
for (auto& [childHierarchy, variant] : args.root.mChildren) {
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
- childHierarchy->getLayer()->id,
- variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, rootSnapshot, /*depth=*/0);
+ LayerHierarchy::TraversalPath childPath =
+ root.makeChild(childHierarchy->getLayer()->id, variant);
+ updateSnapshotsInHierarchy(args, *childHierarchy, childPath, rootSnapshot, /*depth=*/0);
}
}
@@ -520,7 +519,7 @@ void LayerSnapshotBuilder::update(const Args& args) {
const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
const Args& args, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
+ const LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
int depth) {
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
"Cycle detected in LayerSnapshotBuilder. See "
@@ -549,12 +548,10 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
bool childHasValidFrameRate = false;
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
- childHierarchy->getLayer()->id,
- variant);
+ LayerHierarchy::TraversalPath childPath =
+ traversalPath.makeChild(childHierarchy->getLayer()->id, variant);
const LayerSnapshot& childSnapshot =
- updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
- depth + 1);
+ updateSnapshotsInHierarchy(args, *childHierarchy, childPath, *snapshot, depth + 1);
updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(),
args, &childHasValidFrameRate);
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 486cb33959..94b7e5fa5a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -106,9 +106,10 @@ private:
void updateSnapshots(const Args& args);
- const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath,
- const LayerSnapshot& parentSnapshot, int depth);
+ const LayerSnapshot& updateSnapshotsInHierarchy(
+ const Args&, const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
+ int depth);
void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 84b1a73e0b..280d66e12a 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -278,10 +278,9 @@ LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::with(
stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
continue;
}
- frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
- child->getLayer()->id,
- variant);
- LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child, path);
+ LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child,
+ path.makeChild(child->getLayer()->id,
+ variant));
}
// fill in relative and parent info
@@ -338,7 +337,8 @@ LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::withOffscreenL
}
frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot(
- frontend::LayerHierarchy::TraversalPath& path, const frontend::RequestedLayerState& layer) {
+ const frontend::LayerHierarchy::TraversalPath& path,
+ const frontend::RequestedLayerState& layer) {
frontend::LayerSnapshot* snapshot = mSnapshotBuilder.getSnapshot(path);
if (snapshot) {
return snapshot;
@@ -349,7 +349,7 @@ frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot(
}
void LayerProtoFromSnapshotGenerator::writeHierarchyToProto(
- const frontend::LayerHierarchy& root, frontend::LayerHierarchy::TraversalPath& path) {
+ const frontend::LayerHierarchy& root, const frontend::LayerHierarchy::TraversalPath& path) {
using Variant = frontend::LayerHierarchy::Variant;
perfetto::protos::LayerProto* layerProto = mLayersProto.add_layers();
const frontend::RequestedLayerState& layer = *root.getLayer();
@@ -362,10 +362,8 @@ void LayerProtoFromSnapshotGenerator::writeHierarchyToProto(
LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
for (const auto& [child, variant] : root.mChildren) {
- frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
- child->getLayer()->id,
- variant);
- frontend::LayerSnapshot* childSnapshot = getSnapshot(path, layer);
+ frontend::LayerSnapshot* childSnapshot =
+ getSnapshot(path.makeChild(child->getLayer()->id, variant), layer);
if (variant == Variant::Attached || variant == Variant::Detached ||
frontend::LayerHierarchy::isMirror(variant)) {
mChildToParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
@@ -388,10 +386,7 @@ void LayerProtoFromSnapshotGenerator::writeHierarchyToProto(
if (variant == Variant::Detached) {
continue;
}
- frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
- child->getLayer()->id,
- variant);
- writeHierarchyToProto(*child, path);
+ writeHierarchyToProto(*child, path.makeChild(child->getLayer()->id, variant));
}
}
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 3ca553a903..28924e45be 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -98,8 +98,8 @@ public:
private:
void writeHierarchyToProto(const frontend::LayerHierarchy& root,
- frontend::LayerHierarchy::TraversalPath& path);
- frontend::LayerSnapshot* getSnapshot(frontend::LayerHierarchy::TraversalPath& path,
+ const frontend::LayerHierarchy::TraversalPath& path);
+ frontend::LayerSnapshot* getSnapshot(const frontend::LayerHierarchy::TraversalPath& path,
const frontend::RequestedLayerState& layer);
const frontend::LayerSnapshotBuilder& mSnapshotBuilder;
diff --git a/services/surfaceflinger/QueuedTransactionState.h b/services/surfaceflinger/QueuedTransactionState.h
index 86683da26c..6a17a0d0cb 100644
--- a/services/surfaceflinger/QueuedTransactionState.h
+++ b/services/surfaceflinger/QueuedTransactionState.h
@@ -25,6 +25,7 @@
#include <common/FlagManager.h>
#include <ftl/flags.h>
#include <gui/LayerState.h>
+#include <gui/TransactionState.h>
#include <system/window.h>
namespace android {
@@ -50,33 +51,26 @@ public:
struct QueuedTransactionState {
QueuedTransactionState() = default;
- QueuedTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- std::vector<ResolvedComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
- bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
- int originUid, uint64_t transactionId,
- std::vector<uint64_t> mergedTransactionIds)
- : frameTimelineInfo(frameTimelineInfo),
- states(std::move(composerStates)),
- displays(displayStates),
- flags(transactionFlags),
- applyToken(applyToken),
- inputWindowCommands(inputWindowCommands),
- desiredPresentTime(desiredPresentTime),
- isAutoTimestamp(isAutoTimestamp),
+ QueuedTransactionState(TransactionState&& transactionState,
+ std::vector<ResolvedComposerState>&& composerStates,
+ std::vector<uint64_t>&& uncacheBufferIds, int64_t postTime,
+ int originPid, int originUid)
+ : frameTimelineInfo(std::move(transactionState.mFrameTimelineInfo)),
+ states(composerStates),
+ displays(std::move(transactionState.mDisplayStates)),
+ flags(transactionState.mFlags),
+ applyToken(transactionState.mApplyToken),
+ inputWindowCommands(std::move(transactionState.mInputWindowCommands)),
+ desiredPresentTime(transactionState.mDesiredPresentTime),
+ isAutoTimestamp(transactionState.mIsAutoTimestamp),
uncacheBufferIds(std::move(uncacheBufferIds)),
postTime(postTime),
- hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks),
+ hasListenerCallbacks(transactionState.mHasListenerCallbacks),
+ listenerCallbacks(std::move(transactionState.mListenerCallbacks)),
originPid(originPid),
originUid(originUid),
- id(transactionId),
- mergedTransactionIds(std::move(mergedTransactionIds)) {}
+ id(transactionState.getId()),
+ mergedTransactionIds(std::move(transactionState.mMergedTransactionIds)) {}
// Invokes `void(const layer_state_t&)` visitor for matching layers.
template <typename Visitor>
@@ -135,7 +129,7 @@ struct QueuedTransactionState {
FrameTimelineInfo frameTimelineInfo;
std::vector<ResolvedComposerState> states;
- Vector<DisplayState> displays;
+ std::vector<DisplayState> displays;
uint32_t flags;
sp<IBinder> applyToken;
InputWindowCommands inputWindowCommands;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index c9d3b31061..16266c6b00 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -124,7 +124,10 @@ void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) {
// Cancel the pending refresh rate change, if any, before updating the phase configuration.
mVsyncModulator->cancelRefreshRateChange();
- mVsyncConfiguration->reset();
+ {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration->reset();
+ }
updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps);
}
@@ -211,7 +214,7 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
.vsyncId = vsyncId,
.expectedVsyncTime = expectedVsyncTime,
.sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
- .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration,
+ .hwcMinWorkDuration = getCurrentVsyncConfigs().hwcMinWorkDuration,
.debugPresentTimeDelay = debugPresentDelay};
ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
@@ -516,12 +519,26 @@ void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refres
if (!isPacesetter) return;
mRefreshRateStats->setRefreshRate(refreshRate);
- mVsyncConfiguration->setRefreshRateFps(refreshRate);
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
- refreshRate.getPeriod());
+ const auto currentConfigs = [=, this] {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration->setRefreshRateFps(refreshRate);
+ return mVsyncConfiguration->getCurrentConfigs();
+ }();
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(currentConfigs), refreshRate.getPeriod());
}
#pragma clang diagnostic pop
+void Scheduler::reloadPhaseConfiguration(Fps refreshRate, Duration minSfDuration,
+ Duration maxSfDuration, Duration appDuration) {
+ const auto currentConfigs = [=, this] {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration = std::make_unique<impl::WorkDuration>(refreshRate, minSfDuration,
+ maxSfDuration, appDuration);
+ return mVsyncConfiguration->getCurrentConfigs();
+ }();
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(currentConfigs), refreshRate.getPeriod());
+}
+
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
}
@@ -896,8 +913,11 @@ void Scheduler::dump(utils::Dumper& dumper) const {
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
- mVsyncConfiguration->dump(dumper.out());
- dumper.eol();
+ {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration->dump(dumper.out());
+ dumper.eol();
+ }
mRefreshRateStats->dump(dumper.out());
dumper.eol();
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 61469c1b46..694856e9ce 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -53,6 +53,7 @@
#include "RefreshRateSelector.h"
#include "SmallAreaDetectionAllowMappings.h"
#include "Utils/Dumper.h"
+#include "VsyncConfiguration.h"
#include "VsyncModulator.h"
#include <FrontEnd/LayerHierarchy.h>
@@ -95,7 +96,7 @@ public:
// TODO: b/241285191 - Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
void setPacesetterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext)
- EXCLUDES(mDisplayLock);
+ EXCLUDES(mDisplayLock, mVsyncConfigLock);
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -188,9 +189,19 @@ public:
}
}
- void updatePhaseConfiguration(PhysicalDisplayId, Fps);
+ void updatePhaseConfiguration(PhysicalDisplayId, Fps) EXCLUDES(mVsyncConfigLock);
+ void reloadPhaseConfiguration(Fps, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration) EXCLUDES(mVsyncConfigLock);
- const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
+ VsyncConfigSet getCurrentVsyncConfigs() const EXCLUDES(mVsyncConfigLock) {
+ std::scoped_lock lock{mVsyncConfigLock};
+ return mVsyncConfiguration->getCurrentConfigs();
+ }
+
+ VsyncConfigSet getVsyncConfigsForRefreshRate(Fps refreshRate) const EXCLUDES(mVsyncConfigLock) {
+ std::scoped_lock lock{mVsyncConfigLock};
+ return mVsyncConfiguration->getConfigsForRefreshRate(refreshRate);
+ }
// Sets the render rate for the scheduler to run at.
void setRenderRate(PhysicalDisplayId, Fps, bool applyImmediately);
@@ -266,7 +277,7 @@ public:
bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
- void dump(utils::Dumper&) const;
+ void dump(utils::Dumper&) const EXCLUDES(mVsyncConfigLock);
void dump(Cycle, std::string&) const;
void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
@@ -348,7 +359,7 @@ private:
// impl::MessageQueue overrides:
void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override
- REQUIRES(kMainThreadContext, mDisplayLock);
+ REQUIRES(kMainThreadContext, mDisplayLock) EXCLUDES(mVsyncConfigLock);
// Used to skip event dispatch before EventThread creation during boot.
// TODO: b/241285191 - Reorder Scheduler initialization to avoid this.
@@ -476,8 +487,9 @@ private:
const FeatureFlags mFeatures;
+ mutable std::mutex mVsyncConfigLock;
// Stores phase offsets configured per refresh rate.
- const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration;
+ std::unique_ptr<VsyncConfiguration> mVsyncConfiguration GUARDED_BY(mVsyncConfigLock);
// Shifts the VSYNC phase during certain transactions and refresh rate changes.
const sp<VsyncModulator> mVsyncModulator;
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 6ae10f3d31..8cbb17c06e 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -362,6 +362,17 @@ WorkDuration::WorkDuration(Fps currentRefreshRate)
validateSysprops();
}
+WorkDuration::WorkDuration(Fps currentRefreshRate, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration)
+ : WorkDuration(currentRefreshRate,
+ /*sfDuration*/ minSfDuration.ns(),
+ /*appDuration*/ appDuration.ns(),
+ /*sfEarlyDuration*/ maxSfDuration.ns(),
+ /*appEarlyDuration*/ appDuration.ns(),
+ /*sfEarlyGpuDuration*/ maxSfDuration.ns(),
+ /*appEarlyGpuDuration*/ appDuration.ns(),
+ /*hwcMinWorkDuration*/ 0) {}
+
WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration,
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index b6cb373b7e..3d8ae3e5d4 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -144,6 +144,8 @@ private:
class WorkDuration : public VsyncConfiguration {
public:
explicit WorkDuration(Fps currentRefreshRate);
+ WorkDuration(Fps currentRefreshRate, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration);
protected:
// Used for unit tests
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ce7a720714..3784cf12c5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -19,7 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceFlinger.h"
@@ -361,7 +361,7 @@ bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime,
return std::abs(expectedPresentTime.ns() -
(lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
}
-} // namespace anonymous
+} // namespace
// ---------------------------------------------------------------------------
@@ -393,7 +393,7 @@ ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::Pixel
LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
- switch(displayColorSetting) {
+ switch (displayColorSetting) {
case DisplayColorSetting::kManaged:
return std::string("Managed");
case DisplayColorSetting::kUnmanaged:
@@ -401,8 +401,7 @@ std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
case DisplayColorSetting::kEnhanced:
return std::string("Enhanced");
default:
- return std::string("Unknown ") +
- std::to_string(static_cast<int>(displayColorSetting));
+ return std::string("Unknown ") + std::to_string(static_cast<int>(displayColorSetting));
}
}
@@ -539,9 +538,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI
}
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
-
- // These are set by the HWC implementation to indicate that they will use the workarounds.
- mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -594,15 +590,14 @@ sp<IBinder> SurfaceFlinger::createVirtualDisplay(
class DisplayToken : public BBinder {
sp<SurfaceFlinger> flinger;
virtual ~DisplayToken() {
- // no more references, this display must be terminated
- Mutex::Autolock _l(flinger->mStateLock);
- flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
- flinger->setTransactionFlags(eDisplayTransactionNeeded);
- }
- public:
- explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
- : flinger(flinger) {
+ // no more references, this display must be terminated
+ Mutex::Autolock _l(flinger->mStateLock);
+ flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
+ flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
+
+ public:
+ explicit DisplayToken(const sp<SurfaceFlinger>& flinger) : flinger(flinger) {}
};
sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this));
@@ -761,7 +756,7 @@ void SurfaceFlinger::bootFinished() {
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
- ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
+ ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)));
mFrameTracer->initialize();
mFrameTimeline->onBootFinished();
@@ -780,8 +775,7 @@ void SurfaceFlinger::bootFinished() {
property_set("service.bootanim.exit", "1");
const int LOGTAG_SF_STOP_BOOTANIM = 60110;
- LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
- ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
@@ -901,8 +895,8 @@ renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur)
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
SFTRACE_CALL();
- ALOGI( "SurfaceFlinger's main thread ready to run. "
- "Initializing graphics H/W...");
+ ALOGI("SurfaceFlinger's main thread ready to run. "
+ "Initializing graphics H/W...");
addTransactionReadyFilters();
Mutex::Autolock lock(mStateLock);
@@ -1125,17 +1119,16 @@ void SurfaceFlinger::readPersistentProperties() {
static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
}
-status_t SurfaceFlinger::getSupportedFrameTimestamps(
- std::vector<FrameEvent>* outSupported) const {
+status_t SurfaceFlinger::getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const {
*outSupported = {
- FrameEvent::REQUESTED_PRESENT,
- FrameEvent::ACQUIRE,
- FrameEvent::LATCH,
- FrameEvent::FIRST_REFRESH_START,
- FrameEvent::LAST_REFRESH_START,
- FrameEvent::GPU_COMPOSITION_DONE,
- FrameEvent::DEQUEUE_READY,
- FrameEvent::RELEASE,
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GPU_COMPOSITION_DONE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE,
};
if (mHasReliablePresentFences) {
@@ -1230,8 +1223,8 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info
outMode.peakRefreshRate = peakFps.getValue();
outMode.vsyncRate = mode->getVsyncRate().getValue();
- const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(
- Fps::fromValue(outMode.peakRefreshRate));
+ const auto vsyncConfigSet =
+ mScheduler->getVsyncConfigsForRefreshRate(Fps::fromValue(outMode.peakRefreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
outMode.group = mode->getGroup();
@@ -2210,7 +2203,6 @@ status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken
}
hdrInfoReporter->addListener(listener);
-
mAddingHDRLayerInfoListener = true;
return OK;
}
@@ -2271,13 +2263,13 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
const auto cycle = [&] {
if (FlagManager::getInstance().deprecate_vsync_sf()) {
ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger,
- "requested unsupported config eVsyncSourceSurfaceFlinger");
+ "requested unsupported config eVsyncSourceSurfaceFlinger");
return scheduler::Cycle::Render;
}
return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
- ? scheduler::Cycle::LastComposite
- : scheduler::Cycle::Render;
+ ? scheduler::Cycle::LastComposite
+ : scheduler::Cycle::Render;
}();
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
@@ -2306,19 +2298,6 @@ void SurfaceFlinger::scheduleSample() {
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- if (timestamp < 0 && vsyncPeriod.has_value()) {
- if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
- const int32_t value = static_cast<int32_t>(-timestamp);
- // one byte is good enough to encode android.hardware.drm.HdcpLevel
- const int32_t maxLevel = (value >> 8) & 0xFF;
- const int32_t connectedLevel = value & 0xFF;
- ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64,
- __func__, connectedLevel, maxLevel, hwcDisplayId);
- updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
- return;
- }
- }
-
SFTRACE_NAME(vsyncPeriod
? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
: ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
@@ -2807,9 +2786,10 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
// setTransactionFlags which will schedule another SF frame. This was if the tracker
// needs to adjust the vsync timeline, it will be done before the next frame.
if (FlagManager::getInstance().vrr_config() && mustComposite) {
- mScheduler->getVsyncSchedule()->getTracker().onFrameBegin(
- pacesetterFrameTarget.expectedPresentTime(),
- pacesetterFrameTarget.lastSignaledFrameTime());
+ mScheduler->getVsyncSchedule()
+ ->getTracker()
+ .onFrameBegin(pacesetterFrameTarget.expectedPresentTime(),
+ pacesetterFrameTarget.lastSignaledFrameTime());
}
if (transactionFlushNeeded()) {
setTransactionFlags(eTransactionFlushNeeded);
@@ -2978,7 +2958,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
auto compositionDisplay = display->getCompositionDisplay();
if (!compositionDisplay->getState().isEnabled) continue;
- for (auto outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) {
+ for (const auto* outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) {
if (outputLayer->getLayerFE().getCompositionState() == nullptr) {
// This is unexpected but instead of crashing, capture traces to disk
// and recover gracefully by forcing CE to rebuild layer stack.
@@ -3058,7 +3038,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
// This should be interpreted to mean that there are 2 cached sets.
// So there are only 2 non skipped layers -- b and s.
// The layers rrc and cc are flattened into layers b and s respectively.
- const LayerFE::HwcLayerDebugState &hwcState = layerFE->getLastHwcState();
+ const LayerFE::HwcLayerDebugState& hwcState = layerFE->getLastHwcState();
if (hwcState.overrideBufferId != prevOverrideBufferId) {
// End the existing run.
if (prevOverrideBufferId) {
@@ -3070,11 +3050,10 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
}
}
- compositionSummary.push_back(
- layerFE->mSnapshot->classifyCompositionForDebug(hwcState));
+ compositionSummary.push_back(layerFE->mSnapshot->classifyCompositionForDebug(hwcState));
if (hwcState.overrideBufferId && !hwcState.wasSkipped) {
- compositionSummary.push_back(':');
+ compositionSummary.push_back(':');
}
prevOverrideBufferId = hwcState.overrideBufferId;
@@ -3105,11 +3084,23 @@ CompositeResultsPerDisplay SurfaceFlinger::composite(
.c_str());
mPowerAdvisor->setCompositedWorkload(compositedWorkload);
- moveSnapshotsFromCompositionArgs(refreshArgs, layers);
SFTRACE_ASYNC_FOR_TRACK_END(WorkloadTracer::TRACK_NAME,
WorkloadTracer::COMPOSITION_TRACE_COOKIE);
SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Post Composition");
SFTRACE_NAME("postComposition");
+
+ if (mDisplayModeController.supportsHdcp()) {
+ for (const auto& [id, _] : frameTargeters) {
+ ftl::FakeGuard guard(mStateLock);
+ if (const auto display = getCompositionDisplayLocked(id)) {
+ if (!display->isSecure() && display->hasSecureLayers()) {
+ mDisplayModeController.startHdcpNegotiation(id);
+ }
+ }
+ }
+ }
+
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
// Send a power hint after presentation is finished.
@@ -3331,8 +3322,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId,
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
const Fps renderRate = pacesetterDisplay->refreshRateSelector().getActiveMode().fps;
- const nsecs_t vsyncPhase =
- mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset;
+ const nsecs_t vsyncPhase = mScheduler->getCurrentVsyncConfigs().late.sfOffset;
const CompositorTiming compositorTiming(vsyncDeadline.ns(), renderRate.getPeriodNsecs(),
vsyncPhase, presentLatency.ns());
@@ -3815,12 +3805,9 @@ std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDispl
.hwcDisplayId = hwcDisplayId,
.port = info.port,
.activeMode = std::move(activeMode)};
- if (mIsHdcpViaNegVsync) {
- state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
- } else {
- // TODO(b/349703362): Remove this when HDCP aidl API becomes ready
- state.isSecure = true; // All physical displays are currently considered secure.
- }
+ // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced
+ state.isSecure = !mDisplayModeController.supportsHdcp() ||
+ connectionType == ui::DisplayConnectionType::Internal;
state.isProtected = true;
state.displayName = std::move(info.name);
state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId);
@@ -4324,37 +4311,35 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
mVisibleWindowIds = std::move(visibleWindowIds);
}
- BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
- windowInfos = std::move(windowInfos),
- displayInfos = std::move(displayInfos),
- inputWindowCommands =
- std::move(mInputWindowCommands),
- inputFlinger = mInputFlinger, this,
- visibleWindowsChanged, vsyncId,
- frameTime]() mutable {
- SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
- if (updateWindowInfo) {
- mWindowInfosListenerInvoker
- ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
- std::move(displayInfos),
- ftl::to_underlying(vsyncId),
- frameTime.ns()},
- std::move(inputWindowCommands.releaseListeners()),
- /* forceImmediateCall= */ visibleWindowsChanged ||
- !inputWindowCommands.getFocusRequests().empty());
- } else {
- // If there are listeners but no changes to input windows, call the listeners
- // immediately.
- for (const auto& listener : inputWindowCommands.getListeners()) {
- if (IInterface::asBinder(listener)->isBinderAlive()) {
- listener->onWindowInfosReported();
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[updateWindowInfo, windowInfos = std::move(windowInfos),
+ displayInfos = std::move(displayInfos),
+ inputWindowCommands = std::move(mInputWindowCommands), inputFlinger = mInputFlinger,
+ this, visibleWindowsChanged, vsyncId, frameTime]() mutable {
+ SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
+ if (updateWindowInfo) {
+ mWindowInfosListenerInvoker
+ ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
+ std::move(displayInfos),
+ ftl::to_underlying(vsyncId),
+ frameTime.ns()},
+ std::move(inputWindowCommands.releaseListeners()),
+ /* forceImmediateCall= */ visibleWindowsChanged ||
+ !inputWindowCommands.getFocusRequests()
+ .empty());
+ } else {
+ // If there are listeners but no changes to input windows, call the listeners
+ // immediately.
+ for (const auto& listener : inputWindowCommands.getListeners()) {
+ if (IInterface::asBinder(listener)->isBinderAlive()) {
+ listener->onWindowInfosReported();
+ }
+ }
}
- }
- }
- for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) {
- inputFlinger->setFocusedWindow(focusRequest);
- }
- }});
+ for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) {
+ inputFlinger->setFocusedWindow(focusRequest);
+ }
+ }});
mInputWindowCommands.clear();
}
@@ -4664,7 +4649,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
/*applyImmediately*/ true);
}
- const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
+ const auto configs = mScheduler->getCurrentVsyncConfigs();
mScheduler->createEventThread(scheduler::Cycle::Render, mFrameTimeline->getTokenManager(),
/* workDuration */ configs.late.appWorkDuration,
@@ -4998,13 +4983,7 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t nu
return true;
}
-status_t SurfaceFlinger::setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
- const std::vector<uint64_t>& mergedTransactionIds) {
+status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState) {
SFTRACE_CALL();
IPCThreadState* ipc = IPCThreadState::self();
@@ -5012,7 +4991,7 @@ status_t SurfaceFlinger::setTransactionState(
const int originUid = ipc->getCallingUid();
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
ftl::Flags<adpf::Workload> queuedWorkload;
- for (auto& composerState : states) {
+ for (auto& composerState : transactionState.mComposerStates) {
composerState.state.sanitize(permissions);
if (composerState.state.what & layer_state_t::COMPOSITION_EFFECTS) {
queuedWorkload |= adpf::Workload::EFFECTS;
@@ -5022,27 +5001,27 @@ status_t SurfaceFlinger::setTransactionState(
}
}
- for (DisplayState& display : displays) {
+ for (DisplayState& display : transactionState.mDisplayStates) {
display.sanitize(permissions);
}
- if (!inputWindowCommands.empty() &&
+ if (!transactionState.mInputWindowCommands.empty() &&
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
ALOGE("Only privileged callers are allowed to send input commands.");
- inputWindowCommands.clear();
+ transactionState.mInputWindowCommands.clear();
}
- if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+ if (transactionState.mFlags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
const bool hasPermission =
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
callingThreadHasPermission(sWakeupSurfaceFlinger);
if (!hasPermission) {
ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
"eEarlyWakeup[Start|End] flags");
- flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ transactionState.mFlags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
}
}
- if (flags & eEarlyWakeupStart) {
+ if (transactionState.mFlags & eEarlyWakeupStart) {
queuedWorkload |= adpf::Workload::WAKEUP;
}
mPowerAdvisor->setQueuedWorkload(queuedWorkload);
@@ -5050,8 +5029,8 @@ status_t SurfaceFlinger::setTransactionState(
const int64_t postTime = systemTime();
std::vector<uint64_t> uncacheBufferIds;
- uncacheBufferIds.reserve(uncacheBuffers.size());
- for (const auto& uncacheBuffer : uncacheBuffers) {
+ uncacheBufferIds.reserve(transactionState.mUncacheBuffers.size());
+ for (const auto& uncacheBuffer : transactionState.mUncacheBuffers) {
sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
if (buffer != nullptr) {
uncacheBufferIds.push_back(buffer->getId());
@@ -5059,19 +5038,19 @@ status_t SurfaceFlinger::setTransactionState(
}
std::vector<ResolvedComposerState> resolvedStates;
- resolvedStates.reserve(states.size());
- for (auto& state : states) {
+ resolvedStates.reserve(transactionState.mComposerStates.size());
+ for (auto& state : transactionState.mComposerStates) {
resolvedStates.emplace_back(std::move(state));
auto& resolvedState = resolvedStates.back();
resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
resolvedState.state.surface) {
sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
- std::string layerName = (layer) ?
- layer->getDebugName() : std::to_string(resolvedState.state.layerId);
+ std::string layerName =
+ (layer) ? layer->getDebugName() : std::to_string(resolvedState.state.layerId);
resolvedState.externalTexture =
getExternalTextureFromBufferData(*resolvedState.state.bufferData,
- layerName.c_str(), transactionId);
+ layerName.c_str(), transactionState.getId());
if (resolvedState.externalTexture) {
resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
if (FlagManager::getInstance().monitor_buffer_fences()) {
@@ -5099,22 +5078,12 @@ status_t SurfaceFlinger::setTransactionState(
}
}
- QueuedTransactionState state{frameTimelineInfo,
- resolvedStates,
- displays,
- flags,
- applyToken,
- std::move(inputWindowCommands),
- desiredPresentTime,
- isAutoTimestamp,
+ QueuedTransactionState state{std::move(transactionState),
+ std::move(resolvedStates),
std::move(uncacheBufferIds),
postTime,
- hasListenerCallbacks,
- listenerCallbacks,
originPid,
- originUid,
- transactionId,
- mergedTransactionIds};
+ originUid};
state.workloadHint = queuedWorkload;
if (mTransactionTracing) {
@@ -5128,6 +5097,9 @@ status_t SurfaceFlinger::setTransactionState(
}(state.flags);
const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
+ // Copy fields of |state| needed after it is moved into queueTransaction
+ VsyncId vsyncId{state.frameTimelineInfo.vsyncId};
+ auto applyToken = state.applyToken;
{
// Transactions are added via a lockless queue and does not need to be added from the main
// thread.
@@ -5137,7 +5109,7 @@ status_t SurfaceFlinger::setTransactionState(
for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
- scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
+ scheduleNotifyExpectedPresentHint(displayId, vsyncId);
}
}
setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
@@ -5146,7 +5118,7 @@ status_t SurfaceFlinger::setTransactionState(
bool SurfaceFlinger::applyTransactionState(
const FrameTimelineInfo& frameTimelineInfo, std::vector<ResolvedComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags,
+ std::span<DisplayState> displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds, const int64_t postTime,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -5636,7 +5608,8 @@ void SurfaceFlinger::initializeDisplays() {
auto layerStack = ui::DEFAULT_LAYER_STACK.id;
for (const auto& [id, display] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) {
- state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
+ state.displays.emplace_back(
+ DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
}
std::vector<QueuedTransactionState> transactions;
@@ -5704,13 +5677,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
incRefreshableDisplays();
}
- if (displayId == mActiveDisplayId &&
- FlagManager::getInstance().correct_virtual_display_power_state()) {
- applyOptimizationPolicy(__func__);
- }
-
const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
- using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
if (currentMode == hal::PowerMode::OFF) {
// Turn on the display
@@ -5725,10 +5692,12 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
}
- if (displayId == mActiveDisplayId &&
- !FlagManager::getInstance().correct_virtual_display_power_state()) {
- optimizeThreadScheduling("setPhysicalDisplayPowerMode(ON/DOZE)",
- OptimizationPolicy::optimizeForPerformance);
+ if (displayId == mActiveDisplayId) {
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ applyOptimizationPolicy("setPhysicalDisplayPowerMode(ON)");
+ } else {
+ disablePowerOptimizations("setPhysicalDisplayPowerMode(ON)");
+ }
}
getHwComposer().setPowerMode(displayId, mode);
@@ -5737,8 +5706,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
requestHardwareVsync(displayId, enable);
- if (displayId == mActiveDisplayId &&
- !FlagManager::getInstance().correct_virtual_display_power_state()) {
+ if (displayId == mActiveDisplayId) {
mScheduler->enableSyntheticVsync(false);
}
@@ -5755,13 +5723,13 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
if (const auto display = getActivatableDisplay()) {
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
} else {
- if (!FlagManager::getInstance().correct_virtual_display_power_state()) {
- optimizeThreadScheduling("setPhysicalDisplayPowerMode(OFF)",
- OptimizationPolicy::optimizeForPower);
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ applyOptimizationPolicy("setPhysicalDisplayPowerMode(OFF)");
+ } else {
+ enablePowerOptimizations("setPhysicalDisplayPowerMode(OFF)");
}
- if (currentModeNotDozeSuspend &&
- !FlagManager::getInstance().correct_virtual_display_power_state()) {
+ if (currentModeNotDozeSuspend) {
mScheduler->enableSyntheticVsync();
}
}
@@ -5789,9 +5757,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
mVisibleRegionsDirty = true;
scheduleRepaint();
- if (!FlagManager::getInstance().correct_virtual_display_power_state()) {
- mScheduler->enableSyntheticVsync(false);
- }
+ mScheduler->enableSyntheticVsync(false);
}
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
@@ -5801,8 +5767,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa
constexpr bool kDisallow = true;
mScheduler->disableHardwareVsync(displayId, kDisallow);
- if (displayId == mActiveDisplayId &&
- !FlagManager::getInstance().correct_virtual_display_power_state()) {
+ if (displayId == mActiveDisplayId) {
mScheduler->enableSyntheticVsync();
}
getHwComposer().setPowerMode(displayId, mode);
@@ -5841,44 +5806,43 @@ void SurfaceFlinger::setVirtualDisplayPowerMode(const sp<DisplayDevice>& display
to_string(displayId).c_str());
}
-void SurfaceFlinger::optimizeThreadScheduling(
- const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
- ALOGD("%s: Optimizing thread scheduling: %s", whence, to_string(optimizationPolicy));
+bool SurfaceFlinger::shouldOptimizeForPerformance() {
+ for (const auto& [_, display] : mDisplays) {
+ // Displays that are optimized for power are always powered on and should not influence
+ // whether there is an active display for the purpose of power optimization, etc. If these
+ // displays are being shown somewhere, a different (physical or virtual) display that is
+ // optimized for performance will be powered on in addition. Displays optimized for
+ // performance will change power mode, so if they are off then they are not active.
+ if (display->isPoweredOn() &&
+ display->getOptimizationPolicy() ==
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SurfaceFlinger::enablePowerOptimizations(const char* whence) {
+ ALOGD("%s: Enabling power optimizations", whence);
+
+ setSchedAttr(false, whence);
+ setSchedFifo(false, whence);
+}
+
+void SurfaceFlinger::disablePowerOptimizations(const char* whence) {
+ ALOGD("%s: Disabling power optimizations", whence);
- const bool optimizeForPerformance =
- optimizationPolicy == gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
// TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall
// and set it before SCHED_FIFO due to b/190237315.
- setSchedAttr(optimizeForPerformance, whence);
- setSchedFifo(optimizeForPerformance, whence);
+ setSchedAttr(true, whence);
+ setSchedFifo(true, whence);
}
void SurfaceFlinger::applyOptimizationPolicy(const char* whence) {
- using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
-
- const bool optimizeForPerformance =
- std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) {
- const auto& display = pair.second;
- return display->isPoweredOn() &&
- display->getOptimizationPolicy() ==
- OptimizationPolicy::optimizeForPerformance;
- });
-
- optimizeThreadScheduling(whence,
- optimizeForPerformance ? OptimizationPolicy::optimizeForPerformance
- : OptimizationPolicy::optimizeForPower);
-
- if (mScheduler) {
- const bool disableSyntheticVsync =
- std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) {
- const auto& display = pair.second;
- const hal::PowerMode powerMode = display->getPowerMode();
- return powerMode != hal::PowerMode::OFF &&
- powerMode != hal::PowerMode::DOZE_SUSPEND &&
- display->getOptimizationPolicy() ==
- OptimizationPolicy::optimizeForPerformance;
- });
- mScheduler->enableSyntheticVsync(!disableSyntheticVsync);
+ if (shouldOptimizeForPerformance()) {
+ disablePowerOptimizations(whence);
+ } else {
+ enablePowerOptimizations(whence);
}
}
@@ -5922,8 +5886,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_SHELL) &&
- !PermissionCache::checkPermission(sDump, pid, uid)) {
+ if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
pid, uid);
write(fd, result.c_str(), result.size());
@@ -6381,7 +6344,7 @@ void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositio
// figure out if we're stuck somewhere
const nsecs_t now = systemTime();
const nsecs_t inTransaction(mDebugInTransaction);
- nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
+ nsecs_t inTransactionDuration = (inTransaction) ? now - inTransaction : 0;
/*
* Dump library configuration.
@@ -6561,7 +6524,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
- ipc->getCallingPid(), ipc->getCallingUid());
+ ipc->getCallingPid(), ipc->getCallingUid());
return PERMISSION_DENIED;
}
return OK;
@@ -6655,9 +6618,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) {
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1045 are currently used for backdoors. The code
+ // Numbers from 1000 to 1047 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1046) {
+ if (code >= 1000 && code <= 1047) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6677,11 +6640,12 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
CHECK_INTERFACE(ISurfaceComposer, data, reply);
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
- if (CC_UNLIKELY(uid != AID_SYSTEM
- && !PermissionCache::checkCallingPermission(sHardwareTest))) {
+ if (CC_UNLIKELY(uid != AID_SYSTEM &&
+ !PermissionCache::checkCallingPermission(sHardwareTest))) {
const int pid = ipc->getCallingPid();
ALOGE("Permission Denial: "
- "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ "can't access SurfaceFlinger pid=%d, uid=%d",
+ pid, uid);
return PERMISSION_DENIED;
}
int n;
@@ -6752,7 +6716,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
n = data.readInt32();
if (n) {
// color matrix is sent as a column-major mat4 matrix
- for (size_t i = 0 ; i < 4; i++) {
+ for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < 4; j++) {
mClientColorMatrix[i][j] = data.readFloat();
}
@@ -7199,6 +7163,34 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r
mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs)));
return NO_ERROR;
}
+ // Update WorkDuration
+ // parameters:
+ // - (required) i64 minSfNs, used as the late.sf WorkDuration.
+ // - (required) i64 maxSfNs, used as the early.sf and earlyGl.sf WorkDuration.
+ // - (required) i64 appDurationNs, used as the late.app, early.app and earlyGl.app
+ // WorkDuration.
+ // Usage:
+ // adb shell service call SurfaceFlinger 1047 i64 12333333 i64 16666666 i64 16666666
+ case 1047: {
+ if (!property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+ ALOGE("Not supported when work duration is not enabled");
+ return INVALID_OPERATION;
+ }
+ int64_t minSfNs = 0;
+ int64_t maxSfNs = 0;
+ int64_t appDurationNs = 0;
+ if (data.readInt64(&minSfNs) != NO_ERROR || data.readInt64(&maxSfNs) != NO_ERROR ||
+ data.readInt64(&appDurationNs) != NO_ERROR) {
+ return BAD_VALUE;
+ }
+ mScheduler->reloadPhaseConfiguration(mDisplayModeController
+ .getActiveMode(mActiveDisplayId)
+ .fps,
+ Duration::fromNs(minSfNs),
+ Duration::fromNs(maxSfNs),
+ Duration::fromNs(appDurationNs));
+ return NO_ERROR;
+ }
}
}
return err;
@@ -7272,9 +7264,7 @@ auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId)
class WindowDisconnector {
public:
WindowDisconnector(ANativeWindow* window, int api) : mWindow(window), mApi(api) {}
- ~WindowDisconnector() {
- native_window_api_disconnect(mWindow, mApi);
- }
+ ~WindowDisconnector() { native_window_api_disconnect(mWindow, mApi); }
private:
ANativeWindow* mWindow;
@@ -8385,8 +8375,7 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui
}
int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const {
- const auto vsyncConfig =
- mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late;
+ const auto vsyncConfig = mScheduler->getVsyncConfigsForRefreshRate(refreshRate).late;
const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
@@ -8492,10 +8481,12 @@ void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t con
}
static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() {
+ const bool secure = connectedLevel >= 2 /* HDCP_V1 */;
if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) {
Mutex::Autolock lock(mStateLock);
- display->setSecure(connectedLevel >= 2 /* HDCP_V1 */);
+ display->setSecure(secure);
}
+ FTL_FAKE_GUARD(kMainThreadContext, mDisplayModeController.setSecure(displayId, secure));
mScheduler->onHdcpLevelsChanged(scheduler::Cycle::Render, displayId, connectedLevel,
maxLevel);
}));
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c472c4c6d4..f0298079a0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -545,13 +545,7 @@ private:
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
- status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override;
+ status_t setTransactionState(TransactionState&&) override;
void bootFinished();
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
sp<IDisplayEventConnection> createDisplayEventConnection(
@@ -739,14 +733,19 @@ private:
void setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
REQUIRES(mStateLock, kMainThreadContext);
- // Adjusts thread scheduling according to the optimization policy
- static void optimizeThreadScheduling(
- const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy);
+ // Returns whether to optimize globally for performance instead of power.
+ bool shouldOptimizeForPerformance() REQUIRES(mStateLock);
+
+ // Turns on power optimizations, for example when there are no displays to be optimized for
+ // performance.
+ static void enablePowerOptimizations(const char* whence);
+
+ // Turns off power optimizations.
+ static void disablePowerOptimizations(const char* whence);
// Enables or disables power optimizations depending on whether there are displays that should
// be optimized for performance.
- void applyOptimizationPolicy(const char* whence) REQUIRES(kMainThreadContext)
- REQUIRES(mStateLock);
+ void applyOptimizationPolicy(const char* whence) REQUIRES(mStateLock);
// Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
// display. Falls back to the display's defaultModeId otherwise.
@@ -793,7 +792,7 @@ private:
*/
bool applyTransactionState(const FrameTimelineInfo& info,
std::vector<ResolvedComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags,
+ std::span<DisplayState> displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<uint64_t>& uncacheBufferIds,
@@ -1340,8 +1339,6 @@ private:
HWComposer::HotplugEvent event;
};
- bool mIsHdcpViaNegVsync = false;
-
std::mutex mHotplugMutex;
std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 3297c16113..6bbc04cf6f 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -321,7 +321,7 @@ QueuedTransactionState TransactionProtoParser::fromProto(
int32_t displayCount = proto.display_changes_size();
t.displays.reserve(static_cast<size_t>(displayCount));
for (int i = 0; i < displayCount; i++) {
- t.displays.add(fromProto(proto.display_changes(i)));
+ t.displays.emplace_back(fromProto(proto.display_changes(i)));
}
return t;
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index bf1035149b..ebf451501d 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -161,6 +161,7 @@ void FlagManager::dump(std::string& result) const {
DUMP_ACONFIG_FLAG(game_default_frame_rate);
DUMP_ACONFIG_FLAG(graphite_renderengine);
DUMP_ACONFIG_FLAG(hdcp_level_hal);
+ DUMP_ACONFIG_FLAG(hdcp_negotiation);
DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
@@ -259,6 +260,7 @@ FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category
FLAG_MANAGER_ACONFIG_FLAG(misc1, "")
FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config")
FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "")
+FLAG_MANAGER_ACONFIG_FLAG(hdcp_negotiation, "debug.sf.hdcp_negotiation");
FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "")
FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "")
FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved,
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 8f361ac610..72b3bc302a 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -95,6 +95,7 @@ public:
bool game_default_frame_rate() const;
bool graphite_renderengine() const;
bool hdcp_level_hal() const;
+ bool hdcp_negotiation() const;
bool idle_screen_refresh_rate_timeout() const;
bool latch_unsignaled_with_auto_refresh_changed() const;
bool local_tonemap_screenshots() const;
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d412a19f3c..e8b75cfa6d 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -190,6 +190,16 @@ flag {
} # graphite_renderengine_preview_rollout
flag {
+ name: "hdcp_negotiation"
+ namespace: "core_graphics"
+ description: "detect secure layers to start HDCP negotiation"
+ bug: "375340594"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # hdcp_negotiation
+
+flag {
name: "increase_missed_frame_jank_threshold"
namespace: "core_graphics"
description: "Increase the jank threshold to 4 milliseconds"
diff --git a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
index 46b98f9193..192602d7d2 100644
--- a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
+++ b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
@@ -52,6 +52,8 @@ protected:
};
TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
+ // Last strong pointer is removed, the layer is destroyed and is removed
+ // from compostion.
fgLayer = nullptr;
{
SCOPED_TRACE("after setting null");
@@ -61,7 +63,9 @@ TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
}
TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
- auto transaction = Transaction().show(fgLayer);
+ Transaction transaction;
+ transaction.show(fgLayer);
+ // |transaction| retains a strong pointer, so layer is retained.
fgLayer = nullptr;
{
SCOPED_TRACE("after setting null");
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 18bd3b92d2..94cb878311 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -42,14 +42,22 @@ using CallbackInfo = SurfaceComposerClient::CallbackInfo;
using TCLHash = SurfaceComposerClient::TCLHash;
using android::hardware::graphics::common::V1_1::BufferUsage;
-class TransactionHelper : public Transaction {
+class TransactionHelper : public Transaction, public Parcelable {
public:
+ TransactionHelper() : Transaction() {}
size_t getNumListeners() { return mListenerCallbacks.size(); }
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
getListenerCallbacks() {
return mListenerCallbacks;
}
+ status_t writeToParcel(Parcel* parcel) const override {
+ return Transaction::writeToParcel(parcel);
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ return Transaction::readFromParcel(parcel);
+ }
};
class IPCTestUtils {
diff --git a/services/surfaceflinger/tests/end2end/.clang-format b/services/surfaceflinger/tests/end2end/.clang-format
new file mode 120000
index 0000000000..5e8e20be26
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clang-format
@@ -0,0 +1 @@
+../../../../../../build/soong/scripts/system-clang-format \ No newline at end of file
diff --git a/services/surfaceflinger/tests/end2end/.clang-tidy b/services/surfaceflinger/tests/end2end/.clang-tidy
new file mode 100644
index 0000000000..29f3b4721c
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clang-tidy
@@ -0,0 +1,380 @@
+# Copyright 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.
+
+FormatStyle: file
+InheritParentConfig: true
+
+# Please add checks explicitly rather than using wildcards like "modernize-*".
+# These check names are current as of LLVM 20.0.0, as reported by "clang-tidy --list-checks --checks=*"
+# For more information on each check, see https://clang.llvm.org/extra/clang-tidy/checks/list.html.
+Checks:
+ # from android-*
+ - android-cloexec-accept
+ - android-cloexec-accept4
+ - android-cloexec-creat
+ - android-cloexec-dup
+ - android-cloexec-epoll-create
+ - android-cloexec-epoll-create1
+ - android-cloexec-fopen
+ - android-cloexec-inotify-init
+ - android-cloexec-inotify-init1
+ - android-cloexec-memfd-create
+ - android-cloexec-open
+ - android-cloexec-pipe
+ - android-cloexec-pipe2
+ - android-cloexec-socket
+ - android-comparison-in-temp-failure-retry
+
+ # from bugprone-*
+ - bugprone-argument-comment
+ - bugprone-assert-side-effect
+ - bugprone-assignment-in-if-condition
+ - bugprone-bad-signal-to-kill-thread
+ - bugprone-bool-pointer-implicit-conversion
+ - bugprone-branch-clone
+ - bugprone-casting-through-void
+ - bugprone-chained-comparison
+ - bugprone-compare-pointer-to-member-virtual-function
+ - bugprone-copy-constructor-init
+ - bugprone-crtp-constructor-accessibility
+ - bugprone-dangling-handle
+ - bugprone-dynamic-static-initializers
+ - bugprone-easily-swappable-parameters
+ - bugprone-empty-catch
+ - bugprone-exception-escape
+ - bugprone-fold-init-type
+ - bugprone-forward-declaration-namespace
+ - bugprone-forwarding-reference-overload
+ - bugprone-implicit-widening-of-multiplication-result
+ - bugprone-inaccurate-erase
+ - bugprone-inc-dec-in-conditions
+ - bugprone-incorrect-enable-if
+ - bugprone-incorrect-roundings
+ - bugprone-infinite-loop
+ - bugprone-integer-division
+ - bugprone-lambda-function-name
+ - bugprone-macro-parentheses
+ - bugprone-macro-repeated-side-effects
+ - bugprone-misplaced-operator-in-strlen-in-alloc
+ - bugprone-misplaced-pointer-arithmetic-in-alloc
+ - bugprone-misplaced-widening-cast
+ - bugprone-move-forwarding-reference
+ - bugprone-multi-level-implicit-pointer-conversion
+ - bugprone-multiple-new-in-one-expression
+ - bugprone-multiple-statement-macro
+ - bugprone-narrowing-conversions
+ - bugprone-no-escape
+ - bugprone-non-zero-enum-to-bool-conversion
+ - bugprone-not-null-terminated-result
+ - bugprone-optional-value-conversion
+ - bugprone-parent-virtual-call
+ - bugprone-pointer-arithmetic-on-polymorphic-object
+ - bugprone-posix-return
+ - bugprone-redundant-branch-condition
+ - bugprone-reserved-identifier
+ - bugprone-return-const-ref-from-parameter
+ - bugprone-shared-ptr-array-mismatch
+ - bugprone-signal-handler
+ - bugprone-signed-char-misuse
+ - bugprone-sizeof-container
+ - bugprone-sizeof-expression
+ - bugprone-spuriously-wake-up-functions
+ - bugprone-standalone-empty
+ - bugprone-string-constructor
+ - bugprone-string-integer-assignment
+ - bugprone-string-literal-with-embedded-nul
+ - bugprone-stringview-nullptr
+ - bugprone-suspicious-enum-usage
+ - bugprone-suspicious-include
+ - bugprone-suspicious-memory-comparison
+ - bugprone-suspicious-memset-usage
+ - bugprone-suspicious-missing-comma
+ - bugprone-suspicious-realloc-usage
+ - bugprone-suspicious-semicolon
+ - bugprone-suspicious-string-compare
+ - bugprone-suspicious-stringview-data-usage
+ - bugprone-swapped-arguments
+ - bugprone-switch-missing-default-case
+ - bugprone-terminating-continue
+ - bugprone-throw-keyword-missing
+ - bugprone-too-small-loop-variable
+ - bugprone-unchecked-optional-access
+ - bugprone-undefined-memory-manipulation
+ - bugprone-undelegated-constructor
+ - bugprone-unhandled-exception-at-new
+ - bugprone-unhandled-self-assignment
+ - bugprone-unique-ptr-array-mismatch
+ - bugprone-unsafe-functions
+ - bugprone-unused-local-non-trivial-variable
+ - bugprone-unused-raii
+ - bugprone-unused-return-value
+ - bugprone-use-after-move
+ - bugprone-virtual-near-miss
+
+ # from cert-*
+ - cert-con36-c
+ - cert-con54-cpp
+ - cert-ctr56-cpp
+ - cert-dcl03-c
+ - cert-dcl16-c
+ - cert-dcl37-c
+ - cert-dcl50-cpp
+ - cert-dcl51-cpp
+ - cert-dcl54-cpp
+ - cert-dcl58-cpp
+ - cert-dcl59-cpp
+ - cert-env33-c
+ - cert-err09-cpp
+ - cert-err33-c
+ - cert-err34-c
+ - cert-err52-cpp
+ - cert-err58-cpp
+ - cert-err60-cpp
+ - cert-err61-cpp
+ - cert-exp42-c
+ - cert-fio38-c
+ - cert-flp30-c
+ - cert-flp37-c
+ - cert-int09-c
+ - cert-mem57-cpp
+ - cert-msc24-c
+ - cert-msc30-c
+ - cert-msc32-c
+ - cert-msc33-c
+ - cert-msc50-cpp
+ - cert-msc51-cpp
+ - cert-msc54-cpp
+ - cert-oop11-cpp
+ - cert-oop54-cpp
+ - cert-oop57-cpp
+ - cert-oop58-cpp
+ - cert-pos44-c
+ - cert-pos47-c
+ - cert-sig30-c
+ - cert-str34-c
+
+ # from concurrency-*
+ - concurrency-mt-unsafe
+ - concurrency-thread-canceltype-asynchronous
+
+ # from cppcoreguidelines-*
+ - cppcoreguidelines-avoid-c-arrays
+ - cppcoreguidelines-avoid-capturing-lambda-coroutines
+ - cppcoreguidelines-avoid-const-or-ref-data-members
+ - cppcoreguidelines-avoid-do-while
+ - cppcoreguidelines-avoid-goto
+ - cppcoreguidelines-avoid-magic-numbers
+ - cppcoreguidelines-avoid-non-const-global-variables
+ - cppcoreguidelines-avoid-reference-coroutine-parameters
+ - cppcoreguidelines-c-copy-assignment-signature
+ - cppcoreguidelines-explicit-virtual-functions
+ - cppcoreguidelines-init-variables
+ - cppcoreguidelines-interfaces-global-init
+ - cppcoreguidelines-macro-to-enum
+ - cppcoreguidelines-macro-usage
+ - cppcoreguidelines-misleading-capture-default-by-value
+ - cppcoreguidelines-missing-std-forward
+ - cppcoreguidelines-narrowing-conversions
+ - cppcoreguidelines-no-malloc
+ - cppcoreguidelines-no-suspend-with-lock
+ - cppcoreguidelines-noexcept-destructor
+ - cppcoreguidelines-noexcept-move-operations
+ - cppcoreguidelines-noexcept-swap
+ - cppcoreguidelines-non-private-member-variables-in-classes
+ - cppcoreguidelines-owning-memory
+ - cppcoreguidelines-prefer-member-initializer
+ - cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ - cppcoreguidelines-pro-bounds-constant-array-index
+ - cppcoreguidelines-pro-bounds-pointer-arithmetic
+ - cppcoreguidelines-pro-type-const-cast
+ - cppcoreguidelines-pro-type-cstyle-cast
+ - cppcoreguidelines-pro-type-member-init
+ - cppcoreguidelines-pro-type-reinterpret-cast
+ - cppcoreguidelines-pro-type-static-cast-downcast
+ - cppcoreguidelines-pro-type-union-access
+ - cppcoreguidelines-pro-type-vararg
+ - cppcoreguidelines-rvalue-reference-param-not-moved
+ - cppcoreguidelines-slicing
+ - cppcoreguidelines-special-member-functions
+ - cppcoreguidelines-use-default-member-init
+ - cppcoreguidelines-virtual-class-destructor
+
+ # from google-*
+ - google-build-explicit-make-pair
+ - google-build-namespaces
+ - google-build-using-namespace
+ - google-default-arguments
+ - google-explicit-constructor
+ - google-global-names-in-headers
+ - google-objc-avoid-nsobject-new
+ - google-objc-avoid-throwing-exception
+ - google-objc-function-naming
+ - google-objc-global-variable-declaration
+ - google-readability-avoid-underscore-in-googletest-name
+ - google-readability-braces-around-statements
+ - google-readability-casting
+ - google-readability-function-size
+ - google-readability-namespace-comments
+ - google-readability-todo
+ - google-runtime-int
+ - google-runtime-operator
+ - google-upgrade-googletest-case
+
+ # from misc-*
+ - misc-confusable-identifiers
+ - misc-const-correctness
+ - misc-coroutine-hostile-raii
+ - misc-definitions-in-headers
+ - misc-header-include-cycle
+ - misc-include-cleaner
+ - misc-misleading-bidirectional
+ - misc-misleading-identifier
+ - misc-misplaced-const
+ - misc-new-delete-overloads
+ - misc-no-recursion
+ - misc-non-copyable-objects
+ - misc-non-private-member-variables-in-classes
+ - misc-redundant-expression
+ - misc-static-assert
+ - misc-throw-by-value-catch-by-reference
+ - misc-unconventional-assign-operator
+ - misc-uniqueptr-reset-release
+ - misc-unused-alias-decls
+ - misc-unused-parameters
+ - misc-unused-using-decls
+ - misc-use-anonymous-namespace
+ - misc-use-internal-linkage
+
+ # from modernize-*
+ - modernize-avoid-bind
+ - modernize-avoid-c-arrays
+ - modernize-concat-nested-namespaces
+ - modernize-deprecated-headers
+ - modernize-deprecated-ios-base-aliases
+ - modernize-loop-convert
+ - modernize-macro-to-enum
+ - modernize-make-shared
+ - modernize-make-unique
+ - modernize-min-max-use-initializer-list
+ - modernize-pass-by-value
+ - modernize-raw-string-literal
+ - modernize-redundant-void-arg
+ - modernize-replace-auto-ptr
+ - modernize-replace-disallow-copy-and-assign-macro
+ - modernize-replace-random-shuffle
+ - modernize-return-braced-init-list
+ - modernize-shrink-to-fit
+ - modernize-type-traits
+ - modernize-unary-static-assert
+ - modernize-use-auto
+ - modernize-use-bool-literals
+ - modernize-use-constraints
+ - modernize-use-default-member-init
+ - modernize-use-designated-initializers
+ - modernize-use-emplace
+ - modernize-use-equals-default
+ - modernize-use-equals-delete
+ - modernize-use-nodiscard
+ - modernize-use-noexcept
+ - modernize-use-nullptr
+ - modernize-use-override
+ - modernize-use-ranges
+ - modernize-use-starts-ends-with
+ - modernize-use-std-format
+ - modernize-use-std-numbers
+ - modernize-use-std-print
+ - modernize-use-trailing-return-type
+ - modernize-use-transparent-functors
+ - modernize-use-uncaught-exceptions
+ - modernize-use-using
+
+ # from performance-*
+ - performance-avoid-endl
+ - performance-enum-size
+ - performance-faster-string-find
+ - performance-for-range-copy
+ - performance-implicit-conversion-in-loop
+ - performance-inefficient-algorithm
+ - performance-inefficient-string-concatenation
+ - performance-inefficient-vector-operation
+ - performance-move-const-arg
+ - performance-move-constructor-init
+ - performance-no-automatic-move
+ - performance-no-int-to-ptr
+ - performance-noexcept-destructor
+ - performance-noexcept-move-constructor
+ - performance-noexcept-swap
+ - performance-trivially-destructible
+ - performance-type-promotion-in-math-fn
+ - performance-unnecessary-copy-initialization
+ - performance-unnecessary-value-param
+
+ # from portability-*
+ - portability-restrict-system-includes
+ - portability-simd-intrinsics
+ - portability-std-allocator-const
+
+ # from readability-*
+ - readability-avoid-const-params-in-decls
+ - readability-avoid-nested-conditional-operator
+ - readability-avoid-return-with-void-value
+ - readability-avoid-unconditional-preprocessor-if
+ - readability-braces-around-statements
+ - readability-const-return-type
+ - readability-container-contains
+ - readability-container-data-pointer
+ - readability-container-size-empty
+ - readability-convert-member-functions-to-static
+ - readability-delete-null-pointer
+ - readability-duplicate-include
+ - readability-else-after-return
+ - readability-enum-initial-value
+ - readability-function-cognitive-complexity
+ - readability-function-size
+ - readability-identifier-length
+ - readability-identifier-naming
+ - readability-implicit-bool-conversion
+ - readability-inconsistent-declaration-parameter-name
+ - readability-isolate-declaration
+ - readability-magic-numbers
+ - readability-make-member-function-const
+ - readability-math-missing-parentheses
+ - readability-misleading-indentation
+ - readability-misplaced-array-index
+ - readability-named-parameter
+ - readability-non-const-parameter
+ - readability-operators-representation
+ - readability-qualified-auto
+ - readability-redundant-access-specifiers
+ - readability-redundant-casting
+ - readability-redundant-control-flow
+ - readability-redundant-declaration
+ - readability-redundant-function-ptr-dereference
+ - readability-redundant-inline-specifier
+ - readability-redundant-member-init
+ - readability-redundant-preprocessor
+ - readability-redundant-smartptr-get
+ - readability-redundant-string-cstr
+ - readability-redundant-string-init
+ - readability-reference-to-constructed-temporary
+ - readability-simplify-boolean-expr
+ - readability-simplify-subscript-expr
+ - readability-static-accessed-through-instance
+ - readability-static-definition-in-anonymous-namespace
+ - readability-string-compare
+ - readability-suspicious-call-argument
+ - readability-uniqueptr-delete-release
+ - readability-uppercase-literal-suffix
+ - readability-use-anyofallof
+ - readability-use-std-min-max
diff --git a/services/surfaceflinger/tests/end2end/.clangd b/services/surfaceflinger/tests/end2end/.clangd
new file mode 100644
index 0000000000..d64d2f8a55
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clangd
@@ -0,0 +1,20 @@
+# Copyright 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.
+
+Diagnostics:
+ UnusedIncludes: Strict
+ MissingIncludes: Strict
+ ClangTidy:
+ FastCheckFilter: None
+ # See the .clang-tidy files for additional configuration
diff --git a/services/surfaceflinger/tests/end2end/Android.bp b/services/surfaceflinger/tests/end2end/Android.bp
new file mode 100644
index 0000000000..8810330179
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/Android.bp
@@ -0,0 +1,68 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "surfaceflinger_end2end_tests",
+ test_suites: ["device-tests"],
+ require_root: true,
+
+ cpp_std: "experimental",
+ cflags: [
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-DNODISCARD_EXPECTED",
+ "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
+ "-Wall",
+ "-Wconversion",
+ "-Werror",
+ "-Wextra",
+ "-Wformat",
+ "-Wno-non-virtual-dtor",
+ "-Wno-sign-compare",
+ "-Wno-sign-conversion",
+ "-Wshadow",
+ "-Wthread-safety",
+ "-Wunreachable-code",
+ "-Wunused",
+ ],
+ srcs: [
+ "main.cpp",
+ "test_framework/core/TestService.cpp",
+ "test_framework/fake_hwc3/Hwc3Composer.cpp",
+ "test_framework/fake_hwc3/Hwc3Controller.cpp",
+ "test_framework/surfaceflinger/SFController.cpp",
+ "tests/Placeholder_test.cpp",
+ ],
+ tidy: true,
+ tidy_flags: [
+ "--config=", // Use the .clang-tidy closest to each source file for the configuration
+ ],
+ tidy_checks_as_errors: [
+ "*",
+ ],
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+ local_include_dirs: ["."],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbinder_ndk",
+ "libcutils",
+ "libgui",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.graphics.common-V6-ndk",
+ "android.hardware.graphics.composer3-V3-ndk",
+ "libgtest",
+ ],
+}
diff --git a/services/surfaceflinger/tests/end2end/AndroidTest.xml b/services/surfaceflinger/tests/end2end/AndroidTest.xml
new file mode 100644
index 0000000000..99cb7b3fee
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<configuration description="Configuration for surfaceflinger_end2end_tests">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Stop everything to run SurfaceFlinger in isolation, with relaxed SELinux permissions -->
+ <option name="teardown-command" value="stop" />
+ <option name="teardown-command" value="setprop debug.sf.nobootanimation 1" />
+
+ <!-- Restart everything with normal settings after the test finishes. -->
+ <option name="teardown-command" value="stop" />
+ <option name="teardown-command" value="setprop debug.sf.nobootanimation 0" />
+ <option name="teardown-command" value="setprop debug.sf.hwc_service_name default" />
+ <option name="teardown-command" value="start" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="surfaceflinger_end2end_tests->/data/local/tests/surfaceflinger_end2end_tests" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tests" />
+ <option name="module-name" value="surfaceflinger_end2end_tests" />
+ <option name="native-test-timeout" value="15m"/>
+ </test>
+</configuration>
diff --git a/services/surfaceflinger/tests/end2end/OWNERS b/services/surfaceflinger/tests/end2end/OWNERS
new file mode 100644
index 0000000000..aa5b5957a2
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/OWNERS
@@ -0,0 +1,7 @@
+lpique@google.com # primary POC
+bwidawsk@google.com
+ddavenport@google.com
+markyacoub@google.com
+sukoo@google.com
+
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/services/surfaceflinger/tests/end2end/README.md b/services/surfaceflinger/tests/end2end/README.md
new file mode 100644
index 0000000000..2f58cec5c7
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/README.md
@@ -0,0 +1,75 @@
+# `surfaceflinger_end2end_tests`
+
+Tests to cover end to end testing of SurfaceFlinger.
+
+In particular the test framework allows you to simulate various display
+configurations, so the test can confirm displays are handled correctly.
+
+## Quick Useful info
+
+### Running the tests
+
+At present the tests should run on any target, though the typical target would
+be a [Cuttlefish](https://source.android.com/docs/devices/cuttlefish) VM
+target such as `aosp_cf_x86_64_phone`.
+
+At some future time the test may be rewritten to require
+[`vkms`](https://dri.freedesktop.org/docs/drm/gpu/vkms.html) and
+[`drm_hwcomposer`](https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer)
+
+```
+atest surfaceflinger_end2end_tests
+```
+
+You can also run the google test binary directly. However you will also need
+to run a few other set-up and tear-down commands that are part of the
+AndroidTest.xml configuration, so that SurfaceFlinger can be used run isolation
+from the rest of the system.
+
+```
+# Set-up
+adb root
+adb shell stop
+adb shell setenforce 0
+adb shell setprop debug.sf.nobootanimation 1
+
+# Sync and run the test
+adb sync data
+adb shell data/nativetest64/surfaceflinger_end2end_tests/surfaceflinger_end2end_tests
+
+# Tear-down
+adb shell stop
+adb shell setenforce 1
+adb shell setprop debug.sf.nobootanimation 0
+adb shell setprop debug.sf.hwc_service_name default
+```
+
+### Manual clang-tidy checks via Soong
+
+At present Android does not run the clang-tidy checks as part of its
+presubmit checks.
+
+You can run them through the build system by using phony target that are
+automatically created for each source subdirectory.
+
+For the code under `frameworks/native/services/surfaceflinger/tests/end2end`,
+you would build:
+
+```
+m tidy-frameworks-native-services-surfaceflinger-tests-end2end
+```
+
+For more information see the build documentation:
+
+* <https://android.googlesource.com/platform/build/soong/+/main/docs/tidy.md#the-tidy_directory-targets>
+
+### Seeing clang-tidy checks in your editor
+
+If your editor supports using [`clangd`](https://clangd.llvm.org/) as a
+C++ language server, you can build and export a compilation database using
+Soong. With the local `.clangd` configuration file, you should see the same
+checks in editor, along with all the other checks `clangd` runs.
+
+See the build documentation for the compilation database instructions:
+
+https://android.googlesource.com/platform/build/soong/+/main/docs/compdb.md
diff --git a/services/surfaceflinger/tests/end2end/main.cpp b/services/surfaceflinger/tests/end2end/main.cpp
new file mode 100644
index 0000000000..ddf690021d
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/main.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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 <cstdlib>
+#include <string_view>
+
+#include <android-base/logging.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+void init(int argc, char** argv) {
+ using namespace std::string_view_literals;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+
+ auto minimumSeverity = android::base::INFO;
+ for (int i = 1; i < argc; i++) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ const std::string_view arg = argv[i];
+
+ if (arg == "-v"sv) {
+ minimumSeverity = android::base::DEBUG;
+ } else if (arg == "-vv"sv) {
+ minimumSeverity = android::base::VERBOSE;
+ }
+ }
+ ::android::base::SetMinimumLogSeverity(minimumSeverity);
+}
+
+} // namespace
+
+auto main(int argc, char** argv) -> int {
+ init(argc, argv);
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ return RUN_ALL_TESTS();
+} \ No newline at end of file
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
new file mode 100644
index 0000000000..c3a535e10f
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 <cstdint>
+
+namespace android::surfaceflinger::tests::end2end::test_framework::core {
+
+struct DisplayConfiguration final {
+ using Id = int64_t;
+
+ Id id{};
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::core
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp
new file mode 100644
index 0000000000..0531f18527
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 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 <memory>
+#include <span>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/expected.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/core/TestService.h"
+#include "test_framework/fake_hwc3/Hwc3Controller.h"
+#include "test_framework/surfaceflinger/SFController.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::core {
+
+struct TestService::Passkey final {};
+
+auto TestService::startWithDisplays(const std::vector<DisplayConfiguration>& displays)
+ -> base::expected<std::unique_ptr<TestService>, std::string> {
+ using namespace std::string_literals;
+
+ auto service = std::make_unique<TestService>(TestService::Passkey{});
+ if (service == nullptr) {
+ return base::unexpected("Failed to construct the TestService instance."s);
+ }
+
+ if (auto result = service->init(displays); !result) {
+ return base::unexpected("Failed to init the TestService instance: "s + result.error());
+ }
+
+ return service;
+}
+
+TestService::TestService(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto TestService::init(std::span<const DisplayConfiguration> displays)
+ -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto hwcResult = fake_hwc3::Hwc3Controller::make(displays);
+ if (!hwcResult) {
+ return base::unexpected(std::move(hwcResult).error());
+ }
+ auto hwc = *std::move(hwcResult);
+
+ auto flingerResult = surfaceflinger::SFController::make();
+ if (!flingerResult) {
+ return base::unexpected(std::move(flingerResult).error());
+ }
+ auto flinger = *std::move(flingerResult);
+
+ surfaceflinger::SFController::useHwcService(fake_hwc3::Hwc3Controller::getServiceName());
+
+ if (auto result = flinger->startAndConnect(); !result) {
+ return base::unexpected(std::move(result).error());
+ }
+
+ mHwc = std::move(hwc);
+ mFlinger = std::move(flinger);
+ return {};
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::core
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h
new file mode 100644
index 0000000000..21e6426406
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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 <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework {
+
+namespace surfaceflinger {
+
+class SFController;
+
+} // namespace surfaceflinger
+
+namespace fake_hwc3 {
+
+class Hwc3Controller;
+
+} // namespace fake_hwc3
+
+namespace core {
+
+class TestService final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Constructs the test service, and starts it with the given displays as connected at boot.
+ [[nodiscard]] static auto startWithDisplays(const std::vector<DisplayConfiguration>& displays)
+ -> base::expected<std::unique_ptr<TestService>, std::string>;
+
+ explicit TestService(Passkey passkey);
+
+ // Obtains the HWC3 back-end controller
+ [[nodiscard]] auto hwc() -> fake_hwc3::Hwc3Controller& {
+ CHECK(mHwc);
+ return *mHwc;
+ }
+
+ // Obtains the SurfaceFlinger front-end controller
+ [[nodiscard]] auto flinger() -> surfaceflinger::SFController& {
+ CHECK(mFlinger);
+ return *mFlinger;
+ }
+
+ private:
+ [[nodiscard]] auto init(std::span<const DisplayConfiguration> displays)
+ -> base::expected<void, std::string>;
+
+ std::shared_ptr<fake_hwc3::Hwc3Controller> mHwc;
+ std::shared_ptr<surfaceflinger::SFController> mFlinger;
+};
+
+} // namespace core
+} // namespace android::surfaceflinger::tests::end2end::test_framework
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp
new file mode 100644
index 0000000000..5349ef0b58
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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 <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/graphics/composer3/BnComposer.h>
+#include <aidl/android/hardware/graphics/composer3/Capability.h>
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <aidl/android/hardware/graphics/composer3/PowerMode.h>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_status.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/fake_hwc3/Hwc3Composer.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer::Hwc3ComposerImpl final
+ : public aidl::android::hardware::graphics::composer3::BnComposer {
+ using Capability = aidl::android::hardware::graphics::composer3::Capability;
+ using IComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
+ using Hwc3PowerMode = aidl::android::hardware::graphics::composer3::PowerMode;
+
+ // begin IComposer overrides
+
+ auto dump(int dumpFd, const char** args, uint32_t num_args) -> binder_status_t override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(dumpFd, args, num_args);
+ return static_cast<binder_status_t>(STATUS_NO_MEMORY);
+ }
+
+ auto createClient(std::shared_ptr<IComposerClient>* out_client) -> ndk::ScopedAStatus override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(out_client);
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IComposer::EX_NO_RESOURCES, "Client failed to initialize");
+ }
+
+ auto getCapabilities(std::vector<Capability>* out_capabilities) -> ndk::ScopedAStatus override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(out_capabilities);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ // end IComposer overrides
+};
+
+struct Hwc3Composer::Passkey final {};
+
+auto Hwc3Composer::getServiceName(std::string_view baseServiceName) -> std::string {
+ return Hwc3ComposerImpl::makeServiceName(baseServiceName);
+}
+
+auto Hwc3Composer::make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string> {
+ using namespace std::string_literals;
+
+ auto composer = std::make_shared<Hwc3Composer>(Passkey{});
+ if (composer == nullptr) {
+ return base::unexpected("Failed to construct the Hwc3Composer instance."s);
+ }
+
+ if (auto result = composer->init(); !result) {
+ return base::unexpected("Failed to init the Hwc3Composer instance: "s + result.error());
+ }
+
+ return composer;
+}
+
+Hwc3Composer::Hwc3Composer(Hwc3Composer::Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto Hwc3Composer::init() -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto impl = ndk::SharedRefBase::make<Hwc3ComposerImpl>();
+ if (!impl) {
+ return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s);
+ }
+
+ mImpl = std::move(impl);
+
+ return {};
+}
+
+auto Hwc3Composer::getComposer() -> std::shared_ptr<Hwc3IComposer> {
+ return mImpl;
+}
+
+void Hwc3Composer::addDisplay(const core::DisplayConfiguration& display) {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(display, mImpl);
+}
+
+void Hwc3Composer::removeDisplay(core::DisplayConfiguration::Id displayId) {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(displayId, mImpl);
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h
new file mode 100644
index 0000000000..6d6b7374c6
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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 <memory>
+#include <string>
+#include <string_view>
+
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <android-base/expected.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ class Hwc3ComposerImpl; // An internal class implements the AIDL interface.
+
+ public:
+ using Hwc3IComposer = aidl::android::hardware::graphics::composer3::IComposer;
+
+ // Gets the full qualified service name given a base name for the service.
+ [[nodiscard]] static auto getServiceName(std::string_view baseServiceName) -> std::string;
+
+ // Constructs a Hwc3Composer instance.
+ [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string>;
+
+ explicit Hwc3Composer(Passkey passkey);
+
+ // Obtains the AIDL composer3::IComposer interface for the internal instance.
+ [[nodiscard]] auto getComposer() -> std::shared_ptr<Hwc3IComposer>;
+
+ // Adds a display to the composer. This will sent a hotplug connect event.
+ void addDisplay(const core::DisplayConfiguration& display);
+
+ // Removes a display from the composer. This will sent a hotplug disconnect event.
+ void removeDisplay(core::DisplayConfiguration::Id displayId);
+
+ private:
+ [[nodiscard]] auto init() -> base::expected<void, std::string>;
+
+ std::shared_ptr<Hwc3ComposerImpl> mImpl;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp
new file mode 100644
index 0000000000..ea985c09b4
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 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 <memory>
+#include <span>
+#include <string>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_stability.h>
+#include <android/binder_status.h>
+#include <fmt/format.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/fake_hwc3/Hwc3Composer.h"
+#include "test_framework/fake_hwc3/Hwc3Controller.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+struct Hwc3Controller::Passkey final {};
+
+auto Hwc3Controller::make(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string> {
+ using namespace std::string_literals;
+
+ auto controller = std::make_unique<Hwc3Controller>(Passkey{});
+ if (controller == nullptr) {
+ return base::unexpected("Failed to construct the Hwc3Controller instance"s);
+ }
+
+ if (auto result = controller->init(displays); !result) {
+ return base::unexpected("Failed to construct the Hwc3Controller instance: "s +
+ result.error());
+ }
+
+ return controller;
+}
+
+Hwc3Controller::Hwc3Controller(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto Hwc3Controller::init(const std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto qualifiedServiceName = Hwc3Composer::getServiceName(baseServiceName);
+
+ auto composerResult = Hwc3Composer::make();
+ if (!composerResult) {
+ return base::unexpected(std::move(composerResult).error());
+ }
+ auto composer = *std::move(composerResult);
+
+ for (const auto& display : displays) {
+ composer->addDisplay(display);
+ }
+
+ auto binder = composer->getComposer()->asBinder();
+
+ // This downgrade allows us to use the fake service name without it being defined in the
+ // VINTF manifest.
+ AIBinder_forceDowngradeToLocalStability(binder.get());
+
+ auto status = AServiceManager_addService(binder.get(), qualifiedServiceName.c_str());
+ if (status != STATUS_OK) {
+ return base::unexpected(fmt::format("Failed to register service {}. Error {}.",
+ qualifiedServiceName, status));
+ }
+ LOG(INFO) << "Registered service " << qualifiedServiceName << ". Error: " << status;
+
+ mComposer = std::move(composer);
+ return {};
+}
+
+auto Hwc3Controller::getServiceName() -> std::string {
+ return Hwc3Composer::getServiceName(baseServiceName);
+}
+
+void Hwc3Controller::addDisplay(const core::DisplayConfiguration& config) {
+ CHECK(mComposer);
+ mComposer->addDisplay(config);
+}
+
+void Hwc3Controller::removeDisplay(core::DisplayConfiguration::Id displayId) {
+ CHECK(mComposer);
+ mComposer->removeDisplay(displayId);
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h
new file mode 100644
index 0000000000..e53d2cfc48
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 <memory>
+#include <span>
+#include <string>
+
+#include <android-base/expected.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer;
+
+class Hwc3Controller final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Gets the service name for the HWC3 instance that will be created and registered
+ [[nodiscard]] static auto getServiceName() -> std::string;
+
+ // Makes the HWC3 controller instance.
+ [[nodiscard]] static auto make(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string>;
+
+ explicit Hwc3Controller(Passkey passkey);
+
+ // Adds a new display to the HWC3, which will become a hotplug connect event.
+ void addDisplay(const core::DisplayConfiguration& config);
+
+ // Removes a new display from the HWC3, which will become a hotplug disconnect event.
+ void removeDisplay(core::DisplayConfiguration::Id displayId);
+
+ private:
+ static constexpr std::string baseServiceName = "fake";
+
+ [[nodiscard]] auto init(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<void, std::string>;
+
+ std::shared_ptr<Hwc3Composer> mComposer;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp
new file mode 100644
index 0000000000..1cf49c5750
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 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 <chrono>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <ftl/finalizer.h>
+#include <ftl/ignore.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "test_framework/surfaceflinger/SFController.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger {
+
+namespace {
+
+auto waitForSurfaceFlingerAIDL() -> sp<gui::ISurfaceComposer> {
+ constexpr auto kTimeout = std::chrono::seconds(30);
+ constexpr auto kSurfaceFlingerServiceName = "SurfaceFlingerAIDL";
+ const sp<android::IServiceManager> serviceManager(android::defaultServiceManager());
+ const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout;
+
+ LOG(INFO) << "Waiting " << kTimeout << " for service manager registration....";
+ sp<android::IBinder> flingerService;
+ while (flingerService == nullptr) {
+ if (std::chrono::steady_clock::now() > kTimeoutAfter) {
+ LOG(INFO) << "... Timeout!";
+ return nullptr;
+ }
+
+ constexpr auto sleepTime = std::chrono::milliseconds(10);
+ std::this_thread::sleep_for(sleepTime);
+ flingerService = serviceManager->checkService(String16(kSurfaceFlingerServiceName));
+ }
+ LOG(INFO) << "Obtained surfaceflinger interface from service manager.";
+
+ return interface_cast<gui::ISurfaceComposer>(flingerService);
+}
+
+} // namespace
+
+struct SFController::Passkey final {};
+
+void SFController::useHwcService(std::string_view fqn) {
+ base::SetProperty("debug.sf.hwc_service_name", std::string(fqn));
+}
+
+auto SFController::make() -> base::expected<std::shared_ptr<SFController>, std::string> {
+ using namespace std::string_literals;
+
+ auto controller = std::make_unique<SFController>(Passkey{});
+ if (controller == nullptr) {
+ return base::unexpected("Failed to construct the SFController instance."s);
+ }
+
+ if (auto result = controller->init(); !result) {
+ return base::unexpected("Failed to init the SFController instance: "s + result.error());
+ }
+
+ return controller;
+}
+
+SFController::SFController(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto SFController::init() -> base::expected<void, std::string> {
+ LOG(INFO) << "Stopping everything to prepare for tests";
+ // NOLINTBEGIN(cert-env33-c)
+ system("stop");
+ // NOLINTEND(cert-env33-c)
+
+ mCleanup = ftl::Finalizer([this]() { stop(); });
+
+ return {};
+}
+
+auto SFController::startAndConnect() -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ start();
+
+ LOG(VERBOSE) << "Getting ISurfaceComposer....";
+ auto surfaceComposerAidl = waitForSurfaceFlingerAIDL();
+ if (surfaceComposerAidl == nullptr) {
+ return base::unexpected("Failed to obtain the surfaceComposerAidl interface."s);
+ }
+ LOG(VERBOSE) << "Getting ISurfaceComposerClient....";
+ sp<gui::ISurfaceComposerClient> surfaceComposerClientAidl;
+ if (!surfaceComposerAidl->createConnection(&surfaceComposerClientAidl).isOk()) {
+ return base::unexpected("Failed to obtain the surfaceComposerClientAidl interface."s);
+ }
+ if (surfaceComposerClientAidl == nullptr) {
+ return base::unexpected("Failed to obtain a valid surfaceComposerClientAidl interface."s);
+ }
+ auto surfaceComposerClient = sp<SurfaceComposerClient>::make(surfaceComposerClientAidl);
+ if (surfaceComposerClient == nullptr) {
+ return base::unexpected(
+ "Failed to construct a surfaceComposerClient around the aidl interface."s);
+ }
+
+ mSurfaceComposerAidl = std::move(surfaceComposerAidl);
+ mSurfaceComposerClientAidl = std::move(surfaceComposerClientAidl);
+ mSurfaceComposerClient = std::move(surfaceComposerClient);
+
+ LOG(INFO) << "Connected to surfaceflinger";
+ return {};
+}
+
+void SFController::start() {
+ LOG(INFO) << "Starting surfaceflinger";
+ // NOLINTBEGIN(cert-env33-c)
+ system("start surfaceflinger");
+ // NOLINTEND(cert-env33-c)
+}
+
+void SFController::stop() {
+ LOG(INFO) << "Stopping surfaceflinger";
+ // NOLINTBEGIN(cert-env33-c)
+ system("stop surfaceflinger");
+ // NOLINTEND(cert-env33-c)
+
+ if (mSurfaceComposerAidl != nullptr) {
+ LOG(INFO) << "Waiting for SF AIDL interface to die";
+
+ constexpr auto kTimeout = std::chrono::seconds(30);
+ const auto binder = android::gui::ISurfaceComposer::asBinder(mSurfaceComposerAidl);
+ const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout;
+
+ while (binder->isBinderAlive()) {
+ if (std::chrono::steady_clock::now() > kTimeoutAfter) {
+ LOG(INFO) << "... Timeout!";
+ break;
+ }
+
+ ftl::ignore = binder->pingBinder();
+
+ constexpr auto kPollInterval = std::chrono::milliseconds(10);
+ std::this_thread::sleep_for(kPollInterval);
+ }
+
+ constexpr auto kShutdownWait = std::chrono::milliseconds(500);
+ std::this_thread::sleep_for(kShutdownWait);
+ }
+
+ mSurfaceComposerClient = nullptr;
+ mSurfaceComposerClientAidl = nullptr;
+ mSurfaceComposerAidl = nullptr;
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger
diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h
new file mode 100644
index 0000000000..58bac9197f
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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 <memory>
+#include <string>
+#include <string_view>
+
+#include <android-base/expected.h>
+#include <ftl/finalizer.h>
+#include <utils/StrongPointer.h>
+
+namespace android::gui {
+
+class ISurfaceComposer;
+class ISurfaceComposerClient;
+
+} // namespace android::gui
+
+namespace android {
+
+class SurfaceComposerClient;
+
+} // namespace android
+
+namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger {
+
+class SFController final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Sets a property so that SurfaceFlinger uses the named HWC service.
+ static void useHwcService(std::string_view fqn);
+
+ // Makes an instance of the SFController.
+ [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<SFController>, std::string>;
+
+ explicit SFController(Passkey pass);
+
+ // Starts SurfaceFlinger and establishes the AIDL interface connections.
+ [[nodiscard]] auto startAndConnect() -> base::expected<void, std::string>;
+
+ private:
+ [[nodiscard]] auto init() -> base::expected<void, std::string>;
+ static void start();
+ void stop();
+
+ sp<gui::ISurfaceComposer> mSurfaceComposerAidl;
+ sp<gui::ISurfaceComposerClient> mSurfaceComposerClientAidl;
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+ // Finalizers should be last so their destructors are invoked first.
+ ftl::FinalizerFtl mCleanup;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger
diff --git a/services/surfaceflinger/tests/end2end/tests/.clang-tidy b/services/surfaceflinger/tests/end2end/tests/.clang-tidy
new file mode 100644
index 0000000000..4924c466c0
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/tests/.clang-tidy
@@ -0,0 +1,32 @@
+# Copyright 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.
+
+FormatStyle: file
+InheritParentConfig: true
+
+# Note: For tests, we are actually turning off certain checks enabled for the
+# non-test code in the parent .clang-tidy file.
+Checks:
+ - -cppcoreguidelines-avoid-magic-numbers # Allow tests to use magic numbers.
+ - -cppcoreguidelines-avoid-goto # Google Test macros use goto.
+ - -cppcoreguidelines-avoid-non-const-global-variables # Google Test macros define global variables.
+ - -cppcoreguidelines-macro-usage # Google Benchmark defines function-like macros.
+ - -cppcoreguidelines-owning-memory # Google Test macros use operator new directly.
+ - -google-runtime-int # Tests might intentionally use the base "short"/"long" types and not want to use "int16"/"int64".
+ - -misc-use-anonymous-namespace # Google Test macros declare some static global variables to not export them.
+ - -modernize-use-trailing-return-type # Google Test macros use non-trailing return types.
+ - -performance-move-const-arg # Tests might std::move() a trivially copyable value as part of testing that moving works.
+ - -readability-function-cognitive-complexity # Assertions turn into extra branches, increasing apparent complexity.
+ - -readability-magic-numbers # Allow tests to use magic numbers
+
diff --git a/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp
new file mode 100644
index 0000000000..3c4277f8a0
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "test_framework/core/TestService.h"
+
+namespace android::surfaceflinger::tests::end2end {
+namespace {
+
+struct Placeholder : public ::testing::Test {};
+
+TEST_F(Placeholder, Bringup) {
+ auto serviceResult = test_framework::core::TestService::startWithDisplays({
+ {.id = 123},
+ });
+ if (!serviceResult) {
+ LOG(WARNING) << "End2End service not available. " << serviceResult.error();
+ GTEST_SKIP() << "End2End service not available. " << serviceResult.error();
+ }
+}
+
+} // namespace
+} // namespace android::surfaceflinger::tests::end2end
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 6cc6322bfc..9c143fdd41 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -45,28 +45,15 @@ protected:
void setTransactionState() {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
TransactionInfo transaction;
- mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays,
- transaction.flags, transaction.applyToken,
- transaction.inputWindowCommands,
- TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp,
- transaction.unCachedBuffers,
- /*HasListenerCallbacks=*/false, transaction.callbacks,
- transaction.id, transaction.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transaction));
}
- struct TransactionInfo {
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags = 0;
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- InputWindowCommands inputWindowCommands;
- int64_t desiredPresentTime = 0;
- bool isAutoTimestamp = false;
- FrameTimelineInfo frameTimelineInfo{};
- std::vector<client_cache_t> unCachedBuffers;
- uint64_t id = static_cast<uint64_t>(-1);
- std::vector<uint64_t> mergedTransactionIds;
- std::vector<ListenerCallbacks> callbacks;
+ struct TransactionInfo : public TransactionState {
+ TransactionInfo() {
+ mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ mIsAutoTimestamp = false;
+ mId = static_cast<uint64_t>(-1);
+ }
};
struct Compositor final : ICompositor {
@@ -383,4 +370,4 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
}
}
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
index 2332bf62da..d5c22a9601 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
@@ -17,7 +17,6 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
-#include <android_companion_virtualdevice_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <common/test/FlagUtils.h>
#include "DisplayTransactionTestHelpers.h"
@@ -79,19 +78,11 @@ struct EventThreadBaseSupportedVariant {
struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
- setupDisableSyntheticVsyncCallExpectations(test);
- }
-
- static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
- setupEnableSyntheticVsyncCallExpectations(test);
- }
-
- static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
};
@@ -100,20 +91,12 @@ struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
// Expect to enable hardware VSYNC and disable synthetic VSYNC.
EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
- setupDisableSyntheticVsyncCallExpectations(test);
- }
-
- static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1);
}
static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
// Expect to disable hardware VSYNC and enable synthetic VSYNC.
EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
- setupEnableSyntheticVsyncCallExpectations(test);
- }
-
- static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
}
};
@@ -168,7 +151,7 @@ struct TransitionOffToDozeSuspendVariant
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupRepaintEverythingCallExpectations(test);
}
@@ -193,7 +176,7 @@ struct TransitionDozeSuspendToOffVariant
: public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
}
@@ -205,7 +188,7 @@ struct TransitionDozeSuspendToOffVariant
struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
}
};
@@ -223,7 +206,7 @@ struct TransitionDozeSuspendToDozeVariant
struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
}
};
@@ -251,7 +234,7 @@ struct TransitionOnToUnknownVariant
: public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
template <typename Case>
static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
+ Case::EventThread::setupVsyncNoCallExpectations(test);
Case::setupNoComposerPowerModeCallExpectations(test);
}
};
@@ -352,9 +335,6 @@ void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() {
// --------------------------------------------------------------------
// Preconditions
- SET_FLAG_FOR_TEST(android::companion::virtualdevice::flags::correct_virtual_display_power_state,
- true);
-
Case::Doze::setupComposerCallExpectations(this);
auto display =
Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c5973db109..13c32bdf08 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -519,18 +519,8 @@ public:
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
- auto setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
- return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
- inputWindowCommands, desiredPresentTime,
- isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId,
- mergedTransactionIds);
+ auto setTransactionState(TransactionState&& state) {
+ return mFlinger->setTransactionState(std::move(state));
}
auto setTransactionStateInternal(QueuedTransactionState& transaction) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 69dfcc4a8f..1395fb6af3 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,6 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "TransactionApplicationTest"
+#include <cstdint>
+
#include <binder/Binder.h>
#include <common/test/FlagUtils.h>
#include <compositionengine/Display.h>
@@ -69,38 +71,32 @@ public:
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
- struct TransactionInfo {
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags = 0;
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- InputWindowCommands inputWindowCommands;
- int64_t desiredPresentTime = 0;
- bool isAutoTimestamp = true;
- FrameTimelineInfo frameTimelineInfo;
- std::vector<client_cache_t> uncacheBuffers;
- uint64_t id = static_cast<uint64_t>(-1);
- std::vector<uint64_t> mergedTransactionIds;
- static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
+ struct TransactionInfo : public TransactionState {
+ TransactionInfo() {
+ mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ mId = static_cast<uint64_t>(-1);
+ }
};
- void checkEqual(TransactionInfo info, QueuedTransactionState state) {
- EXPECT_EQ(0u, info.states.size());
+ void checkEqual(const TransactionInfo& info, const QueuedTransactionState& state) {
+ EXPECT_EQ(0u, info.mComposerStates.size());
EXPECT_EQ(0u, state.states.size());
- EXPECT_EQ(0u, info.displays.size());
+ EXPECT_EQ(0u, info.mDisplayStates.size());
EXPECT_EQ(0u, state.displays.size());
- EXPECT_EQ(info.flags, state.flags);
- EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
+ EXPECT_EQ(info.mFlags, state.flags);
+ EXPECT_EQ(info.mDesiredPresentTime, state.desiredPresentTime);
}
void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
mTransactionNumber++;
- transaction.flags |= flags;
- transaction.desiredPresentTime = desiredPresentTime;
- transaction.isAutoTimestamp = isAutoTimestamp;
- transaction.frameTimelineInfo = frameTimelineInfo;
+ transaction.mFlags |= flags;
+ transaction.mDesiredPresentTime = desiredPresentTime;
+ transaction.mIsAutoTimestamp = isAutoTimestamp;
+ transaction.mFrameTimelineInfo = frameTimelineInfo;
+ transaction.mHasListenerCallbacks = mHasListenerCallbacks;
+ transaction.mListenerCallbacks = mCallbacks;
}
void NotPlacedOnTransactionQueue(uint32_t flags) {
@@ -111,12 +107,7 @@ public:
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
FrameTimelineInfo{});
nsecs_t applicationTime = systemTime();
- mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id, transaction.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transaction));
// If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
// SF to commit the transaction. If this is animation, it should not time out waiting.
@@ -138,12 +129,7 @@ public:
setupSingle(transaction, flags, /*desiredPresentTime*/ time + s2ns(1), false,
FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id, transaction.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transaction));
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
@@ -169,12 +155,7 @@ public:
/*isAutoTimestamp*/ true, FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
- transactionA.displays, transactionA.flags,
- transactionA.applyToken, transactionA.inputWindowCommands,
- transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
- transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionA.id, transactionA.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionA));
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -184,12 +165,7 @@ public:
mFlinger.flushTransactionQueues();
applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
- transactionB.displays, transactionB.flags,
- transactionB.applyToken, transactionB.inputWindowCommands,
- transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
- transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionB.id, transactionB.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionB));
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -222,12 +198,7 @@ TEST_F(TransactionApplicationTest, AddToPendingQueue) {
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
FrameTimelineInfo{});
- mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
- transactionA.displays, transactionA.flags, transactionA.applyToken,
- transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id,
- transactionA.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionA));
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -243,12 +214,7 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
FrameTimelineInfo{});
- mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
- transactionA.displays, transactionA.flags, transactionA.applyToken,
- transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id,
- transactionA.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionA));
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -257,12 +223,10 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
// transaction here (sending a null applyToken to fake it as from a
// different process) to re-query and reset the cached expected present time
TransactionInfo empty;
- empty.applyToken = sp<IBinder>();
- mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
- empty.applyToken, empty.inputWindowCommands,
- empty.desiredPresentTime, empty.isAutoTimestamp,
- empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id,
- empty.mergedTransactionIds);
+ empty.mApplyToken = sp<IBinder>();
+ empty.mHasListenerCallbacks = mHasListenerCallbacks;
+ empty.mListenerCallbacks = mCallbacks;
+ mFlinger.setTransactionState(std::move(empty));
// flush transaction queue should flush as desiredPresentTime has
// passed
@@ -406,9 +370,9 @@ public:
const auto kFrameTimelineInfo = FrameTimelineInfo{};
setupSingle(transaction, kFlags, kDesiredPresentTime, kIsAutoTimestamp, kFrameTimelineInfo);
- transaction.applyToken = applyToken;
+ transaction.mApplyToken = applyToken;
for (const auto& state : states) {
- transaction.states.push_back(state);
+ transaction.mComposerStates.push_back(state);
}
return transaction;
@@ -419,8 +383,8 @@ public:
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
std::unordered_set<uint32_t> createdLayers;
- for (auto transaction : transactions) {
- for (auto& state : transaction.states) {
+ for (auto& transaction : transactions) {
+ for (auto& state : transaction.mComposerStates) {
auto layerId = static_cast<uint32_t>(state.state.layerId);
if (createdLayers.find(layerId) == createdLayers.end()) {
mFlinger.addLayer(layerId);
@@ -434,8 +398,8 @@ public:
for (auto transaction : transactions) {
std::vector<ResolvedComposerState> resolvedStates;
- resolvedStates.reserve(transaction.states.size());
- for (auto& state : transaction.states) {
+ resolvedStates.reserve(transaction.mComposerStates.size());
+ for (auto& state : transaction.mComposerStates) {
ResolvedComposerState resolvedState;
resolvedState.state = std::move(state.state);
resolvedState.externalTexture =
@@ -446,15 +410,9 @@ public:
resolvedStates.emplace_back(resolvedState);
}
- QueuedTransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
- transaction.displays, transaction.flags,
- transaction.applyToken,
- transaction.inputWindowCommands,
- transaction.desiredPresentTime,
- transaction.isAutoTimestamp, {}, systemTime(),
- mHasListenerCallbacks, mCallbacks, getpid(),
- static_cast<int>(getuid()), transaction.id,
- transaction.mergedTransactionIds);
+ QueuedTransactionState transactionState(std::move(transaction),
+ std::move(resolvedStates), {}, systemTime(),
+ getpid(), static_cast<int>(getuid()));
mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index d3eec5c6f3..b36ad213c8 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -66,7 +66,7 @@ TEST(TransactionProtoParserTest, parse) {
display.token = nullptr;
}
display.width = 85;
- t1.displays.add(display);
+ t1.displays.push_back(display);
}
class TestMapper : public TransactionProtoParser::FlingerDataMapper {
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 21ee071f1b..3589553be0 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -22,6 +22,8 @@
#include <chrono>
#include <thread>
+#include <scheduler/Time.h>
+
#include "Scheduler/VsyncConfiguration.h"
using namespace testing;
@@ -39,6 +41,10 @@ public:
: impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration,
appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration,
hwcMinWorkDuration) {}
+
+ TestableWorkDuration(Fps currentFps, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration)
+ : impl::WorkDuration(currentFps, minSfDuration, maxSfDuration, appDuration) {}
};
class WorkDurationTest : public testing::Test {
@@ -168,6 +174,35 @@ TEST_F(WorkDurationTest, minHwcWorkDuration) {
EXPECT_EQ(mWorkDuration.getCurrentConfigs().hwcMinWorkDuration, 1234ns);
}
+TEST_F(WorkDurationTest, workDurationIsARange) {
+ const Duration minSfDuration = Duration::fromNs(10'500'000);
+ const Duration maxSfDuration = Duration::fromNs(20'500'000);
+ const Duration appDuration = Duration::fromNs(16'000'000);
+ const TestableWorkDuration workDuration{60_Hz, minSfDuration, maxSfDuration, appDuration};
+
+ auto currentOffsets = workDuration.getCurrentConfigs();
+ auto offsets = workDuration.getConfigsForRefreshRate(60_Hz);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+ EXPECT_EQ(offsets.late.appOffset, 6'833'334);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 16'000'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, -3'833'333);
+ EXPECT_EQ(offsets.early.appOffset, 13'500'001);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 20'500'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'000'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, -3'833'333);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 13'500'001);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 20'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'000'000ns);
+}
+
class TestablePhaseOffsets : public impl::PhaseOffsets {
public:
TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 7319f1ee22..00e4cc6818 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -190,6 +190,8 @@ public:
MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*));
MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id));
MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id));
+ MOCK_METHOD(Error, startHdcpNegotiation,
+ (Display, const aidl::android::hardware::drm::HdcpLevels& levels));
MOCK_METHOD(Error, getLuts,
(Display, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 4ca6fe073b..a20b9e1f0d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -116,6 +116,8 @@ public:
MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override));
MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
(override));
+ MOCK_METHOD(hal::Error, startHdcpNegotiation,
+ (const aidl::android::hardware::drm::HdcpLevels& levels), (override));
MOCK_METHOD(hal::Error, getLuts,
(const std::vector<android::sp<android::GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*),
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
index 01d078bbf1..449c45bc0a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -152,6 +152,8 @@ public:
MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId));
MOCK_METHOD(status_t, setDisplayPictureProfileHandle,
(PhysicalDisplayId, const PictureProfileHandle&));
+ MOCK_METHOD(status_t, startHdcpNegotiation,
+ (PhysicalDisplayId, const aidl::android::hardware::drm::HdcpLevels&));
MOCK_METHOD(status_t, getLuts,
(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
std::vector<aidl::android::hardware::graphics::composer3::Luts>*));