Merge "[sf] Add readme for surfaceflinger frontend" into udc-dev
diff --git a/include/input/Input.h b/include/input/Input.h
index 1e810b4..fe0c775 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -30,7 +30,6 @@
#include <stdint.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
-#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <array>
#include <limits>
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index eaf3b5e..d50c5f8 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
#define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
+#include <stdint.h>
+
__BEGIN_DECLS
/**
@@ -27,7 +29,7 @@
/**
* Hints for the session used to signal upcoming changes in the mode or workload.
*/
-enum SessionHint {
+enum SessionHint: int32_t {
/**
* This hint indicates a sudden increase in CPU workload intensity. It means
* that this hint session needs extra CPU resources immediately to meet the
@@ -61,7 +63,7 @@
* @return 0 on success
* EPIPE if communication with the system service has failed.
*/
-int APerformanceHint_sendHint(void* session, int hint);
+int APerformanceHint_sendHint(void* session, SessionHint hint);
/**
* Return the list of thread ids, this API should only be used for testing only.
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 84900a7..f2b4a6e 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -190,7 +190,8 @@
data.writeInt32(apiType);
data.writeInt32(appUid);
data.writeInt32(appPid);
- status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply);
+ status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply,
+ IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
ALOGD("FGS Logger Transaction failed");
ALOGD("%d", err);
@@ -205,7 +206,8 @@
data.writeInt32(apiType);
data.writeInt32(appUid);
data.writeInt32(appPid);
- status_t err = remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply);
+ status_t err =
+ remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
ALOGD("FGS Logger Transaction failed");
ALOGD("%d", err);
@@ -222,7 +224,8 @@
data.writeInt32(state);
data.writeInt32(appUid);
data.writeInt32(appPid);
- status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply);
+ status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply,
+ IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
ALOGD("FGS Logger Transaction failed");
ALOGD("%d", err);
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 80fed98..bf34987 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -217,6 +217,7 @@
"DebugEGLImageTracker.cpp",
"DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
+ "FenceMonitor.cpp",
"GLConsumer.cpp",
"IConsumerListener.cpp",
"IGraphicBufferConsumer.cpp",
diff --git a/libs/gui/FenceMonitor.cpp b/libs/gui/FenceMonitor.cpp
new file mode 100644
index 0000000..230c81a
--- /dev/null
+++ b/libs/gui/FenceMonitor.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/FenceMonitor.h>
+#include <gui/TraceUtils.h>
+#include <log/log.h>
+
+#include <thread>
+
+namespace android::gui {
+
+FenceMonitor::FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) {
+ std::thread thread(&FenceMonitor::loop, this);
+ pthread_setname_np(thread.native_handle(), mName);
+ thread.detach();
+}
+
+void FenceMonitor::queueFence(const sp<Fence>& fence) {
+ char message[64];
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued);
+ ATRACE_NAME(message);
+ // Need an increment on both to make the trace number correct.
+ mFencesQueued++;
+ mFencesSignaled++;
+ return;
+ }
+ snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued);
+ ATRACE_NAME(message);
+
+ mQueue.push_back(fence);
+ mCondition.notify_one();
+ mFencesQueued++;
+ ATRACE_INT(mName, int32_t(mQueue.size()));
+}
+
+void FenceMonitor::loop() {
+ while (true) {
+ threadLoop();
+ }
+}
+
+void FenceMonitor::threadLoop() {
+ sp<Fence> fence;
+ uint32_t fenceNum;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+ fence = mQueue[0];
+ fenceNum = mFencesSignaled;
+ }
+ {
+ char message[64];
+ snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum);
+ ATRACE_NAME(message);
+
+ status_t result = fence->waitForever(message);
+ if (result != OK) {
+ ALOGE("Error waiting for fence: %d", result);
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.pop_front();
+ mFencesSignaled++;
+ ATRACE_INT(mName, int32_t(mQueue.size()));
+ }
+}
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index d72f65e..b526a6c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -59,15 +59,13 @@
virtual ~BpSurfaceComposer();
- status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& state, const 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) override {
+ status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ const 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 {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -103,6 +101,11 @@
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) {
return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
data, &reply, IBinder::FLAG_ONEWAY);
@@ -187,10 +190,16 @@
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);
+ listenerCallbacks, transactionId, mergedTransactions);
}
default: {
return BBinder::onTransact(code, data, reply, flags);
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index a5cf8d6..ed69100 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -30,6 +30,7 @@
#include <android/gui/DisplayStatInfo.h>
#include <android/native_window.h>
+#include <gui/FenceMonitor.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
#include <utils/NativeHandle.h>
@@ -545,82 +546,6 @@
return NO_ERROR;
}
-class FenceMonitor {
-public:
- explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) {
- std::thread thread(&FenceMonitor::loop, this);
- pthread_setname_np(thread.native_handle(), mName);
- thread.detach();
- }
-
- void queueFence(const sp<Fence>& fence) {
- char message[64];
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
- snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued);
- ATRACE_NAME(message);
- // Need an increment on both to make the trace number correct.
- mFencesQueued++;
- mFencesSignaled++;
- return;
- }
- snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued);
- ATRACE_NAME(message);
-
- mQueue.push_back(fence);
- mCondition.notify_one();
- mFencesQueued++;
- ATRACE_INT(mName, int32_t(mQueue.size()));
- }
-
-private:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
- void loop() {
- while (true) {
- threadLoop();
- }
- }
-#pragma clang diagnostic pop
-
- void threadLoop() {
- sp<Fence> fence;
- uint32_t fenceNum;
- {
- std::unique_lock<std::mutex> lock(mMutex);
- while (mQueue.empty()) {
- mCondition.wait(lock);
- }
- fence = mQueue[0];
- fenceNum = mFencesSignaled;
- }
- {
- char message[64];
- snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum);
- ATRACE_NAME(message);
-
- status_t result = fence->waitForever(message);
- if (result != OK) {
- ALOGE("Error waiting for fence: %d", result);
- }
- }
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mQueue.pop_front();
- mFencesSignaled++;
- ATRACE_INT(mName, int32_t(mQueue.size()));
- }
- }
-
- const char* mName;
- uint32_t mFencesQueued;
- uint32_t mFencesSignaled;
- std::deque<sp<Fence>> mQueue;
- std::condition_variable mCondition;
- std::mutex mMutex;
-};
-
void Surface::getDequeueBufferInputLocked(
IGraphicBufferProducer::DequeueBufferInput* dequeueInput) {
LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null");
@@ -694,7 +619,7 @@
ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
- static FenceMonitor hwcReleaseThread("HWC release");
+ static gui::FenceMonitor hwcReleaseThread("HWC release");
hwcReleaseThread.queueFence(fence);
}
@@ -893,7 +818,7 @@
sp<GraphicBuffer>& gbuf(mSlots[slot].buffer);
if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
- static FenceMonitor hwcReleaseThread("HWC release");
+ static gui::FenceMonitor hwcReleaseThread("HWC release");
hwcReleaseThread.queueFence(output.fence);
}
@@ -1163,7 +1088,7 @@
mQueueBufferCondition.broadcast();
if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
- static FenceMonitor gpuCompletionThread("GPU completion");
+ static gui::FenceMonitor gpuCompletionThread("GPU completion");
gpuCompletionThread.queueFence(fence);
}
}
@@ -1871,12 +1796,14 @@
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
+ auto useForRefreshRateSelection = static_cast<bool>(va_arg(args, int32_t));
ALOGV("Surface::%s", __func__);
FrameTimelineInfo ftlInfo;
ftlInfo.vsyncId = frameTimelineVsyncId;
ftlInfo.inputEventId = inputEventId;
ftlInfo.startTimeNanos = startTimeNanos;
+ ftlInfo.useForRefreshRateSelection = useForRefreshRateSelection;
return setFrameTimelineInfo(frameNumber, ftlInfo);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1b13ec1..0fda358 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -827,6 +827,15 @@
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;
mTransactionNestCount = transactionNestCount;
@@ -842,6 +851,7 @@
mInputWindowCommands = inputWindowCommands;
mApplyToken = applyToken;
mUncacheBuffers = std::move(uncacheBuffers);
+ mMergedTransactionIds = std::move(mergedTransactionIds);
return NO_ERROR;
}
@@ -900,6 +910,11 @@
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;
}
@@ -924,6 +939,22 @@
}
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& [handle, composerState] : other.mComposerStates) {
if (mComposerStates.count(handle) == 0) {
mComposerStates[handle] = composerState;
@@ -998,12 +1029,17 @@
mIsAutoTimestamp = true;
clearFrameTimelineInfo(mFrameTimelineInfo);
mApplyToken = nullptr;
+ mMergedTransactionIds.clear();
}
uint64_t SurfaceComposerClient::Transaction::getId() {
return mId;
}
+std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() {
+ return mMergedTransactionIds;
+}
+
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
@@ -1014,7 +1050,7 @@
status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
ISurfaceComposer::eOneWay,
Transaction::getDefaultApplyToken(), {}, systemTime(),
- true, {uncacheBuffer}, false, {}, generateId());
+ true, {uncacheBuffer}, false, {}, generateId(), {});
if (status != NO_ERROR) {
ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
strerror(-status));
@@ -1189,7 +1225,8 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
- mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId);
+ mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId,
+ mMergedTransactionIds);
mId = generateId();
// Clear the current states and flags
@@ -2245,11 +2282,13 @@
t.vsyncId = other.vsyncId;
t.inputEventId = other.inputEventId;
t.startTimeNanos = other.startTimeNanos;
+ t.useForRefreshRateSelection = other.useForRefreshRateSelection;
}
} else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
t.vsyncId = other.vsyncId;
t.inputEventId = other.inputEventId;
t.startTimeNanos = other.startTimeNanos;
+ t.useForRefreshRateSelection = other.useForRefreshRateSelection;
}
}
@@ -2258,6 +2297,7 @@
t.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
t.inputEventId = os::IInputConstants::INVALID_INPUT_EVENT_ID;
t.startTimeNanos = 0;
+ t.useForRefreshRateSelection = false;
}
SurfaceComposerClient::Transaction&
diff --git a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
index 6ffe466..6a86c6a 100644
--- a/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
+++ b/libs/gui/aidl/android/gui/FrameTimelineInfo.aidl
@@ -33,4 +33,8 @@
// The current time in nanoseconds the application started to render the frame.
long startTimeNanos = 0;
+
+ // Whether this vsyncId should be used to heuristically select the display refresh rate
+ // TODO(b/281695725): Clean this up once TextureView use setFrameRate API
+ boolean useForRefreshRateSelection = false;
}
diff --git a/libs/gui/include/gui/FenceMonitor.h b/libs/gui/include/gui/FenceMonitor.h
new file mode 100644
index 0000000..62cedde
--- /dev/null
+++ b/libs/gui/include/gui/FenceMonitor.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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>
+#include <deque>
+#include <mutex>
+
+#include <ui/Fence.h>
+
+namespace android::gui {
+
+class FenceMonitor {
+public:
+ explicit FenceMonitor(const char* name);
+ void queueFence(const sp<Fence>& fence);
+
+private:
+ void loop();
+ void threadLoop();
+
+ const char* mName;
+ uint32_t mFencesQueued;
+ uint32_t mFencesSignaled;
+ std::deque<sp<Fence>> mQueue;
+ std::condition_variable mCondition;
+ std::mutex mMutex;
+};
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index bd21851..7c150d5 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -116,7 +116,7 @@
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId) = 0;
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8d2cdaf..fb57f63 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -419,6 +419,11 @@
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 mTransactionNestCount = 0;
@@ -482,6 +487,8 @@
// The id is updated every time the transaction is applied.
uint64_t getId();
+ std::vector<uint64_t> getMergedTransactionIds();
+
status_t apply(bool synchronous = false, bool oneWay = false);
// Merge another transaction in to this one, clearing other
// as if it had been applied.
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5bc6904..096a43c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -695,16 +695,14 @@
mSupportsPresent = supportsPresent;
}
- status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/,
- Vector<ComposerState>& /*state*/,
- const 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*/) override {
+ status_t setTransactionState(
+ const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
+ const 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;
}
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index af9ce3a..9a459b1 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -49,11 +49,10 @@
std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(eventTime);
std::chrono::microseconds microseconds =
std::chrono::duration_cast<std::chrono::microseconds>(eventTime - seconds);
- struct input_event ev = {.type = type,
- .code = code,
- .value = value,
- .input_event_sec = static_cast<time_t>(seconds.count()),
- .input_event_usec = static_cast<suseconds_t>(microseconds.count())};
+ struct input_event ev = {.type = type, .code = code, .value = value};
+ ev.input_event_sec = static_cast<decltype(ev.input_event_sec)>(seconds.count());
+ ev.input_event_usec = static_cast<decltype(ev.input_event_usec)>(microseconds.count());
+
return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
}
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 6c54635..0fee3c1 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1066,13 +1066,12 @@
(int)compatibility, (int)changeFrameRateStrategy);
}
-static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
- uint64_t frameNumber,
- int64_t frameTimelineVsyncId,
- int32_t inputEventId,
- int64_t startTimeNanos) {
+static inline int native_window_set_frame_timeline_info(
+ struct ANativeWindow* window, uint64_t frameNumber, int64_t frameTimelineVsyncId,
+ int32_t inputEventId, int64_t startTimeNanos, int32_t useForRefreshRateSelection) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
- frameTimelineVsyncId, inputEventId, startTimeNanos);
+ frameTimelineVsyncId, inputEventId, startTimeNanos,
+ useForRefreshRateSelection);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp
index baa9d75..6959274 100644
--- a/libs/permission/AppOpsManager.cpp
+++ b/libs/permission/AppOpsManager.cpp
@@ -146,6 +146,14 @@
}
}
+void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, int32_t flags,
+ const sp<IAppOpsCallback>& callback) {
+ sp<IAppOpsService> service = getService();
+ if (service != nullptr) {
+ service->startWatchingModeWithFlags(op, packageName, flags, callback);
+ }
+}
+
void AppOpsManager::stopWatchingMode(const sp<IAppOpsCallback>& callback) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp
index d59f445..7f235a4 100644
--- a/libs/permission/IAppOpsService.cpp
+++ b/libs/permission/IAppOpsService.cpp
@@ -166,6 +166,17 @@
}
return reply.readBool();
}
+
+ virtual void startWatchingModeWithFlags(int32_t op, const String16& packageName,
+ int32_t flags, const sp<IAppOpsCallback>& callback) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(op);
+ data.writeString16(packageName);
+ data.writeInt32(flags);
+ data.writeStrongBinder(IInterface::asBinder(callback));
+ remote()->transact(START_WATCHING_MODE_WITH_FLAGS_TRANSACTION, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService")
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index abcd527..243532b 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -151,6 +151,10 @@
_NUM_OP = 117
};
+ enum {
+ WATCH_FOREGROUND_CHANGES = 1 << 0
+ };
+
AppOpsManager();
int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
@@ -174,6 +178,8 @@
const std::optional<String16>& attributionTag);
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback);
+ void startWatchingMode(int32_t op, const String16& packageName, int32_t flags,
+ const sp<IAppOpsCallback>& callback);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
int32_t permissionToOpCode(const String16& permission);
void setCameraAudioRestriction(int32_t mode);
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index 22f056b..918fcdb 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -52,6 +52,8 @@
const String16& packageName) = 0;
virtual void setCameraAudioRestriction(int32_t mode) = 0;
virtual bool shouldCollectNotes(int32_t opCode) = 0;
+ virtual void startWatchingModeWithFlags(int32_t op, const String16& packageName,
+ int32_t flags, const sp<IAppOpsCallback>& callback) = 0;
enum {
CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -64,6 +66,7 @@
CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
+ START_WATCHING_MODE_WITH_FLAGS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
};
enum {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index cfea85f..5854135 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -39,10 +39,10 @@
#include <SkPath.h>
#include <SkPoint.h>
#include <SkPoint3.h>
+#include <SkRRect.h>
#include <SkRect.h>
#include <SkRefCnt.h>
#include <SkRegion.h>
-#include <SkRRect.h>
#include <SkRuntimeEffect.h>
#include <SkSamplingOptions.h>
#include <SkScalar.h>
@@ -51,9 +51,11 @@
#include <SkString.h>
#include <SkSurface.h>
#include <SkTileMode.h>
-#include <src/core/SkTraceEventCommon.h>
#include <android-base/stringprintf.h>
+#include <gui/FenceMonitor.h>
#include <gui/TraceUtils.h>
+#include <pthread.h>
+#include <src/core/SkTraceEventCommon.h>
#include <sync/sync.h>
#include <ui/BlurRegion.h>
#include <ui/DataspaceUtils.h>
@@ -63,6 +65,7 @@
#include <cmath>
#include <cstdint>
+#include <deque>
#include <memory>
#include <numeric>
@@ -229,7 +232,6 @@
static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
return SkPoint3::Make(vector.x, vector.y, vector.z);
}
-
} // namespace
namespace android {
@@ -1134,8 +1136,13 @@
activeSurface->flush();
}
- base::unique_fd drawFence = flushAndSubmit(grContext);
- resultPromise->set_value(sp<Fence>::make(std::move(drawFence)));
+ auto drawFence = sp<Fence>::make(flushAndSubmit(grContext));
+
+ if (ATRACE_ENABLED()) {
+ static gui::FenceMonitor sMonitor("RE Completion");
+ sMonitor.queueFence(drawFence);
+ }
+ resultPromise->set_value(std::move(drawFence));
}
size_t SkiaRenderEngine::getMaxTextureSize() const {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f3ada8e..9125fe4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -681,6 +681,25 @@
return left;
}
+// Filter windows in a TouchState and targets in a vector to remove untrusted windows/targets from
+// both.
+void filterUntrustedTargets(TouchState& touchState, std::vector<InputTarget>& targets) {
+ std::erase_if(touchState.windows, [&](const TouchedWindow& window) {
+ if (!window.windowHandle->getInfo()->inputConfig.test(
+ WindowInfo::InputConfig::TRUSTED_OVERLAY)) {
+ // In addition to TouchState, erase this window from the input targets! We don't have a
+ // good way to do this today except by adding a nested loop.
+ // TODO(b/282025641): simplify this code once InputTargets are being identified
+ // separately from TouchedWindows.
+ std::erase_if(targets, [&](const InputTarget& target) {
+ return target.inputChannel->getConnectionToken() == window.windowHandle->getToken();
+ });
+ return true;
+ }
+ return false;
+ });
+}
+
} // namespace
// --- InputDispatcher ---
@@ -2588,6 +2607,14 @@
}
}
+ // If this is a touchpad navigation gesture, it needs to only be sent to trusted targets, as we
+ // only want the system UI to handle these gestures.
+ const bool isTouchpadNavGesture = isFromSource(entry.source, AINPUT_SOURCE_MOUSE) &&
+ entry.classification == MotionClassification::MULTI_FINGER_SWIPE;
+ if (isTouchpadNavGesture) {
+ filterUntrustedTargets(/* byref */ tempTouchState, /* byref */ targets);
+ }
+
// Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
@@ -2595,6 +2622,7 @@
// Do not send this event to those windows.
continue;
}
+
addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
targets);
@@ -2783,6 +2811,30 @@
}
}
+std::optional<InputTarget> InputDispatcher::createInputTargetLocked(
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::optional<nsecs_t> firstDownTimeInTarget) const {
+ std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str());
+ return {};
+ }
+ InputTarget inputTarget;
+ inputTarget.inputChannel = inputChannel;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
+ inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
+ const auto& displayInfoIt = mDisplayInfos.find(windowHandle->getInfo()->displayId);
+ if (displayInfoIt != mDisplayInfos.end()) {
+ inputTarget.displayTransform = displayInfoIt->second.transform;
+ } else {
+ // DisplayInfo not found for this window on display windowInfo->displayId.
+ // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
+ }
+ return inputTarget;
+}
+
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
@@ -2798,25 +2850,12 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
- InputTarget inputTarget;
- std::shared_ptr<InputChannel> inputChannel =
- getInputChannelLocked(windowHandle->getToken());
- if (inputChannel == nullptr) {
- ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ std::optional<InputTarget> target =
+ createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
+ if (!target) {
return;
}
- inputTarget.inputChannel = inputChannel;
- inputTarget.flags = targetFlags;
- inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
- inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
- const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId);
- if (displayInfoIt != mDisplayInfos.end()) {
- inputTarget.displayTransform = displayInfoIt->second.transform;
- } else {
- // DisplayInfo not found for this window on display windowInfo->displayId.
- // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
- }
- inputTargets.push_back(inputTarget);
+ inputTargets.push_back(*target);
it = inputTargets.end() - 1;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9b12f2f..0e9cfef 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -556,6 +556,10 @@
std::vector<Monitor> selectResponsiveMonitorsLocked(
const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
+ std::optional<InputTarget> createInputTargetLocked(
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0eb4ad2..0354164 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1072,16 +1072,8 @@
Device* device = getDeviceLocked(deviceId);
if (device != nullptr && device->keyMap.haveKeyLayout()) {
for (size_t codeIndex = 0; codeIndex < keyCodes.size(); codeIndex++) {
- std::vector<int32_t> scanCodes =
- device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]);
-
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (const int32_t scanCode : scanCodes) {
- if (device->keyBitmask.test(scanCode)) {
- outFlags[codeIndex] = 1;
- break;
- }
+ if (device->hasKeycodeLocked(keyCodes[codeIndex])) {
+ outFlags[codeIndex] = 1;
}
}
return true;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 721bb0a..c8c5115 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -446,17 +446,17 @@
// Switch-like devices.
if (classes.test(InputDeviceClass::SWITCH)) {
- mappers.push_back(std::make_unique<SwitchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<SwitchInputMapper>(contextPtr, readerConfig));
}
// Scroll wheel-like devices.
if (classes.test(InputDeviceClass::ROTARY_ENCODER)) {
- mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<RotaryEncoderInputMapper>(contextPtr, readerConfig));
}
// Vibrator-like devices.
if (classes.test(InputDeviceClass::VIBRATOR)) {
- mappers.push_back(std::make_unique<VibratorInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<VibratorInputMapper>(contextPtr, readerConfig));
}
// Battery-like devices or light-containing devices.
@@ -488,7 +488,7 @@
// Cursor-like devices.
if (classes.test(InputDeviceClass::CURSOR)) {
- mappers.push_back(std::make_unique<CursorInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<CursorInputMapper>(contextPtr, readerConfig));
}
// Touchscreens and touchpad devices.
@@ -501,21 +501,21 @@
(identifier.product == 0x05c4 || identifier.product == 0x09cc);
if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
- mappers.push_back(std::make_unique<TouchpadInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
- mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH)) {
- mappers.push_back(createInputMapper<SingleTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
}
// Joystick-like devices.
if (classes.test(InputDeviceClass::JOYSTICK)) {
- mappers.push_back(std::make_unique<JoystickInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<JoystickInputMapper>(contextPtr, readerConfig));
}
// Motion sensor enabled devices.
if (classes.test(InputDeviceClass::SENSOR)) {
- mappers.push_back(std::make_unique<SensorInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<SensorInputMapper>(contextPtr, readerConfig));
}
// External stylus-like devices.
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index dab4661..061c6a3 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -199,6 +199,29 @@
}
}
+ // Send BUTTON_RELEASE events. (This has to happen before any UP events to avoid sending
+ // BUTTON_RELEASE events without any pointers.)
+ uint32_t newButtonState;
+ if (coords.size() - upSlots.size() + downSlots.size() == 0) {
+ // If there won't be any pointers down after this evdev sync, we won't be able to send
+ // button updates on their own, as motion events without pointers are invalid. To avoid
+ // erroneously reporting buttons being held for long periods, send BUTTON_RELEASE events for
+ // all pressed buttons when the last pointer is lifted.
+ //
+ // This also prevents us from sending BUTTON_PRESS events too early in the case of touchpads
+ // which report a button press one evdev sync before reporting a touch going down.
+ newButtonState = 0;
+ } else {
+ newButtonState = mCursorButtonAccumulator.getButtonState();
+ }
+ for (uint32_t button = 1; button <= AMOTION_EVENT_BUTTON_FORWARD; button <<= 1) {
+ if (!(newButtonState & button) && mButtonState & button) {
+ mButtonState &= ~button;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ coords, properties, /*actionButton=*/button));
+ }
+ }
+
// For any touches that were lifted, send UP or POINTER_UP events.
for (size_t slotNumber : upSlots) {
const size_t indexToRemove = coordsIndexForSlotNumber.at(slotNumber);
@@ -240,16 +263,11 @@
out.push_back(makeMotionArgs(when, readTime, action, coords, properties));
}
- const uint32_t newButtonState = mCursorButtonAccumulator.getButtonState();
for (uint32_t button = 1; button <= AMOTION_EVENT_BUTTON_FORWARD; button <<= 1) {
if (newButtonState & button && !(mButtonState & button)) {
mButtonState |= button;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, coords,
properties, /*actionButton=*/button));
- } else if (!(newButtonState & button) && mButtonState & button) {
- mButtonState &= ~button;
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- coords, properties, /*actionButton=*/button));
}
}
return out;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 8ef5ff6..c684ed4 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -71,9 +71,7 @@
CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
: InputMapper(deviceContext, readerConfig),
- mLastEventTime(std::numeric_limits<nsecs_t>::min()) {
- configureWithZeroChanges(readerConfig);
-}
+ mLastEventTime(std::numeric_limits<nsecs_t>::min()) {}
CursorInputMapper::~CursorInputMapper() {
if (mPointerController != nullptr) {
@@ -142,46 +140,51 @@
ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, readerConfig, changes);
- if (!changes.any()) {
- configureWithZeroChanges(readerConfig);
- return out;
+ if (!changes.any()) { // first time only
+ configureBasicParams();
}
- const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
- changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE);
+ const bool configurePointerCapture = !changes.any() ||
+ (mParameters.mode != Parameters::Mode::NAVIGATION &&
+ changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE));
if (configurePointerCapture) {
configureOnPointerCapture(readerConfig);
out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
- if (changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || configurePointerCapture) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) ||
+ configurePointerCapture) {
configureOnChangePointerSpeed(readerConfig);
}
- if (changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) ||
+ configurePointerCapture) {
configureOnChangeDisplayInfo(readerConfig);
}
return out;
}
-void CursorInputMapper::configureParameters() {
- mParameters.mode = Parameters::Mode::POINTER;
- const PropertyMap& config = getDeviceContext().getConfiguration();
+CursorInputMapper::Parameters CursorInputMapper::computeParameters(
+ const InputDeviceContext& deviceContext) {
+ Parameters parameters;
+ parameters.mode = Parameters::Mode::POINTER;
+ const PropertyMap& config = deviceContext.getConfiguration();
std::optional<std::string> cursorModeString = config.getString("cursor.mode");
if (cursorModeString.has_value()) {
if (*cursorModeString == "navigation") {
- mParameters.mode = Parameters::Mode::NAVIGATION;
+ parameters.mode = Parameters::Mode::NAVIGATION;
} else if (*cursorModeString != "pointer" && *cursorModeString != "default") {
ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString->c_str());
}
}
- mParameters.orientationAware = config.getBool("cursor.orientationAware").value_or(false);
+ parameters.orientationAware = config.getBool("cursor.orientationAware").value_or(false);
- mParameters.hasAssociatedDisplay = false;
- if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
- mParameters.hasAssociatedDisplay = true;
+ parameters.hasAssociatedDisplay = false;
+ if (parameters.mode == Parameters::Mode::POINTER || parameters.orientationAware) {
+ parameters.hasAssociatedDisplay = true;
}
+ return parameters;
}
void CursorInputMapper::dumpParameters(std::string& dump) {
@@ -424,22 +427,11 @@
return mDisplayId;
}
-void CursorInputMapper::configureWithZeroChanges(const InputReaderConfiguration& readerConfig) {
- // Configuration with zero changes
- configureBasicParams();
- if (mParameters.mode != Parameters::Mode::NAVIGATION &&
- readerConfig.pointerCaptureRequest.enable) {
- configureOnPointerCapture(readerConfig);
- }
- configureOnChangePointerSpeed(readerConfig);
- configureOnChangeDisplayInfo(readerConfig);
-}
-
void CursorInputMapper::configureBasicParams() {
mCursorScrollAccumulator.configure(getDeviceContext());
// Configure basic parameters.
- configureParameters();
+ mParameters = computeParameters(getDeviceContext());
// Configure device mode.
switch (mParameters.mode) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index caf2e5a..b879bfd 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -53,8 +53,10 @@
class CursorInputMapper : public InputMapper {
public:
- explicit CursorInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
virtual ~CursorInputMapper();
virtual uint32_t getSources() const override;
@@ -125,15 +127,17 @@
nsecs_t mDownTime;
nsecs_t mLastEventTime;
- void configureParameters();
+ explicit CursorInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
void dumpParameters(std::string& dump);
- void configureWithZeroChanges(const InputReaderConfiguration& readerConfig);
void configureBasicParams();
void configureOnPointerCapture(const InputReaderConfiguration& config);
void configureOnChangePointerSpeed(const InputReaderConfiguration& config);
void configureOnChangeDisplayInfo(const InputReaderConfiguration& config);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+
+ static Parameters computeParameters(const InputDeviceContext& deviceContext);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 49673a2..313f092 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -22,8 +22,10 @@
class JoystickInputMapper : public InputMapper {
public:
- explicit JoystickInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
virtual ~JoystickInputMapper();
virtual uint32_t getSources() const override;
@@ -87,6 +89,9 @@
}
};
+ explicit JoystickInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
+
static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo,
bool explicitlyMapped);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 1d788df..f300ee1 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -27,6 +27,8 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
+ explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
~MultiTouchInputMapper() override;
@@ -39,8 +41,6 @@
bool hasStylus() const override;
private:
- explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
// simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
// mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
// It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index d3dcbe1..9e2e8c4 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -25,8 +25,10 @@
class RotaryEncoderInputMapper : public InputMapper {
public:
- explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
virtual ~RotaryEncoderInputMapper();
virtual uint32_t getSources() const override;
@@ -45,6 +47,8 @@
float mScalingFactor;
ui::Rotation mOrientation;
+ explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
};
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 1f82559..a55dcd1 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -27,8 +27,10 @@
class SensorInputMapper : public InputMapper {
public:
- explicit SensorInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
~SensorInputMapper() override;
uint32_t getSources() const override;
@@ -106,6 +108,9 @@
}
};
+ explicit SensorInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
+
static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo);
// Axes indexed by raw ABS_* axis index.
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 7726bfb..dac53cf 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,6 +27,8 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
+ explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
~SingleTouchInputMapper() override;
@@ -40,8 +42,6 @@
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
- explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 7ec282b..2fb48bb 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -22,8 +22,10 @@
class SwitchInputMapper : public InputMapper {
public:
- explicit SwitchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
virtual ~SwitchInputMapper();
virtual uint32_t getSources() const override;
@@ -36,6 +38,8 @@
uint32_t mSwitchValues;
uint32_t mUpdatedSwitchMask;
+ explicit SwitchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
void processSwitch(int32_t switchCode, int32_t switchValue);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when);
};
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 3128d18..23d0fd3 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -40,8 +40,10 @@
class TouchpadInputMapper : public InputMapper {
public:
- explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
~TouchpadInputMapper();
uint32_t getSources() const override;
@@ -58,6 +60,8 @@
private:
void resetGestureInterpreter(nsecs_t when);
+ explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
[[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 384c075..9079c73 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -22,8 +22,10 @@
class VibratorInputMapper : public InputMapper {
public:
- explicit VibratorInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
+ template <class T, class... Args>
+ friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ Args... args);
virtual ~VibratorInputMapper();
virtual uint32_t getSources() const override;
@@ -46,6 +48,8 @@
ssize_t mIndex;
nsecs_t mNextStepTime;
+ explicit VibratorInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> nextStep();
[[nodiscard]] NotifyVibratorStateArgs stopVibrating();
};
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index fd2be5f..7eca6fa 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -132,7 +132,7 @@
case kGestureTypeScroll:
return handleScroll(when, readTime, gesture);
case kGestureTypeFling:
- return {handleFling(when, readTime, gesture)};
+ return handleFling(when, readTime, gesture);
case kGestureTypeSwipe:
return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
gesture.details.swipe.dy);
@@ -149,7 +149,8 @@
}
}
-NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+NotifyMotionArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
rotateDelta(mOrientation, &deltaX, &deltaY);
@@ -312,19 +313,39 @@
return out;
}
-NotifyArgs GestureConverter::handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
- // We don't actually want to use the gestures library's fling velocity values (to ensure
- // consistency between touchscreen and touchpad flings), so we're just using the "start fling"
- // gestures as a marker for the end of a two-finger scroll gesture.
- if (gesture.details.fling.fling_state != GESTURES_FLING_START ||
- mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
- return {};
+std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ switch (gesture.details.fling.fling_state) {
+ case GESTURES_FLING_START:
+ if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
+ // We don't actually want to use the gestures library's fling velocity values (to
+ // ensure consistency between touchscreen and touchpad flings), so we're just using
+ // the "start fling" gestures as a marker for the end of a two-finger scroll
+ // gesture.
+ return {endScroll(when, readTime)};
+ }
+ break;
+ case GESTURES_FLING_TAP_DOWN:
+ if (mCurrentClassification == MotionClassification::NONE) {
+ // Use the tap down state of a fling gesture as an indicator that a contact
+ // has been initiated with the touchpad. We treat this as a move event with zero
+ // magnitude, which will also result in the pointer icon being updated.
+ // TODO(b/282023644): Add a signal in libgestures for when a stable contact has been
+ // initiated with a touchpad.
+ return {handleMove(when, readTime,
+ Gesture(kGestureMove, gesture.start_time, gesture.end_time,
+ /*dx=*/0.f,
+ /*dy=*/0.f))};
+ }
+ break;
+ default:
+ break;
}
- return endScroll(when, readTime);
+ return {};
}
-NotifyArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
+NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
@@ -507,14 +528,29 @@
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords,
float xCursorPosition, float yCursorPosition) {
- return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, SOURCE,
- mPointerController->getDisplayId(), /* policyFlags= */ POLICY_FLAG_WAKE,
- action, /* actionButton= */ actionButton, /* flags= */ 0,
- mReaderContext.getGlobalMetaState(), buttonState,
- mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties, pointerCoords, /* xPrecision= */ 1.0f,
- /* yPrecision= */ 1.0f, xCursorPosition, yCursorPosition,
- /* downTime= */ mDownTime, /* videoFrames= */ {});
+ return {mReaderContext.getNextId(),
+ when,
+ readTime,
+ mDeviceId,
+ SOURCE,
+ mPointerController->getDisplayId(),
+ /* policyFlags= */ POLICY_FLAG_WAKE,
+ action,
+ /* actionButton= */ actionButton,
+ /* flags= */ 0,
+ mReaderContext.getGlobalMetaState(),
+ buttonState,
+ mCurrentClassification,
+ AMOTION_EVENT_EDGE_FLAG_NONE,
+ pointerCount,
+ pointerProperties,
+ pointerCoords,
+ /* xPrecision= */ 1.0f,
+ /* yPrecision= */ 1.0f,
+ xCursorPosition,
+ yCursorPosition,
+ /* downTime= */ mDownTime,
+ /* videoFrames= */ {}};
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 70e8fb7..b613b88 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -52,14 +52,16 @@
const Gesture& gesture);
private:
- [[nodiscard]] NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] NotifyMotionArgs handleMove(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
- [[nodiscard]] NotifyArgs handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
- [[nodiscard]] NotifyArgs endScroll(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+ [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
uint32_t fingerCount, float dx,
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 3dc5152..99a6a1f 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -781,4 +781,175 @@
WithPointerId(/*index=*/1, /*id=*/0)));
}
+// Motion events without any pointers are invalid, so when a button press is reported in the same
+// frame as a touch down, the button press must be reported second. Similarly with a button release
+// and a touch lift.
+TEST_F(CapturedTouchpadEventConverterTest,
+ ButtonPressedAndReleasedInSameFrameAsTouch_ReportedWithPointers) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u),
+ WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 0);
+ args = processSync(conv);
+ ASSERT_EQ(3u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u),
+ WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP));
+}
+
+// Some touchpads sometimes report a button press before they report the finger touching the pad. In
+// that case we need to wait until the touch comes to report the button press.
+TEST_F(CapturedTouchpadEventConverterTest, ButtonPressedBeforeTouch_ReportedOnceTouchOccurs) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 1);
+ ASSERT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), WithPointerCount(1u),
+ WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+}
+
+// When all fingers are lifted from a touchpad, we should release any buttons that are down, since
+// we won't be able to report them being lifted later if no pointers are present.
+TEST_F(CapturedTouchpadEventConverterTest, ButtonReleasedAfterTouchLifts_ReportedWithLift) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ args = processSync(conv);
+ ASSERT_EQ(3u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), WithPointerCount(1u),
+ WithCoords(50, 100), WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(0)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP));
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 0);
+ ASSERT_EQ(0u, processSync(conv).size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, MultipleButtonsPressedDuringTouch_ReportedCorrectly) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 1);
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)));
+
+ processAxis(conv, EV_KEY, BTN_RIGHT, 1);
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY)));
+
+ processAxis(conv, EV_KEY, BTN_LEFT, 0);
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY)));
+
+ processAxis(conv, EV_KEY, BTN_RIGHT, 0);
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0)));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index c6d541e..a723636 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -888,4 +888,21 @@
WithToolType(ToolType::FINGER)));
}
+TEST_F(GestureConverterTest, FlingTapDown) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, tapDownGesture);
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X, POINTER_Y));
+ ASSERT_TRUE(mFakePointerController->isPointerShown());
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f6f02d8..3f2658a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -86,6 +86,8 @@
AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_2_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
// The default pid and uid for windows created on the primary display by the test.
static constexpr int32_t WINDOW_PID = 999;
@@ -1562,8 +1564,7 @@
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
- mPointers.size() == 1) {
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
mRawXCursorPosition = pointerCoords[0].getX();
mRawYCursorPosition = pointerCoords[0].getY();
}
@@ -1660,6 +1661,11 @@
return *this;
}
+ MotionArgsBuilder& classification(MotionClassification classification) {
+ mClassification = classification;
+ return *this;
+ }
+
NotifyMotionArgs build() {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
@@ -1670,15 +1676,14 @@
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
- mPointers.size() == 1) {
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
mRawXCursorPosition = pointerCoords[0].getX();
mRawYCursorPosition = pointerCoords[0].getY();
}
NotifyMotionArgs args(InputEvent::nextId(), mEventTime, /*readTime=*/mEventTime, mDeviceId,
mSource, mDisplayId, mPolicyFlags, mAction, mActionButton, mFlags,
- AMETA_NONE, mButtonState, MotionClassification::NONE, /*edgeFlags=*/0,
+ AMETA_NONE, mButtonState, mClassification, /*edgeFlags=*/0,
mPointers.size(), pointerProperties.data(), pointerCoords.data(),
/*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
mRawYCursorPosition, mDownTime, /*videoFrames=*/{});
@@ -1697,6 +1702,7 @@
int32_t mActionButton{0};
int32_t mButtonState{0};
int32_t mFlags{0};
+ MotionClassification mClassification{MotionClassification::NONE};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
@@ -4003,6 +4009,126 @@
EXPECT_EQ(-10, event->getY(1)); // -50 + 40
}
+TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeOnlySentToTrustedOverlays) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 400, 400));
+ sp<FakeWindowHandle> trustedOverlay =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Trusted Overlay",
+ ADISPLAY_ID_DEFAULT);
+ trustedOverlay->setSpy(true);
+ trustedOverlay->setTrustedOverlay(true);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {trustedOverlay, window}}});
+
+ // Start a three-finger touchpad swipe
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(100))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(100))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_2_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(100))
+ .pointer(PointerBuilder(2, ToolType::FINGER).x(300).y(100))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+
+ trustedOverlay->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ trustedOverlay->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+ trustedOverlay->consumeMotionEvent(WithMotionAction(POINTER_2_DOWN));
+
+ // Move the swipe a bit
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(105))
+ .pointer(PointerBuilder(2, ToolType::FINGER).x(300).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+
+ trustedOverlay->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // End the swipe
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_2_UP, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(105))
+ .pointer(PointerBuilder(2, ToolType::FINGER).x(300).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+
+ trustedOverlay->consumeMotionEvent(WithMotionAction(POINTER_2_UP));
+ trustedOverlay->consumeMotionEvent(WithMotionAction(POINTER_1_UP));
+ trustedOverlay->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeNotSentToSingleWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 400, 400));
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Start a three-finger touchpad swipe
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(100))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(100))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_2_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(100))
+ .pointer(PointerBuilder(2, ToolType::FINGER).x(300).y(100))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+
+ // Move the swipe a bit
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(105))
+ .pointer(PointerBuilder(2, ToolType::FINGER).x(300).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+
+ // End the swipe
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_2_UP, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(105))
+ .pointer(PointerBuilder(2, ToolType::FINGER).x(300).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(250).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(105))
+ .classification(MotionClassification::MULTI_FINGER_SWIPE)
+ .build());
+
+ window->assertNoEvents();
+}
+
/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index ae3ae6c..9fbe762 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2603,13 +2603,13 @@
};
TEST_F(SwitchInputMapperTest, GetSources) {
- SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+ SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
}
TEST_F(SwitchInputMapperTest, GetSwitchState) {
- SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+ SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1);
ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
@@ -2619,7 +2619,7 @@
}
TEST_F(SwitchInputMapperTest, Process) {
- SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
+ SwitchInputMapper& mapper = constructAndAddMapper<SwitchInputMapper>();
std::list<NotifyArgs> out;
out = process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
ASSERT_TRUE(out.empty());
@@ -2645,13 +2645,13 @@
};
TEST_F(VibratorInputMapperTest, GetSources) {
- VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+ VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
}
TEST_F(VibratorInputMapperTest, GetVibratorIds) {
- VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+ VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
ASSERT_EQ(mapper.getVibratorIds().size(), 2U);
}
@@ -2659,7 +2659,7 @@
TEST_F(VibratorInputMapperTest, Vibrate) {
constexpr uint8_t DEFAULT_AMPLITUDE = 192;
constexpr int32_t VIBRATION_TOKEN = 100;
- VibratorInputMapper& mapper = addMapperAndConfigure<VibratorInputMapper>();
+ VibratorInputMapper& mapper = constructAndAddMapper<VibratorInputMapper>();
VibrationElement pattern(2);
VibrationSequence sequence(2);
@@ -2784,7 +2784,7 @@
}
TEST_F(SensorInputMapperTest, GetSources) {
- SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+ SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
}
@@ -2792,7 +2792,7 @@
TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
setAccelProperties();
prepareAccelAxes();
- SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+ SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
std::chrono::microseconds(10000),
@@ -2822,7 +2822,7 @@
TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
setGyroProperties();
prepareGyroAxes();
- SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+ SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>();
ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
std::chrono::microseconds(10000),
@@ -3862,21 +3862,21 @@
TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
}
TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
}
TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
InputDeviceInfo info;
mapper.populateDeviceInfo(info);
@@ -3906,7 +3906,7 @@
TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
InputDeviceInfo info;
mapper.populateDeviceInfo(info);
@@ -3924,7 +3924,7 @@
TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
@@ -4012,7 +4012,7 @@
TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
NotifyMotionArgs args;
@@ -4036,7 +4036,7 @@
TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
NotifyMotionArgs args;
@@ -4065,7 +4065,7 @@
TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
NotifyMotionArgs args;
@@ -4114,7 +4114,7 @@
// InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
// need to be rotated.
addConfigurationProperty("cursor.orientationAware", "1");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
@@ -4132,7 +4132,7 @@
addConfigurationProperty("cursor.mode", "navigation");
// Since InputReader works in the un-rotated coordinate space, only devices that are not
// orientation-aware are affected by display rotation.
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
clearViewports();
prepareDisplay(ui::ROTATION_0);
@@ -4181,7 +4181,7 @@
TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
mFakePointerController->setPosition(100, 200);
@@ -4435,7 +4435,7 @@
TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
mFakePointerController->setPosition(100, 200);
@@ -4456,7 +4456,7 @@
TEST_F(CursorInputMapperTest, Process_PointerCapture) {
addConfigurationProperty("cursor.mode", "pointer");
mFakePolicy->setPointerCapture(true);
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4548,7 +4548,7 @@
const VelocityControlParameters testParams(/*scale=*/5.f, /*low threshold=*/0.f,
/*high threshold=*/100.f, /*acceleration=*/10.f);
mFakePolicy->setVelocityControlParams(testParams);
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4589,7 +4589,7 @@
TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4630,7 +4630,7 @@
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
// Set up the default display.
prepareDisplay(ui::ROTATION_90);
@@ -4656,7 +4656,7 @@
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
// Set up the default display.
prepareDisplay(ui::ROTATION_90);
@@ -4682,7 +4682,7 @@
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
// Set up the default display as the display on which the pointer should be shown.
prepareDisplay(ui::ROTATION_90);
@@ -4715,7 +4715,7 @@
TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
nsecs_t kernelEventTime = ARBITRARY_TIME;
nsecs_t expectedEventTime = ARBITRARY_TIME;
@@ -4742,7 +4742,7 @@
TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
nsecs_t expectedEventTime = ARBITRARY_TIME;
process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
@@ -4779,7 +4779,7 @@
TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) {
addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
+ CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
nsecs_t kernelEventTime = ARBITRARY_TIME;
nsecs_t expectedEventTime = ARBITRARY_TIME;
@@ -10904,7 +10904,7 @@
TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
prepareAxes();
- JoystickInputMapper& mapper = addMapperAndConfigure<JoystickInputMapper>();
+ JoystickInputMapper& mapper = constructAndAddMapper<JoystickInputMapper>();
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 20f4de1..f6ca9e2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -50,8 +50,6 @@
namespace hal = hardware::graphics::composer::hal;
-ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
-
DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay)
@@ -282,10 +280,6 @@
Rect orientedDisplaySpaceRect) {
mOrientation = orientation;
- if (isPrimary()) {
- sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
- }
-
// We need to take care of display rotation for globalTransform for case if the panel is not
// installed aligned with device orientation.
const auto transformOrientation = orientation + mPhysicalOrientation;
@@ -326,10 +320,6 @@
return mStagedBrightness;
}
-ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
- return sPrimaryDisplayRotationFlags;
-}
-
void DisplayDevice::dump(utils::Dumper& dumper) const {
using namespace std::string_view_literals;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 51876e7..dc5f8a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -109,8 +109,6 @@
ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
ui::Rotation getOrientation() const { return mOrientation; }
- static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
-
std::optional<float> getStagedBrightness() const REQUIRES(kMainThreadContext);
ui::Transform::RotationFlags getTransformHint() const;
const ui::Transform& getTransform() const;
@@ -274,8 +272,6 @@
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
- static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
-
// Allow nullopt as initial power mode.
using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
std::optional<TracedPowerMode> mPowerMode;
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 76b36fe..218a64a 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -28,7 +28,7 @@
ui::Transform transform;
bool receivesInput;
bool isSecure;
- // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
+ // TODO(b/259407931): can eliminate once SurfaceFlinger::sActiveDisplayRotationFlags is removed.
bool isPrimary;
bool isVirtual;
ui::Transform::RotationFlags rotationFlags;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ce7d37e..985c6f9 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -667,6 +667,7 @@
snapshot.relativeLayerMetadata.mMap.clear();
}
+// TODO (b/259407931): Remove.
uint32_t getPrimaryDisplayRotationFlags(
const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
for (auto& [_, display] : displays) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3371ae2..f12aab7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -254,7 +254,8 @@
mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
}
if (mHadClonedChild) {
- mFlinger->mNumClones--;
+ auto& roots = mFlinger->mLayerMirrorRoots;
+ roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end());
}
if (hasTrustedPresentationListener()) {
mFlinger->mNumTrustedPresentationListeners--;
@@ -1649,8 +1650,8 @@
void Layer::dumpOffscreenDebugInfo(std::string& result) const {
std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
- StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(),
- mOwnerPid, mOwnerUid);
+ StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(),
+ mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : "");
}
void Layer::onDisconnect() {
@@ -2594,7 +2595,7 @@
mDrawingState.inputInfo = tmpInputInfo;
}
-void Layer::updateMirrorInfo() {
+bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) {
if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
// If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
// it means that there is a clone, but the layer it was cloned from has been destroyed. In
@@ -2602,7 +2603,7 @@
// destroyed. The root, this layer, will still be around since the client can continue
// to hold a reference, but no cloned layers will be displayed.
mClonedChild = nullptr;
- return;
+ return true;
}
std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
@@ -2617,6 +2618,13 @@
mClonedChild->updateClonedDrawingState(clonedLayersMap);
mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap);
mClonedChild->updateClonedRelatives(clonedLayersMap);
+
+ for (Layer* root : cloneRootsPendingUpdates) {
+ if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) {
+ return false;
+ }
+ }
+ return true;
}
void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2764,7 +2772,7 @@
void Layer::setClonedChild(const sp<Layer>& clonedChild) {
mClonedChild = clonedChild;
mHadClonedChild = true;
- mFlinger->mNumClones++;
+ mFlinger->mLayerMirrorRoots.push_back(this);
}
bool Layer::setDropInputMode(gui::DropInputMode mode) {
@@ -2977,7 +2985,7 @@
if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
std::swap(bufferWidth, bufferHeight);
}
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
if (mDrawingState.transformToDisplayInverse) {
if (invTransform & ui::Transform::ROT_90) {
std::swap(bufferWidth, bufferHeight);
@@ -3077,6 +3085,7 @@
mDrawingState.desiredPresentTime = desiredPresentTime;
mDrawingState.isAutoTimestamp = isAutoTimestamp;
mDrawingState.latchedVsyncId = info.vsyncId;
+ mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection;
mDrawingState.modified = true;
if (!buffer) {
resetDrawingStateBufferInfo();
@@ -3139,15 +3148,31 @@
}
void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+ ATRACE_CALL();
const nsecs_t presentTime = [&] {
- if (!mDrawingState.isAutoTimestamp) return mDrawingState.desiredPresentTime;
+ if (!mDrawingState.isAutoTimestamp) {
+ ATRACE_FORMAT_INSTANT("desiredPresentTime");
+ return mDrawingState.desiredPresentTime;
+ }
- const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
- mDrawingState.latchedVsyncId);
- if (prediction.has_value()) return prediction->presentTime;
+ if (mDrawingState.useVsyncIdForRefreshRateSelection) {
+ const auto prediction =
+ mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+ mDrawingState.latchedVsyncId);
+ if (prediction.has_value()) {
+ ATRACE_FORMAT_INSTANT("predictedPresentTime");
+ return prediction->presentTime;
+ }
+ }
return static_cast<nsecs_t>(0);
}();
+
+ if (ATRACE_ENABLED() && presentTime > 0) {
+ const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now();
+ ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
+ }
+
mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
scheduler::LayerHistory::LayerUpdateType::Buffer);
}
@@ -3287,7 +3312,7 @@
}
if (getTransformToDisplayInverse()) {
- uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
if (invTransform & ui::Transform::ROT_90) {
std::swap(bufWidth, bufHeight);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2640c92..38590e6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -234,6 +234,7 @@
float desiredHdrSdrRatio = 1.f;
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
int64_t latchedVsyncId = 0;
+ bool useVsyncIdForRefreshRateSelection = false;
};
explicit Layer(const LayerCreationArgs& args);
@@ -651,7 +652,7 @@
gui::WindowInfo::Type getWindowType() const { return mWindowType; }
- void updateMirrorInfo();
+ bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates);
/*
* doTransaction - process the transaction. This is a good place to figure
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index e713263..f855f27 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -25,8 +25,8 @@
#include <system/window.h>
#include <utils/Log.h>
-#include "DisplayDevice.h"
#include "LayerFE.h"
+#include "SurfaceFlinger.h"
namespace android {
@@ -260,7 +260,7 @@
* the code below applies the primary display's inverse transform to
* the texture transform
*/
- uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ uint32_t transform = SurfaceFlinger::getActiveDisplayRotationFlags();
mat4 tr = inverseOrientation(transform);
/**
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index f6b5afc..d606cff 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -85,7 +85,7 @@
// If layer is offscreen, update mirroring info if it exists
if (mLayer->isRemovedFromCurrentState()) {
mLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->updateMirrorInfo(); });
+ [&](Layer* layer) { layer->updateMirrorInfo({}); });
mLayer->traverse(LayerVector::StateSet::Drawing,
[&](Layer* layer) { layer->updateCloneBufferInfo(); });
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index af9acf3..281b0ae 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -612,6 +612,15 @@
preferredExpectedPresentationTime + multiplier * frameInterval;
if (expectedPresentationTime >= preferredExpectedPresentationTime +
scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()) {
+ if (currentIndex == 0) {
+ ALOGW("%s: Expected present time is too far in the future but no timelines are "
+ "valid. preferred EPT=%" PRId64 ", Calculated EPT=%" PRId64
+ ", multiplier=%" PRId64 ", frameInterval=%" PRId64 ", threshold=%" PRId64,
+ __func__, preferredExpectedPresentationTime, expectedPresentationTime,
+ multiplier, frameInterval,
+ static_cast<int64_t>(
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+ }
break;
}
@@ -625,6 +634,20 @@
.expectedPresentationTime = expectedPresentationTime};
currentIndex++;
}
+
+ if (currentIndex == 0) {
+ ALOGW("%s: No timelines are valid. preferred EPT=%" PRId64 ", frameInterval=%" PRId64
+ ", threshold=%" PRId64,
+ __func__, preferredExpectedPresentationTime, frameInterval,
+ static_cast<int64_t>(scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+ outVsyncEventData.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(timestamp, preferredDeadlineTimestamp,
+ preferredExpectedPresentationTime),
+ .deadlineTimestamp = preferredDeadlineTimestamp,
+ .expectedPresentationTime = preferredExpectedPresentationTime};
+ currentIndex++;
+ }
+
outVsyncEventData.frameTimelinesLength = currentIndex;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dfe8c72..5d96fc4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -356,6 +356,8 @@
PermissionCache::checkPermission(permission, pid, uid);
}
+ui::Transform::RotationFlags SurfaceFlinger::sActiveDisplayRotationFlags = ui::Transform::ROT_0;
+
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
mPid(getpid()),
@@ -2619,7 +2621,7 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
- refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
+ refreshArgs.internalDisplayRotationFlags = getActiveDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -3563,6 +3565,8 @@
currentState.orientedDisplaySpaceRect);
if (display->getId() == mActiveDisplayId) {
mActiveDisplayTransformHint = display->getTransformHint();
+ sActiveDisplayRotationFlags =
+ ui::Transform::toRotationFlags(display->getOrientation());
}
}
if (currentState.width != drawingState.width ||
@@ -4025,8 +4029,24 @@
}
commitOffscreenLayers();
- if (mNumClones > 0) {
- mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
+ if (mLayerMirrorRoots.size() > 0) {
+ std::deque<Layer*> pendingUpdates;
+ pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(),
+ mLayerMirrorRoots.end());
+ std::vector<Layer*> needsUpdating;
+ for (Layer* cloneRoot : mLayerMirrorRoots) {
+ pendingUpdates.pop_front();
+ if (cloneRoot->isRemovedFromCurrentState()) {
+ continue;
+ }
+ if (cloneRoot->updateMirrorInfo(pendingUpdates)) {
+ } else {
+ needsUpdating.push_back(cloneRoot);
+ }
+ }
+ for (Layer* cloneRoot : needsUpdating) {
+ cloneRoot->updateMirrorInfo({});
+ }
}
}
@@ -4133,7 +4153,7 @@
mBootStage = BootStage::BOOTANIMATION;
}
- if (mNumClones > 0) {
+ if (mLayerMirrorRoots.size() > 0) {
mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
}
@@ -4158,8 +4178,8 @@
leakingParentLayerFound = true;
sp<Layer> parent = sp<Layer>::fromExisting(layer);
while (parent) {
- ALOGE("Parent Layer: %s handleIsAlive: %s", parent->getName().c_str(),
- std::to_string(parent->isHandleAlive()).c_str());
+ ALOGE("Parent Layer: %s%s", parent->getName().c_str(),
+ (parent->isHandleAlive() ? "handleAlive" : ""));
parent = parent->getParent();
}
// Sample up to 100 layers
@@ -4174,21 +4194,28 @@
}
});
- ALOGE("Dumping random sampling of on-screen layers: ");
+ int numLayers = 0;
+ mDrawingState.traverse([&](Layer* layer) { numLayers++; });
+
+ ALOGE("Dumping random sampling of on-screen layers total(%u):", numLayers);
mDrawingState.traverse([&](Layer* layer) {
// Aim to dump about 200 layers to avoid totally trashing
// logcat. On the other hand, if there really are 4096 layers
// something has gone totally wrong its probably the most
// useful information in logcat.
if (rand() % 20 == 13) {
- ALOGE("Layer: %s", layer->getName().c_str());
+ ALOGE("Layer: %s%s", layer->getName().c_str(),
+ (layer->isHandleAlive() ? "handleAlive" : ""));
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
});
ALOGE("Dumping random sampling of off-screen layers total(%zu): ",
mOffscreenLayers.size());
for (Layer* offscreenLayer : mOffscreenLayers) {
if (rand() % 20 == 13) {
- ALOGE("Offscreen-layer: %s", offscreenLayer->getName().c_str());
+ ALOGE("Offscreen-layer: %s%s", offscreenLayer->getName().c_str(),
+ (offscreenLayer->isHandleAlive() ? "handleAlive" : ""));
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
}));
@@ -4474,7 +4501,8 @@
const 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<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
+ const std::vector<uint64_t>& mergedTransactionIds) {
ATRACE_CALL();
IPCThreadState* ipc = IPCThreadState::self();
@@ -4563,7 +4591,8 @@
listenerCallbacks,
originPid,
originUid,
- transactionId};
+ transactionId,
+ mergedTransactionIds};
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
@@ -5474,14 +5503,19 @@
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
}
- // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
- // We can merge the syscall later.
- if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
- ALOGW("Couldn't set uclamp.min on display on: %s\n", strerror(errno));
+ if (displayId == mActiveDisplayId) {
+ // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and
+ // set it before SCHED_FIFO due to b/190237315.
+ if (setSchedAttr(true) != NO_ERROR) {
+ ALOGW("Failed to set uclamp.min after powering on active display: %s",
+ strerror(errno));
+ }
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Failed to set SCHED_FIFO after powering on active display: %s",
+ strerror(errno));
+ }
}
- if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
- ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
- }
+
getHwComposer().setPowerMode(displayId, mode);
if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
setHWCVsyncEnabled(displayId,
@@ -5495,15 +5529,21 @@
scheduleComposite(FrameHint::kActive);
} else if (mode == hal::PowerMode::OFF) {
// Turn off the display
- if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
- ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
- }
- if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
- ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
- }
- if (displayId == mActiveDisplayId && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
- mScheduler->disableHardwareVsync(displayId, true);
- mScheduler->enableSyntheticVsync();
+
+ if (displayId == mActiveDisplayId) {
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
+ strerror(errno));
+ }
+ if (setSchedAttr(false) != NO_ERROR) {
+ ALOGW("Failed set uclamp.min after powering off active display: %s",
+ strerror(errno));
+ }
+
+ if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ mScheduler->enableSyntheticVsync();
+ }
}
// Make sure HWVsync is disabled before turning off the display
@@ -6952,7 +6992,8 @@
const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
- if (capturingHdrLayers && !hintForSeamlessTransition) {
+ // TODO: Enable once HDR screenshots are ready.
+ if constexpr (/* DISABLES CODE */ (false)) {
// For now since we only support 8-bit screenshots, just use HLG and
// assume that 1.0 >= display max luminance. This isn't quite as future
// proof as PQ is, but is good enough.
@@ -7943,6 +7984,7 @@
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay.getTransformHint();
+ sActiveDisplayRotationFlags = ui::Transform::toRotationFlags(activeDisplay.getOrientation());
// The policy of the new active/pacesetter display may have changed while it was inactive. In
// that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
@@ -8144,7 +8186,7 @@
});
}
if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
- mDrawingState.traverseInZOrder([&refreshArgs, cursorOnly, &layers](Layer* layer) {
+ auto moveSnapshots = [&layers, &refreshArgs, cursorOnly](Layer* layer) {
if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
if (cursorOnly &&
layer->getLayerSnapshot()->compositionType !=
@@ -8155,7 +8197,22 @@
refreshArgs.layers.push_back(layerFE);
layers.emplace_back(layer, layerFE.get());
}
- });
+ };
+
+ if (cursorOnly || !mVisibleRegionsDirty) {
+ // for hot path avoid traversals by walking though the previous composition list
+ for (sp<Layer> layer : mPreviouslyComposedLayers) {
+ moveSnapshots(layer.get());
+ }
+ } else {
+ mPreviouslyComposedLayers.clear();
+ mDrawingState.traverseInZOrder(
+ [&moveSnapshots](Layer* layer) { moveSnapshots(layer); });
+ mPreviouslyComposedLayers.reserve(layers.size());
+ for (auto [layer, _] : layers) {
+ mPreviouslyComposedLayers.push_back(sp<Layer>::fromExisting(layer));
+ }
+ }
}
return layers;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d92ec7a..e2691ab 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,8 +296,7 @@
// the client can no longer modify this layer directly.
void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
- // TODO: Remove atomic if move dtor to main thread CL lands
- std::atomic<uint32_t> mNumClones;
+ std::vector<Layer*> mLayerMirrorRoots;
TransactionCallbackInvoker& getTransactionCallbackInvoker() {
return mTransactionCallbackInvoker;
@@ -332,6 +331,14 @@
const DisplayDevice* getDisplayFromLayerStack(ui::LayerStack)
REQUIRES(mStateLock, kMainThreadContext);
+ // TODO (b/259407931): Remove.
+ // TODO (b/281857977): This should be annotated with REQUIRES(kMainThreadContext), but this
+ // would require thread safety annotations throughout the frontend (in particular Layer and
+ // LayerFE).
+ static ui::Transform::RotationFlags getActiveDisplayRotationFlags() {
+ return sActiveDisplayRotationFlags;
+ }
+
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
@@ -508,15 +515,13 @@
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
- status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& state, const 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) override;
+ status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ const 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;
void bootFinished();
virtual status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
sp<IDisplayEventConnection> createDisplayEventConnection(
@@ -1199,6 +1204,11 @@
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
+ // Sorted list of layers that were composed during previous frame. This is used to
+ // avoid an expensive traversal of the layer hierarchy when there are no
+ // visible region changes. Because this is a list of strong pointers, this will
+ // extend the life of the layer but this list is only updated in the main thread.
+ std::vector<sp<Layer>> mPreviouslyComposedLayers;
BootStage mBootStage = BootStage::BOOTLOADER;
@@ -1390,6 +1400,10 @@
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
+ // Must only be accessed on the main thread.
+ // TODO (b/259407931): Remove.
+ static ui::Transform::RotationFlags sActiveDisplayRotationFlags;
+
bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) {
return hasDisplay(
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 57d927b..0694180 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -69,6 +69,13 @@
for (auto& displayState : t.displays) {
proto.mutable_display_changes()->Add(std::move(toProto(displayState)));
}
+
+ proto.mutable_merged_transaction_ids()->Reserve(
+ static_cast<int32_t>(t.mergedTransactionIds.size()));
+ for (auto& mergedTransactionId : t.mergedTransactionIds) {
+ proto.mutable_merged_transaction_ids()->Add(mergedTransactionId);
+ }
+
return proto;
}
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 62a7dfd..7132a59 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -56,7 +56,8 @@
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)
+ int originPid, int originUid, uint64_t transactionId,
+ std::vector<uint64_t> mergedTransactionIds)
: frameTimelineInfo(frameTimelineInfo),
states(std::move(composerStates)),
displays(displayStates),
@@ -71,7 +72,8 @@
listenerCallbacks(listenerCallbacks),
originPid(originPid),
originUid(originUid),
- id(transactionId) {}
+ id(transactionId),
+ mergedTransactionIds(std::move(mergedTransactionIds)) {}
// Invokes `void(const layer_state_t&)` visitor for matching layers.
template <typename Visitor>
@@ -131,6 +133,7 @@
int originUid;
uint64_t id;
bool sentFenceTimeoutWarning = false;
+ std::vector<uint64_t> mergedTransactionIds;
};
} // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index ce4d18f..80943b5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -185,11 +185,12 @@
bool hasListenerCallbacks = mFdp.ConsumeBool();
std::vector<ListenerCallbacks> listenerCallbacks{};
uint64_t transactionId = mFdp.ConsumeIntegral<uint64_t>();
+ std::vector<uint64_t> mergedTransactionIds{};
mTestableFlinger.setTransactionState(FrameTimelineInfo{}, states, displays, flags, applyToken,
InputWindowCommands{}, desiredPresentTime, isAutoTimestamp,
- {}, hasListenerCallbacks, listenerCallbacks,
- transactionId);
+ {}, hasListenerCallbacks, listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
void SurfaceFlingerFuzzer::setDisplayStateLocked() {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 4d13aca..da5ec48 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -737,19 +737,18 @@
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
- auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& states, const 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) {
+ auto setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+ const 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);
+ listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
auto flushTransactionQueues() {
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 2c4eb10..b0cee9b 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -100,6 +100,7 @@
uint64 transaction_id = 6;
repeated LayerState layer_changes = 7;
repeated DisplayState display_changes = 8;
+ repeated uint64 merged_transaction_ids = 9;
}
// Keep insync with layer_state_t
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 0495678..cf23169 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -91,7 +91,7 @@
// Set uclamp.min setting on all threads, maybe an overkill but we want
// to cover important threads like RenderEngine.
if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
- ALOGW("Couldn't set uclamp.min: %s\n", strerror(errno));
+ ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno));
}
// The binder threadpool we start will inherit sched policy and priority
@@ -155,7 +155,7 @@
startDisplayService(); // dependency on SF getting registered above
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
- ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
+ ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno));
}
// run surface flinger in this thread
diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
new file mode 100644
index 0000000..7077523
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest {
+ static constexpr bool kWithMockScheduler = false;
+ ActiveDisplayRotationFlagsTest() : DisplayTransactionTest(kWithMockScheduler) {}
+
+ void SetUp() override {
+ injectMockScheduler(kInnerDisplayId);
+
+ // Inject inner and outer displays with uninitialized power modes.
+ constexpr bool kInitPowerMode = false;
+ {
+ InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+ mInnerDisplay = injector.inject();
+ }
+ {
+ OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ mOuterDisplay = injector.inject();
+ }
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+ // The flags are a static variable, so by modifying them in the test, we
+ // are modifying the real ones used by SurfaceFlinger. Save the original
+ // flags so we can restore them on teardown. This isn't perfect - the
+ // phone may have been rotated during the test, so we're restoring the
+ // wrong flags. But if the phone is rotated, this may also fail the test.
+ mOldRotationFlags = mFlinger.mutableActiveDisplayRotationFlags();
+
+ // Reset to the expected default state.
+ mFlinger.mutableActiveDisplayRotationFlags() = ui::Transform::ROT_0;
+ }
+
+ void TearDown() override { mFlinger.mutableActiveDisplayRotationFlags() = mOldRotationFlags; }
+
+ static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
+ static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+
+ sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
+ ui::Transform::RotationFlags mOldRotationFlags;
+};
+
+TEST_F(ActiveDisplayRotationFlagsTest, defaultRotation) {
+ ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90) {
+ auto displayToken = mInnerDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_90;
+
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+ ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) {
+ auto displayToken = mOuterDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_90;
+
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+ ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) {
+ auto displayToken = mInnerDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_180;
+
+ displayToken = mOuterDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_270;
+
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+ ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) {
+ mFlinger.mutableActiveDisplayId() = kOuterDisplayId;
+ auto displayToken = mInnerDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_180;
+
+ displayToken = mOuterDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_270;
+
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+ ASSERT_EQ(ui::Transform::ROT_270, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, onActiveDisplayChanged) {
+ auto displayToken = mInnerDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_180;
+
+ displayToken = mOuterDisplay->getDisplayToken().promote();
+ mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+ mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+ ui::ROTATION_270;
+
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+ ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
+
+ mFlinger.onActiveDisplayChanged(mInnerDisplay.get(), *mOuterDisplay);
+ ASSERT_EQ(ui::Transform::ROT_270, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 55705ca..70f8a83 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -70,6 +70,7 @@
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
+ "ActiveDisplayRotationFlagsTest.cpp",
"CompositionTest.cpp",
"DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 6ca21bd..e8a9cfe 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -680,6 +680,9 @@
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
test->mFlinger.setLayerSidebandStream(layer, stream);
+ auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+ layerDrawingState.crop =
+ Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
}
static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -814,6 +817,7 @@
Mock::VerifyAndClear(test->mComposer);
test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+ test->mFlinger.mutableVisibleRegionsDirty() = true;
}
static void cleanupInjectedLayers(CompositionTest* test) {
@@ -822,6 +826,7 @@
test->mDisplay->getCompositionDisplay()->clearOutputLayers();
test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+ test->mFlinger.mutablePreviouslyComposedLayers().clear();
// Layer should be unregistered with scheduler.
test->mFlinger.commit();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a189c00..945e488 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -466,19 +466,18 @@
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
- auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- Vector<ComposerState>& states, const 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) {
+ auto setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+ const 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);
+ listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
auto setTransactionStateInternal(TransactionState& transaction) {
@@ -588,6 +587,7 @@
auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
+ auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
auto& mutableTexturePool() { return mFlinger->mTexturePool; }
@@ -599,6 +599,11 @@
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
+ auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; }
+
+ auto& mutableActiveDisplayRotationFlags() {
+ return SurfaceFlinger::sActiveDisplayRotationFlags;
+ }
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 6a641b3..afb8efb 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -73,6 +73,7 @@
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));
};
@@ -108,7 +109,7 @@
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id);
+ transaction.id, transaction.mergedTransactionIds);
// 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.
@@ -135,7 +136,7 @@
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id);
+ transaction.id, transaction.mergedTransactionIds);
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
@@ -166,7 +167,7 @@
transactionA.applyToken, transactionA.inputWindowCommands,
transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionA.id);
+ transactionA.id, transactionA.mergedTransactionIds);
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -181,7 +182,7 @@
transactionB.applyToken, transactionB.inputWindowCommands,
transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionB.id);
+ transactionB.id, transactionB.mergedTransactionIds);
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -218,7 +219,8 @@
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id);
+ mHasListenerCallbacks, mCallbacks, transactionA.id,
+ transactionA.mergedTransactionIds);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -238,7 +240,8 @@
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id);
+ mHasListenerCallbacks, mCallbacks, transactionA.id,
+ transactionA.mergedTransactionIds);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -251,7 +254,8 @@
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.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id,
+ empty.mergedTransactionIds);
// flush transaction queue should flush as desiredPresentTime has
// passed
@@ -388,7 +392,8 @@
transaction.desiredPresentTime,
transaction.isAutoTimestamp, {}, systemTime(),
mHasListenerCallbacks, mCallbacks, getpid(),
- static_cast<int>(getuid()), transaction.id);
+ static_cast<int>(getuid()), transaction.id,
+ transaction.mergedTransactionIds);
mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
@@ -1083,4 +1088,137 @@
EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
}
+TEST(TransactionHandlerTest, TransactionsKeepTrackOfDirectMerges) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3, transaction4;
+
+ uint64_t transaction2Id = transaction2.getId();
+ uint64_t transaction3Id = transaction3.getId();
+ EXPECT_NE(transaction2Id, transaction3Id);
+
+ transaction1.merge(std::move(transaction2));
+ transaction1.merge(std::move(transaction3));
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 2u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction3Id);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[1], transaction2Id);
+}
+
+TEST(TransactionHandlerTest, TransactionsKeepTrackOfIndirectMerges) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3, transaction4;
+
+ uint64_t transaction2Id = transaction2.getId();
+ uint64_t transaction3Id = transaction3.getId();
+ uint64_t transaction4Id = transaction4.getId();
+ EXPECT_NE(transaction2Id, transaction3Id);
+ EXPECT_NE(transaction2Id, transaction4Id);
+ EXPECT_NE(transaction3Id, transaction4Id);
+
+ transaction4.merge(std::move(transaction2));
+ transaction4.merge(std::move(transaction3));
+
+ EXPECT_EQ(transaction4.getMergedTransactionIds().size(), 2u);
+ EXPECT_EQ(transaction4.getMergedTransactionIds()[0], transaction3Id);
+ EXPECT_EQ(transaction4.getMergedTransactionIds()[1], transaction2Id);
+
+ transaction1.merge(std::move(transaction4));
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 3u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction4Id);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[1], transaction3Id);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[2], transaction2Id);
+}
+
+TEST(TransactionHandlerTest, TransactionMergesAreCleared) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3;
+
+ transaction1.merge(std::move(transaction2));
+ transaction1.merge(std::move(transaction3));
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 2u);
+
+ transaction1.clear();
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().empty(), true);
+}
+
+TEST(TransactionHandlerTest, TransactionMergesAreCapped) {
+ SurfaceComposerClient::Transaction transaction;
+ std::vector<uint64_t> mergedTransactionIds;
+
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds.push_back(transactionToMerge.getId());
+ transaction.merge(std::move(transactionToMerge));
+ }
+
+ // Keeps latest 10 merges in order of merge recency
+ EXPECT_EQ(transaction.getMergedTransactionIds().size(), 10u);
+ for (uint i = 0; i < 10u; i++) {
+ EXPECT_EQ(transaction.getMergedTransactionIds()[i],
+ mergedTransactionIds[mergedTransactionIds.size() - 1 - i]);
+ }
+}
+
+TEST(TransactionHandlerTest, KeepsMergesFromMoreRecentMerge) {
+ SurfaceComposerClient::Transaction transaction1, transaction2, transaction3;
+ std::vector<uint64_t> mergedTransactionIds1, mergedTransactionIds2, mergedTransactionIds3;
+ uint64_t transaction2Id = transaction2.getId();
+ uint64_t transaction3Id = transaction3.getId();
+
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds1.push_back(transactionToMerge.getId());
+ transaction1.merge(std::move(transactionToMerge));
+ }
+
+ for (uint i = 0; i < 5u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds2.push_back(transactionToMerge.getId());
+ transaction2.merge(std::move(transactionToMerge));
+ }
+
+ transaction1.merge(std::move(transaction2));
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction2Id);
+ for (uint i = 0; i < 5u; i++) {
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 1u],
+ mergedTransactionIds2[mergedTransactionIds2.size() - 1 - i]);
+ }
+ for (uint i = 0; i < 4u; i++) {
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 6u],
+ mergedTransactionIds1[mergedTransactionIds1.size() - 1 - i]);
+ }
+
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ mergedTransactionIds3.push_back(transactionToMerge.getId());
+ transaction3.merge(std::move(transactionToMerge));
+ }
+
+ transaction1.merge(std::move(transaction3));
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction3Id);
+ for (uint i = 0; i < 9u; i++) {
+ EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 1],
+ mergedTransactionIds3[mergedTransactionIds3.size() - 1 - i]);
+ }
+}
+
+TEST(TransactionHandlerTest, CanAddTransactionWithFullMergedIds) {
+ SurfaceComposerClient::Transaction transaction1, transaction2;
+ for (uint i = 0; i < 20u; i++) {
+ SurfaceComposerClient::Transaction transactionToMerge;
+ transaction1.merge(std::move(transactionToMerge));
+ }
+
+ EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+
+ auto transaction1Id = transaction1.getId();
+ transaction2.merge(std::move(transaction1));
+ EXPECT_EQ(transaction2.getMergedTransactionIds().size(), 10u);
+ auto mergedTransactionIds = transaction2.getMergedTransactionIds();
+ EXPECT_TRUE(std::count(mergedTransactionIds.begin(), mergedTransactionIds.end(),
+ transaction1Id) > 0);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 82aac7e..92411a7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -67,8 +67,14 @@
ASSERT_EQ(actualProto.transactions().size(),
static_cast<int32_t>(expectedTransactions.size()));
for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
- EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
- expectedTransactions[i].originPid);
+ const auto expectedTransaction = expectedTransactions[i];
+ const auto protoTransaction = actualProto.transactions(static_cast<int32_t>(i));
+ EXPECT_EQ(protoTransaction.transaction_id(), expectedTransaction.id);
+ EXPECT_EQ(protoTransaction.pid(), expectedTransaction.originPid);
+ for (uint32_t i = 0; i < expectedTransaction.mergedTransactionIds.size(); i++) {
+ EXPECT_EQ(protoTransaction.merged_transaction_ids(static_cast<int32_t>(i)),
+ expectedTransaction.mergedTransactionIds[i]);
+ }
}
}
@@ -92,6 +98,7 @@
TransactionState transaction;
transaction.id = i;
transaction.originPid = static_cast<int32_t>(i);
+ transaction.mergedTransactionIds = std::vector<uint64_t>{i + 100, i + 102};
transactions.emplace_back(transaction);
mTracing.addQueuedTransaction(transaction);
}