Merge "Fix subtle bug in disableHardwareVsync" into udc-dev
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 69ca793..95f5c03 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -317,12 +317,6 @@
write /sys/kernel/debug/tracing/tracing_on 1
write /sys/kernel/tracing/tracing_on 1
-on late-init && property:ro.boot.fastboot.boottrace=enabled
- setprop debug.atrace.tags.enableflags 802922
- setprop persist.traced.enable 0
- write /sys/kernel/debug/tracing/tracing_on 1
- write /sys/kernel/tracing/tracing_on 1
-
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
# will likely fail with EBUSY as it would be in use by traced_probes.
@@ -411,6 +405,103 @@
chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace
chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace
+# Handle hyp tracing instance
+on late-init && property:ro.boot.hypervisor.vm.supported=1
+
+# Hypervisor tracing instance doesn't support changing trace_clock
+ chmod 0440 /sys/kernel/debug/tracing/hyp/trace_clock
+ chmod 0440 /sys/kernel/tracing/hyp/trace_clock
+
+ chmod 0660 /sys/kernel/debug/tracing/hyp/buffer_size_kb
+ chmod 0660 /sys/kernel/tracing/hyp/buffer_size_kb
+
+ chmod 0660 /sys/kernel/debug/tracing/hyp/tracing_on
+ chmod 0660 /sys/kernel/tracing/hyp/tracing_on
+
+# Tracing disabled by default
+ write /sys/kernel/debug/tracing/hyp/tracing_on 0
+ write /sys/kernel/tracing/hyp/tracing_on 0
+
+# Read and truncate the hyp trace.
+ chmod 0660 /sys/kernel/debug/tracing/hyp/trace
+ chmod 0660 /sys/kernel/tracing/hyp/trace
+
+# Read and truncate the per-CPU kernel trace.
+# Cannot use wildcards in .rc files. Update this if there is a phone with
+# TODO(b/249050813, ioffe): introduce per-cpu wildcard
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu0/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu0/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu1/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu1/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu2/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu2/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu3/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu3/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu4/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu4/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu5/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu5/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu6/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu6/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu7/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu7/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu8/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu8/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu9/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu9/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu10/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu10/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu11/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu11/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu12/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu12/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu13/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu13/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu14/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu14/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu15/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu15/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu16/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu16/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu17/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu17/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu18/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu18/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu19/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu19/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu20/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu20/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu21/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu21/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu22/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu22/trace
+ chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu23/trace
+ chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu23/trace
+
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/header_page
+ chmod 0440 /sys/kernel/tracing/hyp/events/header_page
+
+# Hyp events start here
+
+# hyp_enter event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/id
+
+# hyp_exit event
+ chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/enable
+ chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/enable
+# TODO(b/249050813): should this be handled in kernel?
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/format
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/format
+ chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/id
+ chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/id
+
+
on property:persist.debug.atrace.boottrace=1
start boottrace
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS
index 5f56531..ab81ecf 100644
--- a/cmds/dumpstate/OWNERS
+++ b/cmds/dumpstate/OWNERS
@@ -3,3 +3,4 @@
gavincorkery@google.com
nandana@google.com
jsharkey@android.com
+smoreland@google.com
\ No newline at end of file
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a48313a..23cdd10 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2062,6 +2062,8 @@
SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"telecom"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
if (include_sensitive_info) {
// Contains raw IP addresses, omit from reports on user builds.
RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 34ea759..ce3d669 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -442,6 +442,16 @@
static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
const std::string& location, bool read_write, bool is_secondary_dex) {
std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
+ if (read_write && GetBoolProperty("dalvik.vm.useartservice", false)) {
+ // ART Service doesn't use flock and instead assumes profile files are
+ // immutable, so ensure we don't open a file for writing when it's
+ // active.
+ // TODO(b/251921228): Normally installd isn't called at all in that
+ // case, but OTA is still an exception that uses the legacy code.
+ LOG(ERROR) << "Opening ref profile " << profile
+ << " for writing is unsafe when ART Service is enabled.";
+ return invalid_unique_fd();
+ }
return open_profile(
uid,
profile,
@@ -450,14 +460,13 @@
}
static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
- const std::string& location, bool read_write, bool is_secondary_dex) {
+ const std::string& location,
+ bool is_secondary_dex) {
std::string profile_path = create_reference_profile_path(package_name, location,
is_secondary_dex);
- unique_fd ufd = open_profile(
- uid,
- profile_path,
- read_write ? (O_CREAT | O_RDWR) : O_RDONLY,
- S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run.
+ unique_fd ufd = open_profile(uid, profile_path, O_RDONLY,
+ S_IRUSR | S_IWUSR |
+ S_IRGRP); // so that ART can also read it when apps run.
return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
clear_profile(path);
@@ -1104,8 +1113,7 @@
location = profile_name;
}
}
- return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
- is_secondary_dex);
+ return open_reference_profile_as_unique_file(uid, pkgname, location, is_secondary_dex);
}
// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index d5ca725..5e8ef5d 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -75,7 +75,7 @@
ProcessState::initWithDriver("/dev/vndbinder");
#endif
#ifndef __ANDROID__
- setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
#endif
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index ce30b41..165a868 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -561,6 +561,7 @@
case OMX_IndexConfigPriority: return "ConfigPriority";
case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate";
case OMX_IndexParamConsumerUsageBits: return "ParamConsumerUsageBits";
+ case OMX_IndexParamConsumerUsageBits64: return "ParamConsumerUsageBits64";
case OMX_IndexConfigLatency: return "ConfigLatency";
default: return asString((OMX_INDEXTYPE)i, def);
}
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 0af40dd..5ddd719 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -105,6 +105,7 @@
OMX_IndexConfigLowLatency, /**< reference: OMX_CONFIG_BOOLEANTYPE */
OMX_IndexConfigAndroidTunnelPeek, /**< reference: OMX_CONFIG_BOOLEANTYPE */
OMX_IndexConfigAndroidTunnelPeekLegacyMode, /**< reference: OMX_CONFIG_BOOLEANTYPE */
+ OMX_IndexParamConsumerUsageBits64, /**< reference: OMX_PARAM_U64TYPE */
OMX_IndexExtOtherEndUnused,
/* Time configurations */
diff --git a/include/android/OWNERS b/include/android/OWNERS
new file mode 100644
index 0000000..38f9c55
--- /dev/null
+++ b/include/android/OWNERS
@@ -0,0 +1 @@
+per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index b5e6f65..c67310e 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -235,7 +235,7 @@
KeyedVector<int32_t, Key*> mKeys;
KeyboardType mType;
std::string mLoadFileName;
- bool mLayoutOverlayApplied;
+ bool mLayoutOverlayApplied = false;
std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 68ebf75..de8ddca 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -22,6 +22,7 @@
#include <string>
#include <unordered_map>
+#include <android-base/result.h>
#include <android-base/thread_annotations.h>
#include <android/sysprop/InputProperties.sysprop.h>
#include <input/Input.h>
@@ -66,21 +67,27 @@
* checkEnableMotionPredition: the function to check whether the prediction should run. Used to
* provide an additional way of turning prediction on and off. Can be toggled at runtime.
*/
- MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath = nullptr,
+ MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);
- void record(const MotionEvent& event);
- std::vector<std::unique_ptr<MotionEvent>> predict(nsecs_t timestamp);
+ /**
+ * Record the actual motion received by the view. This event will be used for calculating the
+ * predictions.
+ *
+ * @return empty result if the event was processed correctly, error if the event is not
+ * consistent with the previously recorded events.
+ */
+ android::base::Result<void> record(const MotionEvent& event);
+ std::unique_ptr<MotionEvent> predict(nsecs_t timestamp);
bool isPredictionAvailable(int32_t deviceId, int32_t source);
private:
const nsecs_t mPredictionTimestampOffsetNanos;
- const std::string mModelPath;
const std::function<bool()> mCheckMotionPredictionEnabled;
std::unique_ptr<TfLiteMotionPredictorModel> mModel;
- // Buffers/events for each device seen by record().
- std::unordered_map</*deviceId*/ int32_t, TfLiteMotionPredictorBuffers> mDeviceBuffers;
- std::unordered_map</*deviceId*/ int32_t, MotionEvent> mLastEvents;
+
+ std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
+ std::optional<MotionEvent> mLastEvent;
};
} // namespace android
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 54e2851..7de551b41 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -99,7 +99,7 @@
class TfLiteMotionPredictorModel {
public:
// Creates a model from an encoded Flatbuffer model.
- static std::unique_ptr<TfLiteMotionPredictorModel> create(const char* modelPath);
+ static std::unique_ptr<TfLiteMotionPredictorModel> create();
~TfLiteMotionPredictorModel();
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 2e70304..51b97165 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -16,6 +16,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
#include <android-base/unique_fd.h>
#include <binder/RecordedTransaction.h>
#include <sys/mman.h>
@@ -176,13 +177,33 @@
RecordedTransaction t;
ChunkDescriptor chunk;
const long pageSize = sysconf(_SC_PAGE_SIZE);
+ struct stat fileStat;
+ if (fstat(fd.get(), &fileStat) != 0) {
+ LOG(ERROR) << "Unable to get file information";
+ return std::nullopt;
+ }
+
+ off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
do {
+ if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
+ LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+ return std::nullopt;
+ }
transaction_checksum_t checksum = 0;
if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) {
LOG(ERROR) << "Failed to read chunk descriptor.";
return std::nullopt;
}
- off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+
+ fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
@@ -194,14 +215,24 @@
size_t chunkPayloadSize =
chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
+ if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
+ LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+ return std::nullopt;
+ }
+
if (PADDING8(chunkPayloadSize) != 0) {
LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
return std::nullopt;
}
- transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>(
- mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED,
- fd.get(), mmapPageAlignedStart));
+ size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset;
+ void* mappedMemory =
+ mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart);
+ auto mmap_guard = android::base::make_scope_guard(
+ [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); });
+
+ transaction_checksum_t* payloadMap =
+ reinterpret_cast<transaction_checksum_t*>(mappedMemory);
payloadMap += mmapPayloadStartOffset /
sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
// page-alignment
@@ -218,7 +249,12 @@
LOG(ERROR) << "Checksum failed.";
return std::nullopt;
}
- lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+
+ fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
+ if (fdCurrentPosition == -1) {
+ LOG(ERROR) << "Invalid offset in file descriptor.";
+ return std::nullopt;
+ }
switch (chunk.chunkType) {
case HEADER_CHUNK: {
@@ -255,7 +291,7 @@
break;
default:
LOG(INFO) << "Unrecognized chunk.";
- continue;
+ break;
}
} while (chunk.chunkType != END_CHUNK);
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index ce6ef2b..233a8e4 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -90,16 +90,16 @@
return mMaxIncomingThreads;
}
-void RpcSession::setMaxOutgoingThreads(size_t threads) {
+void RpcSession::setMaxOutgoingConnections(size_t connections) {
RpcMutexLockGuard _l(mMutex);
LOG_ALWAYS_FATAL_IF(mStartedSetup,
"Must set max outgoing threads before setting up connections");
- mMaxOutgoingThreads = threads;
+ mMaxOutgoingConnections = connections;
}
size_t RpcSession::getMaxOutgoingThreads() {
RpcMutexLockGuard _l(mMutex);
- return mMaxOutgoingThreads;
+ return mMaxOutgoingConnections;
}
bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
@@ -558,11 +558,11 @@
return status;
}
- size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
- ALOGI_IF(outgoingThreads != numThreadsAvailable,
+ size_t outgoingConnections = std::min(numThreadsAvailable, mMaxOutgoingConnections);
+ ALOGI_IF(outgoingConnections != numThreadsAvailable,
"Server hints client to start %zu outgoing threads, but client will only start %zu "
"because it is preconfigured to start at most %zu outgoing threads.",
- numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads);
+ numThreadsAvailable, outgoingConnections, mMaxOutgoingConnections);
// TODO(b/189955605): we should add additional sessions dynamically
// instead of all at once - the other side should be responsible for setting
@@ -571,10 +571,10 @@
// any requests at all.
// we've already setup one client
- LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu "
- "incoming threads",
- outgoingThreads, numThreadsAvailable, mMaxIncomingThreads);
- for (size_t i = 0; i + 1 < outgoingThreads; i++) {
+ LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing connections (server max: "
+ "%zu) and %zu incoming threads",
+ outgoingConnections, numThreadsAvailable, mMaxIncomingThreads);
+ for (size_t i = 0; i + 1 < outgoingConnections; i++) {
if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status;
}
@@ -932,7 +932,8 @@
(session->server()
? "This is a server session, so see RpcSession::setMaxIncomingThreads "
"for the corresponding client"
- : "This is a client session, so see RpcSession::setMaxOutgoingThreads "
+ : "This is a client session, so see "
+ "RpcSession::setMaxOutgoingConnections "
"for this client or RpcServer::setMaxThreads for the corresponding "
"server"));
return WOULD_BLOCK;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index b27f102..2b0e5ba 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -557,13 +557,12 @@
.parcelDataSize = static_cast<uint32_t>(data.dataSize()),
};
- constexpr size_t kWaitMaxUs = 1000000;
- constexpr size_t kWaitLogUs = 10000;
- size_t waitUs = 0;
-
// Oneway calls have no sync point, so if many are sent before, whether this
// is a twoway or oneway transaction, they may have filled up the socket.
// So, make sure we drain them before polling
+ constexpr size_t kWaitMaxUs = 1000000;
+ constexpr size_t kWaitLogUs = 10000;
+ size_t waitUs = 0;
iovec iovs[]{
{&command, sizeof(RpcWireHeader)},
@@ -591,8 +590,9 @@
},
rpcFields->mFds.get());
status != OK) {
- // TODO(b/167966510): need to undo onBinderLeaving - we know the
- // refcount isn't successfully transferred.
+ // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate
+ // errors here, then we may need to undo the binder-sent counts for the transaction as
+ // well as for the binder objects in the Parcel
return status;
}
diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp
index 194254a..2b67f03 100644
--- a/libs/binder/ServiceManagerHost.cpp
+++ b/libs/binder/ServiceManagerHost.cpp
@@ -159,8 +159,8 @@
LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value());
auto rpcSession = RpcSession::make();
- if (options.maxOutgoingThreads.has_value()) {
- rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads);
+ if (options.maxOutgoingConnections.has_value()) {
+ rpcSession->setMaxOutgoingConnections(*options.maxOutgoingConnections);
}
if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort());
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index c78f870..55167a7 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -224,12 +224,12 @@
// }
// Resources are cleaned up when the object is destroyed.
//
-// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated.
-// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked
-// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads.
-// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|.
+// For each returned binder object, at most |maxOutgoingConnections| outgoing connections are
+// instantiated, depending on how many the service on the device is configured with.
+// Hence, only |maxOutgoingConnections| calls can be made simultaneously.
+// See also RpcSession::setMaxOutgoingConnections.
struct RpcDelegateServiceManagerOptions {
- std::optional<size_t> maxOutgoingThreads;
+ std::optional<size_t> maxOutgoingConnections;
};
sp<IServiceManager> createRpcDelegateServiceManager(
const RpcDelegateServiceManagerOptions& options);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 25193a3..1001b64 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -119,7 +119,10 @@
[[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd);
/**
- * This must be called before adding a client session.
+ * This must be called before adding a client session. This corresponds
+ * to the number of incoming connections to RpcSession objects in the
+ * server, which will correspond to the number of outgoing connections
+ * in client RpcSession objects.
*
* If this is not specified, this will be a single-threaded server.
*
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 40faf2c..0750ccf 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -54,8 +54,6 @@
*/
class RpcSession final : public virtual RefBase {
public:
- static constexpr size_t kDefaultMaxOutgoingThreads = 10;
-
// Create an RpcSession with default configuration (raw sockets).
static sp<RpcSession> make();
@@ -67,26 +65,30 @@
/**
* Set the maximum number of incoming threads allowed to be made (for things like callbacks).
* By default, this is 0. This must be called before setting up this connection as a client.
- * Server sessions will inherits this value from RpcServer.
+ * Server sessions will inherits this value from RpcServer. Each thread will serve a
+ * connection to the remote RpcSession.
*
* If this is called, 'shutdown' on this session must also be called.
* Otherwise, a threadpool will leak.
*
- * TODO(b/189955605): start these dynamically
+ * TODO(b/189955605): start these lazily - currently all are started
*/
void setMaxIncomingThreads(size_t threads);
size_t getMaxIncomingThreads();
/**
- * Set the maximum number of outgoing threads allowed to be made.
- * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this
- * connection as a client.
+ * Set the maximum number of outgoing connections allowed to be made.
+ * By default, this is |kDefaultMaxOutgoingConnections|. This must be called before setting up
+ * this connection as a client.
*
- * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession
- * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where
- * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|.
+ * For an RpcSession client, if you are connecting to a server which starts N threads,
+ * then this must be set to >= N. If you set the maximum number of outgoing connections
+ * to 1, but the server requests 10, then it would be considered an error. If you set a
+ * maximum number of connections to 10, and the server requests 1, then only 1 will be
+ * created. This API is used to limit the amount of resources a server can request you
+ * create.
*/
- void setMaxOutgoingThreads(size_t threads);
+ void setMaxOutgoingConnections(size_t connections);
size_t getMaxOutgoingThreads();
/**
@@ -219,6 +221,8 @@
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
+ static constexpr size_t kDefaultMaxOutgoingConnections = 10;
+
// internal version of setProtocolVersion that
// optionally skips the mStartedSetup check
[[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted);
@@ -368,7 +372,7 @@
bool mStartedSetup = false;
size_t mMaxIncomingThreads = 0;
- size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
+ size_t mMaxOutgoingConnections = kDefaultMaxOutgoingConnections;
std::optional<uint32_t> mProtocolVersion;
FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 42d226b..a157792 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -126,11 +126,11 @@
void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
ARpcSession_FileDescriptorTransportMode mode);
-// Sets the maximum number of incoming threads.
+// Sets the maximum number of incoming threads, to service connections.
void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);
-// Sets the maximum number of outgoing threads.
-void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads);
+// Sets the maximum number of outgoing connections.
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* session, size_t connections);
// Decrements the refcount of the underlying RpcSession object.
void ARpcSession_free(ARpcSession* session);
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index daff8c1..a167f23 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -265,8 +265,8 @@
session->setMaxIncomingThreads(threads);
}
-void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) {
+void ARpcSession_setMaxOutgoingConnections(ARpcSession* handle, size_t connections) {
auto session = handleToStrongPointer<RpcSession>(handle);
- session->setMaxOutgoingThreads(threads);
+ session->setMaxOutgoingConnections(connections);
}
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index f68612c..d833b83 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -26,11 +26,11 @@
#pragma once
+#include <android/binder_status.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
-
-#include <android/binder_status.h>
+#include <uchar.h>
struct AIBinder;
typedef struct AIBinder AIBinder;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 5b2532a..882f1d6 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -52,7 +52,7 @@
constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService";
constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
-constexpr unsigned int kShutdownWaitTime = 10;
+constexpr unsigned int kShutdownWaitTime = 11;
constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
class MyTestFoo : public IFoo {
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index afd414a..d36ebac 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -21,6 +21,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
target: {
darwin: {
enabled: false,
@@ -72,6 +73,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
target: {
darwin: {
enabled: false,
@@ -129,6 +131,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
index 0b517cf..28c5390 100644
--- a/libs/binder/rust/rpcbinder/src/session.rs
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -75,11 +75,14 @@
};
}
- /// Sets the maximum number of outgoing threads.
- pub fn set_max_outgoing_threads(&self, threads: usize) {
+ /// Sets the maximum number of outgoing connections.
+ pub fn set_max_outgoing_connections(&self, connections: usize) {
// SAFETY - Only passes the 'self' pointer as an opaque handle.
unsafe {
- binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads)
+ binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections(
+ self.as_ptr(),
+ connections,
+ )
};
}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 6f686fb..5557168 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -209,8 +209,8 @@
}
/// Mark this binder object with local stability, which is vendor if we are
- /// building for the VNDK and system otherwise.
- #[cfg(any(vendor_ndk, android_vndk))]
+ /// building for android_vendor and system otherwise.
+ #[cfg(android_vendor)]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
@@ -220,8 +220,8 @@
}
/// Mark this binder object with local stability, which is vendor if we are
- /// building for the VNDK and system otherwise.
- #[cfg(not(any(vendor_ndk, android_vndk)))]
+ /// building for android_vendor and system otherwise.
+ #[cfg(not(android_vendor))]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp
index 464da60..77a5fa8 100644
--- a/libs/binder/tests/binderHostDeviceTest.cpp
+++ b/libs/binder/tests/binderHostDeviceTest.cpp
@@ -66,7 +66,7 @@
void initHostRpcServiceManagerOnce() {
static std::once_flag gSmOnce;
std::call_once(gSmOnce, [] {
- setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1}));
+ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1}));
});
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 955c650..8974ad7 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -507,7 +507,13 @@
}
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
- EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
+
+ // b/268232063 - succeeds ~0.08% of the time
+ {
+ auto ret = IPCThreadState::self()->freeze(pid, true, 0);
+ EXPECT_TRUE(ret == -EAGAIN || ret == OK);
+ }
+
EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply));
@@ -1370,7 +1376,7 @@
}));
}
- data.writeInt32(100);
+ data.writeInt32(500);
// Give a chance for all threads to be used
EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 84c93dd..6e34d25 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -350,7 +350,7 @@
for (const auto& session : sessions) {
CHECK(session->setProtocolVersion(clientVersion));
session->setMaxIncomingThreads(options.numIncomingConnections);
- session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setMaxOutgoingConnections(options.numOutgoingConnections);
session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
switch (socketType) {
@@ -544,6 +544,8 @@
GTEST_SKIP() << "This test requires multiple threads";
}
+ constexpr size_t kNumServerThreads = 3;
+
// This test forces a oneway transaction to be queued by issuing two
// `blockingSendFdOneway` calls, then drains the queue by issuing two
// `blockingRecvFd` calls.
@@ -552,7 +554,7 @@
// https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY
auto proc = createRpcTestSocketServerProcess({
- .numThreads = 3,
+ .numThreads = kNumServerThreads,
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
.serverSupportedFileDescriptorTransportModes =
{RpcSession::FileDescriptorTransportMode::UNIX},
@@ -573,6 +575,8 @@
EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
CHECK(android::base::ReadFdToString(fdB.get(), &result));
EXPECT_EQ(result, "b");
+
+ saturateThreadPool(kNumServerThreads, proc.rootIface);
}
TEST_P(BinderRpc, OnewayCallQueueing) {
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 714f063..a27bd2f 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -85,7 +85,9 @@
}
};
-int main(int argc, const char* argv[]) {
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+
LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
base::unique_fd writeEnd(atoi(argv[1]));
base::unique_fd readEnd(atoi(argv[2]));
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index b3bb5eb..84abbac 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -71,7 +71,7 @@
auto session = android::RpcSession::make(std::move(factory));
EXPECT_TRUE(session->setProtocolVersion(clientVersion));
- session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setMaxOutgoingConnections(options.numOutgoingConnections);
session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
status = session->setupPreconnectedClient({}, [&]() {
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index 8ea948c..a881582 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -104,3 +104,42 @@
defaults: ["binder_fuzz_defaults"],
srcs: ["MemoryDealerFuzz.cpp"],
}
+
+cc_fuzz {
+ name: "binder_recordedTransactionFileFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["RecordedTransactionFileFuzz.cpp"],
+ corpus: [
+ "recorded_transaction_corpus/*",
+ ],
+}
+
+cc_fuzz {
+ name: "binder_recordedTransactionFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["RecordedTransactionFuzz.cpp"],
+ target: {
+ android: {
+ shared_libs: [
+ "libcutils",
+ "libutils",
+ "libbase",
+ "libbinder",
+ ],
+ static_libs: ["libbinder_random_parcel"],
+ },
+ host: {
+ static_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libbase",
+ "libbinder",
+ "libbinder_random_parcel",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
new file mode 100644
index 0000000..73790fa
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <filesystem>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::FILE* intermediateFile = std::tmpfile();
+ fwrite(data, sizeof(uint8_t), size, intermediateFile);
+ rewind(intermediateFile);
+ int fileNumber = fileno(intermediateFile);
+
+ android::base::unique_fd fd(fileNumber);
+
+ auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd);
+
+ std::fclose(intermediateFile);
+
+ if (transaction.has_value()) {
+ intermediateFile = std::tmpfile();
+
+ android::base::unique_fd fdForWriting(fileno(intermediateFile));
+ auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+ std::fclose(intermediateFile);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
new file mode 100644
index 0000000..943fb9f
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <android-base/macros.h>
+#include <binder/RecordedTransaction.h>
+#include <fuzzbinder/random_parcel.h>
+#include <filesystem>
+#include <string>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::fillRandomParcel;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+ android::String16 interfaceName =
+ android::String16(provider.ConsumeRandomLengthString().c_str());
+
+ uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+ time_t sec = provider.ConsumeIntegral<time_t>();
+ long nsec = provider.ConsumeIntegral<long>();
+ timespec timestamp = {.tv_sec = sec, .tv_nsec = nsec};
+ android::status_t transactionStatus = provider.ConsumeIntegral<android::status_t>();
+
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+
+ // same options so that FDs and binders could be shared in both Parcels
+ android::RandomParcelOptions options;
+
+ android::Parcel p0, p1;
+ fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
+ fillRandomParcel(&p1, std::move(provider), &options);
+
+ auto transaction =
+ android::binder::debug::RecordedTransaction::fromDetails(interfaceName, code, flags,
+ timestamp, p0, p1,
+ transactionStatus);
+
+ if (transaction.has_value()) {
+ std::FILE* intermediateFile = std::tmpfile();
+ android::base::unique_fd fdForWriting(fileno(intermediateFile));
+ auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting);
+
+ std::fclose(intermediateFile);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
new file mode 100644
index 0000000..79442078
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording
Binary files differ
diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
new file mode 100644
index 0000000..658addb
--- /dev/null
+++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction
Binary files differ
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 29924ff..96dcce1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -11,7 +11,7 @@
name: "fakeservicemanager_defaults",
host_supported: true,
srcs: [
- "ServiceManager.cpp",
+ "FakeServiceManager.cpp",
],
shared_libs: [
@@ -28,7 +28,7 @@
cc_library {
name: "libfakeservicemanager",
defaults: ["fakeservicemanager_defaults"],
- export_include_dirs: ["include/fakeservicemanager"],
+ export_include_dirs: ["include"],
}
cc_test_host {
@@ -38,5 +38,5 @@
"test_sm.cpp",
],
static_libs: ["libgmock"],
- local_include_dirs: ["include/fakeservicemanager"],
+ local_include_dirs: ["include"],
}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
similarity index 66%
rename from libs/fakeservicemanager/ServiceManager.cpp
rename to libs/fakeservicemanager/FakeServiceManager.cpp
index 1109ad8..3272bbc 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
namespace android {
-ServiceManager::ServiceManager() {}
+FakeServiceManager::FakeServiceManager() {}
-sp<IBinder> ServiceManager::getService( const String16& name) const {
+sp<IBinder> FakeServiceManager::getService( const String16& name) const {
// Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
return checkService(name);
}
-sp<IBinder> ServiceManager::checkService( const String16& name) const {
+sp<IBinder> FakeServiceManager::checkService( const String16& name) const {
auto it = mNameToService.find(name);
if (it == mNameToService.end()) {
return nullptr;
@@ -33,7 +33,7 @@
return it->second;
}
-status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
if (service == nullptr) {
@@ -43,7 +43,7 @@
return NO_ERROR;
}
-Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) {
Vector<String16> services;
for (auto const& [name, service] : mNameToService) {
(void) service;
@@ -52,19 +52,19 @@
return services;
}
-IBinder* ServiceManager::onAsBinder() {
+IBinder* FakeServiceManager::onAsBinder() {
return nullptr;
}
-sp<IBinder> ServiceManager::waitForService(const String16& name) {
+sp<IBinder> FakeServiceManager::waitForService(const String16& name) {
return checkService(name);
}
-bool ServiceManager::isDeclared(const String16& name) {
+bool FakeServiceManager::isDeclared(const String16& name) {
return mNameToService.find(name) != mNameToService.end();
}
-Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) {
+Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) {
Vector<String16> out;
const String16 prefix = name + String16("/");
for (const auto& [registeredName, service] : mNameToService) {
@@ -76,38 +76,38 @@
return out;
}
-std::optional<String16> ServiceManager::updatableViaApex(const String16& name) {
+std::optional<String16> FakeServiceManager::updatableViaApex(const String16& name) {
(void)name;
return std::nullopt;
}
-Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) {
+Vector<String16> FakeServiceManager::getUpdatableNames(const String16& apexName) {
(void)apexName;
return {};
}
-std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo(
+std::optional<IServiceManager::ConnectionInfo> FakeServiceManager::getConnectionInfo(
const String16& name) {
(void)name;
return std::nullopt;
}
-status_t ServiceManager::registerForNotifications(const String16&,
+status_t FakeServiceManager::registerForNotifications(const String16&,
const sp<LocalRegistrationCallback>&) {
return INVALID_OPERATION;
}
-status_t ServiceManager::unregisterForNotifications(const String16&,
+status_t FakeServiceManager::unregisterForNotifications(const String16&,
const sp<LocalRegistrationCallback>&) {
return INVALID_OPERATION;
}
-std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() {
+std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDebugInfo() {
std::vector<IServiceManager::ServiceDebugInfo> ret;
return ret;
}
-void ServiceManager::clear() {
+void FakeServiceManager::clear() {
mNameToService.clear();
}
} // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
similarity index 96%
rename from libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
rename to libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
index ba6bb7d..97add24 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h
@@ -28,9 +28,9 @@
* A local host simple implementation of IServiceManager, that does not
* communicate over binder.
*/
-class ServiceManager : public IServiceManager {
+class FakeServiceManager : public IServiceManager {
public:
- ServiceManager();
+ FakeServiceManager();
sp<IBinder> getService( const String16& name) const override;
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 8682c1c..6fc21c6 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -21,14 +21,14 @@
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
-#include "ServiceManager.h"
+#include "fakeservicemanager/FakeServiceManager.h"
using android::sp;
using android::BBinder;
using android::IBinder;
using android::OK;
using android::status_t;
-using android::ServiceManager;
+using android::FakeServiceManager;
using android::String16;
using android::IServiceManager;
using testing::ElementsAre;
@@ -45,19 +45,19 @@
}
TEST(AddService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
TEST(AddService, SadNullBinder) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
}
TEST(AddService, HappyOverExistingService) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -65,7 +65,7 @@
}
TEST(AddService, HappyClearAddedService) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
EXPECT_NE(sm->getService(String16("foo")), nullptr);
@@ -74,7 +74,7 @@
}
TEST(GetService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -84,13 +84,13 @@
}
TEST(GetService, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->getService(String16("foo")), nullptr);
}
TEST(ListServices, AllServices) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
@@ -109,13 +109,13 @@
}
TEST(WaitForService, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
}
TEST(WaitForService, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
@@ -125,13 +125,13 @@
}
TEST(IsDeclared, NonExistant) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
EXPECT_FALSE(sm->isDeclared(String16("foo")));
}
TEST(IsDeclared, HappyHappy) {
- auto sm = new ServiceManager();
+ auto sm = new FakeServiceManager();
sp<IBinder> service = getBinder();
EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS
index 347c4e0..1db8cbe 100644
--- a/libs/graphicsenv/OWNERS
+++ b/libs/graphicsenv/OWNERS
@@ -1,10 +1,4 @@
-abdolrashidi@google.com
-cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
ianelliott@google.com
-lfy@google.com
lpy@google.com
-romanl@google.com
-vantablack@google.com
-yuxinhu@google.com
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 99bf6ba..46fb068 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -15,8 +15,10 @@
*/
// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <gui/Choreographer.h>
+#include <gui/TraceUtils.h>
#include <jni.h>
#undef LOG_TAG
@@ -297,6 +299,8 @@
mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
if (cb.vsyncCallback != nullptr) {
+ ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
+ vsyncEventData.preferredVsyncId());
const ChoreographerFrameCallbackDataImpl frameCallbackData =
createFrameCallbackData(timestamp);
registerStartTime();
@@ -306,8 +310,10 @@
cb.data);
mInCallback = false;
} else if (cb.callback64 != nullptr) {
+ ATRACE_FORMAT("AChoreographer_frameCallback64");
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
+ ATRACE_FORMAT("AChoreographer_frameCallback");
cb.callback(timestamp, cb.data);
}
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index f6bba16..b391337 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -74,7 +74,7 @@
surfaceDamageRegion(),
api(-1),
colorTransform(mat4()),
- bgColorAlpha(0),
+ bgColor(0),
bgColorDataspace(ui::Dataspace::UNKNOWN),
colorSpaceAgnostic(false),
shadowRadius(0.0f),
@@ -140,7 +140,10 @@
SAFE_PARCEL(output.writeFloat, cornerRadius);
SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
SAFE_PARCEL(output.writeParcelable, metadata);
- SAFE_PARCEL(output.writeFloat, bgColorAlpha);
+ SAFE_PARCEL(output.writeFloat, bgColor.r);
+ SAFE_PARCEL(output.writeFloat, bgColor.g);
+ SAFE_PARCEL(output.writeFloat, bgColor.b);
+ SAFE_PARCEL(output.writeFloat, bgColor.a);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
SAFE_PARCEL(output.writeBool, colorSpaceAgnostic);
SAFE_PARCEL(output.writeVectorSize, listeners);
@@ -259,7 +262,14 @@
SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
SAFE_PARCEL(input.readParcelable, &metadata);
- SAFE_PARCEL(input.readFloat, &bgColorAlpha);
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.r = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.g = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.b = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ bgColor.a = tmpFloat;
SAFE_PARCEL(input.readUint32, &tmpUint32);
bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32);
SAFE_PARCEL(input.readBool, &colorSpaceAgnostic);
@@ -618,8 +628,7 @@
}
if (other.what & eBackgroundColorChanged) {
what |= eBackgroundColorChanged;
- color.rgb = other.color.rgb;
- bgColorAlpha = other.bgColorAlpha;
+ bgColor = other.bgColor;
bgColorDataspace = other.bgColorDataspace;
}
if (other.what & eMetadataChanged) {
@@ -752,7 +761,7 @@
CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform);
if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged;
if (other.what & eInputInfoChanged) diff |= eInputInfoChanged;
- CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace);
+ CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace);
if (other.what & eMetadataChanged) diff |= eMetadataChanged;
CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 001d475..2f5830d 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1363,7 +1363,8 @@
(mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
(mask & layer_state_t::eEnableBackpressure) ||
(mask & layer_state_t::eIgnoreDestinationFrame) ||
- (mask & layer_state_t::eLayerIsDisplayDecoration)) {
+ (mask & layer_state_t::eLayerIsDisplayDecoration) ||
+ (mask & layer_state_t::eLayerIsRefreshRateIndicator)) {
s->what |= layer_state_t::eFlagsChanged;
}
s->flags &= ~mask;
@@ -1560,8 +1561,8 @@
}
s->what |= layer_state_t::eBackgroundColorChanged;
- s->color.rgb = color;
- s->bgColorAlpha = alpha;
+ s->bgColor.rgb = color;
+ s->bgColor.a = alpha;
s->bgColorDataspace = dataspace;
registerSurfaceControlForCallback(sc);
diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp
index 23f0921..76c60c2 100644
--- a/libs/gui/VsyncEventData.cpp
+++ b/libs/gui/VsyncEventData.cpp
@@ -23,6 +23,9 @@
namespace android::gui {
+static_assert(VsyncEventData::kFrameTimelinesLength == 7,
+ "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_LENGTH (and here)");
+
int64_t VsyncEventData::preferredVsyncId() const {
return frameTimelines[preferredFrameTimelineIndex].vsyncId;
}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index da144bd..6e3be5c 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -160,6 +160,7 @@
// This is needed to maintain compatibility for SurfaceView scaling behavior.
// See SurfaceView scaling behavior for more details.
eIgnoreDestinationFrame = 0x400,
+ eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR
};
enum {
@@ -226,9 +227,9 @@
bool hasBufferChanges() const;
// Layer hierarchy updates.
- static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eBackgroundColorChanged |
- layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
- layer_state_t::eReparent;
+ static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent |
+ layer_state_t::eLayerStackChanged;
// Geometry updates.
static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
@@ -264,9 +265,8 @@
static constexpr uint64_t AFFECTS_CHILDREN = layer_state_t::GEOMETRY_CHANGES |
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
- layer_state_t::eFlagsChanged | layer_state_t::eLayerStackChanged |
- layer_state_t::eTrustedOverlayChanged | layer_state_t::eFrameRateChanged |
- layer_state_t::eFixedTransformHintChanged;
+ layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
+ layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES |
@@ -333,7 +333,7 @@
// The following refer to the alpha, and dataspace, respectively of
// the background color layer
- float bgColorAlpha;
+ half4 bgColor;
ui::Dataspace bgColorDataspace;
// A color space agnostic layer means the color of this layer can be
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 1bfe462..198908d 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -16,17 +16,59 @@
#define LOG_TAG "Surface"
-#include <gui/view/Surface.h>
-
+#include <android/binder_libbinder.h>
+#include <android/binder_parcel.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
-
-#include <utils/Log.h>
-
#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+#include <system/window.h>
+#include <utils/Log.h>
namespace android {
namespace view {
+// Since this is a parcelable utility and we want to keep the wire format stable, only build this
+// when building the system libgui to detect any issues loading the wrong libgui from
+// libnativewindow
+
+#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__))
+
+extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel) {
+ int value;
+ int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
+ if (err != OK || value != NATIVE_WINDOW_SURFACE) {
+ ALOGE("Error: ANativeWindow is not backed by Surface");
+ return STATUS_BAD_VALUE;
+ }
+ // Use a android::view::Surface to parcelize the window
+ android::view::Surface shimSurface;
+ shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window);
+ shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window);
+ return shimSurface.writeToParcel(parcel);
+}
+
+extern "C" status_t android_view_Surface_readFromParcel(
+ const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
+ // Use a android::view::Surface to unparcel the window
+ android::view::Surface shimSurface;
+ status_t ret = shimSurface.readFromParcel(parcel);
+ if (ret != OK) {
+ ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+ auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false,
+ shimSurface.surfaceControlHandle);
+ ANativeWindow* anw = surface.get();
+ ANativeWindow_acquire(anw);
+ *outWindow = anw;
+ return STATUS_OK;
+}
+
+#endif
+
status_t Surface::writeToParcel(Parcel* parcel) const {
return writeToParcel(parcel, false);
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index bdbd708..311b244 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -555,10 +555,10 @@
ATRACE_NAME(message.c_str());
}
ALOGD_IF(debugTransportPublisher(),
- "channel '%s' publisher ~ %s: seq=%u, deviceId=%d, source=%s, "
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, "
"action=%s, flags=0x%x, keyCode=%s, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%" PRId64 ", eventTime=%" PRId64,
- mChannel->getName().c_str(), __func__, seq, deviceId,
+ mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
inputEventSourceToString(source).c_str(), KeyEvent::actionToString(action), flags,
KeyEvent::getLabel(keyCode), scanCode, metaState, repeatCount, downTime, eventTime);
@@ -608,13 +608,13 @@
if (debugTransportPublisher()) {
std::string transformString;
transform.dump(transformString, "transform", " ");
- ALOGD("channel '%s' publisher ~ %s: seq=%u, deviceId=%d, source=%s, "
+ ALOGD("channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, "
"displayId=%" PRId32 ", "
"action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32 " \n%s",
- mChannel->getName().c_str(), __func__, seq, deviceId,
+ mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
inputEventSourceToString(source).c_str(), displayId,
MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags,
metaState, buttonState, motionClassificationToString(classification), xPrecision,
@@ -680,8 +680,8 @@
mChannel->getName().c_str(), toString(hasFocus));
ATRACE_NAME(message.c_str());
}
- ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, hasFocus=%s",
- mChannel->getName().c_str(), __func__, seq, toString(hasFocus));
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, id=%d, hasFocus=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, toString(hasFocus));
InputMessage msg;
msg.header.type = InputMessage::Type::FOCUS;
@@ -700,8 +700,8 @@
ATRACE_NAME(message.c_str());
}
ALOGD_IF(debugTransportPublisher(),
- "channel '%s' publisher ~ %s: seq=%u, pointerCaptureEnabled=%s",
- mChannel->getName().c_str(), __func__, seq, toString(pointerCaptureEnabled));
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, pointerCaptureEnabled=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, toString(pointerCaptureEnabled));
InputMessage msg;
msg.header.type = InputMessage::Type::CAPTURE;
@@ -720,8 +720,8 @@
ATRACE_NAME(message.c_str());
}
ALOGD_IF(debugTransportPublisher(),
- "channel '%s' publisher ~ %s: seq=%u, x=%f, y=%f, isExiting=%s",
- mChannel->getName().c_str(), __func__, seq, x, y, toString(isExiting));
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, x=%f, y=%f, isExiting=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, x, y, toString(isExiting));
InputMessage msg;
msg.header.type = InputMessage::Type::DRAG;
@@ -740,8 +740,9 @@
mChannel->getName().c_str(), toString(isInTouchMode));
ATRACE_NAME(message.c_str());
}
- ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, isInTouchMode=%s",
- mChannel->getName().c_str(), __func__, seq, toString(isInTouchMode));
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: seq=%u, id=%d, isInTouchMode=%s",
+ mChannel->getName().c_str(), __func__, seq, eventId, toString(isInTouchMode));
InputMessage msg;
msg.header.type = InputMessage::Type::TOUCH_MODE;
@@ -752,15 +753,18 @@
}
android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
- ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s", mChannel->getName().c_str(),
- __func__);
-
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: %s",
+ mChannel->getName().c_str(), __func__, strerror(result));
return android::base::Error(result);
}
if (msg.header.type == InputMessage::Type::FINISHED) {
+ ALOGD_IF(debugTransportPublisher(),
+ "channel '%s' publisher ~ %s: finished: seq=%u, handled=%s",
+ mChannel->getName().c_str(), __func__, msg.header.seq,
+ toString(msg.body.finished.handled));
return Finished{
.seq = msg.header.seq,
.handled = msg.body.finished.handled,
@@ -769,6 +773,8 @@
}
if (msg.header.type == InputMessage::Type::TIMELINE) {
+ ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: timeline: id=%d",
+ mChannel->getName().c_str(), __func__, msg.body.timeline.eventId);
return Timeline{
.inputEventId = msg.body.timeline.eventId,
.graphicsTimeline = msg.body.timeline.graphicsTimeline,
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 7d11ef2..b4151c6 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -35,7 +35,6 @@
namespace android {
namespace {
-const char DEFAULT_MODEL_PATH[] = "/system/etc/motion_predictor_model.fb";
const int64_t PREDICTION_INTERVAL_NANOS =
12500000 / 3; // TODO(b/266747937): Get this from the model.
@@ -62,48 +61,58 @@
// --- MotionPredictor ---
-MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath,
+MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
std::function<bool()> checkMotionPredictionEnabled)
: mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos),
- mModelPath(modelPath == nullptr ? DEFAULT_MODEL_PATH : modelPath),
mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {}
-void MotionPredictor::record(const MotionEvent& event) {
+android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
+ if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
+ // We still have an active gesture for another device. The provided MotionEvent is not
+ // consistent the previous gesture.
+ LOG(ERROR) << "Inconsistent event stream: last event is " << *mLastEvent << ", but "
+ << __func__ << " is called with " << event;
+ return android::base::Error()
+ << "Inconsistent event stream: still have an active gesture from device "
+ << mLastEvent->getDeviceId() << ", but received " << event;
+ }
if (!isPredictionAvailable(event.getDeviceId(), event.getSource())) {
ALOGE("Prediction not supported for device %d's %s source", event.getDeviceId(),
inputEventSourceToString(event.getSource()).c_str());
- return;
+ return {};
}
// Initialise the model now that it's likely to be used.
if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create(mModelPath.c_str());
+ mModel = TfLiteMotionPredictorModel::create();
}
- TfLiteMotionPredictorBuffers& buffers =
- mDeviceBuffers.try_emplace(event.getDeviceId(), mModel->inputLength()).first->second;
+ if (mBuffers == nullptr) {
+ mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+ }
const int32_t action = event.getActionMasked();
- if (action == AMOTION_EVENT_ACTION_UP) {
+ if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
- buffers.reset();
- return;
+ mBuffers->reset();
+ mLastEvent.reset();
+ return {};
} else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
ALOGD_IF(isDebug(), "Skipping unsupported %s action",
MotionEvent::actionToString(action).c_str());
- return;
+ return {};
}
if (event.getPointerCount() != 1) {
ALOGD_IF(isDebug(), "Prediction not supported for multiple pointers");
- return;
+ return {};
}
const int32_t toolType = event.getPointerProperties(0)->toolType;
if (toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS) {
ALOGD_IF(isDebug(), "Prediction not supported for non-stylus tool: %s",
motionToolTypeToString(toolType));
- return;
+ return {};
}
for (size_t i = 0; i <= event.getHistorySize(); ++i) {
@@ -111,100 +120,98 @@
continue;
}
const PointerCoords* coords = event.getHistoricalRawPointerCoords(0, i);
- buffers.pushSample(event.getHistoricalEventTime(i),
- {
- .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X),
- .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y),
- .pressure = event.getHistoricalPressure(0, i),
- .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT, 0,
- i),
- .orientation = event.getHistoricalOrientation(0, i),
- });
+ mBuffers->pushSample(event.getHistoricalEventTime(i),
+ {
+ .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+ .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y),
+ .pressure = event.getHistoricalPressure(0, i),
+ .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT,
+ 0, i),
+ .orientation = event.getHistoricalOrientation(0, i),
+ });
}
- mLastEvents.try_emplace(event.getDeviceId())
- .first->second.copyFrom(&event, /*keepHistory=*/false);
+ if (!mLastEvent) {
+ mLastEvent = MotionEvent();
+ }
+ mLastEvent->copyFrom(&event, /*keepHistory=*/false);
+ return {};
}
-std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict(nsecs_t timestamp) {
- std::vector<std::unique_ptr<MotionEvent>> predictions;
-
- for (const auto& [deviceId, buffer] : mDeviceBuffers) {
- if (!buffer.isReady()) {
- continue;
- }
-
- LOG_ALWAYS_FATAL_IF(!mModel);
- buffer.copyTo(*mModel);
- LOG_ALWAYS_FATAL_IF(!mModel->invoke());
-
- // Read out the predictions.
- const std::span<const float> predictedR = mModel->outputR();
- const std::span<const float> predictedPhi = mModel->outputPhi();
- const std::span<const float> predictedPressure = mModel->outputPressure();
-
- TfLiteMotionPredictorSample::Point axisFrom = buffer.axisFrom().position;
- TfLiteMotionPredictorSample::Point axisTo = buffer.axisTo().position;
-
- if (isDebug()) {
- ALOGD("deviceId: %d", deviceId);
- ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y);
- ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y);
- ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str());
- ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str());
- ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str());
- ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str());
- ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str());
- ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str());
- ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str());
- ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str());
- }
-
- const MotionEvent& event = mLastEvents[deviceId];
- bool hasPredictions = false;
- std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
- int64_t predictionTime = buffer.lastTimestamp();
- const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
-
- for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
- const TfLiteMotionPredictorSample::Point point =
- convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
- // TODO(b/266747654): Stop predictions if confidence is < some threshold.
-
- ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y);
- PointerCoords coords;
- coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y);
- // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
- coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
-
- predictionTime += PREDICTION_INTERVAL_NANOS;
- if (i == 0) {
- hasPredictions = true;
- prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
- event.getDisplayId(), INVALID_HMAC,
- AMOTION_EVENT_ACTION_MOVE, event.getActionButton(),
- event.getFlags(), event.getEdgeFlags(), event.getMetaState(),
- event.getButtonState(), event.getClassification(),
- event.getTransform(), event.getXPrecision(),
- event.getYPrecision(), event.getRawXCursorPosition(),
- event.getRawYCursorPosition(), event.getRawTransform(),
- event.getDownTime(), predictionTime, event.getPointerCount(),
- event.getPointerProperties(), &coords);
- } else {
- prediction->addSample(predictionTime, &coords);
- }
-
- axisFrom = axisTo;
- axisTo = point;
- }
- // TODO(b/266747511): Interpolate to futureTime?
- if (hasPredictions) {
- predictions.push_back(std::move(prediction));
- }
+std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
+ if (mBuffers == nullptr || !mBuffers->isReady()) {
+ return nullptr;
}
- return predictions;
+
+ LOG_ALWAYS_FATAL_IF(!mModel);
+ mBuffers->copyTo(*mModel);
+ LOG_ALWAYS_FATAL_IF(!mModel->invoke());
+
+ // Read out the predictions.
+ const std::span<const float> predictedR = mModel->outputR();
+ const std::span<const float> predictedPhi = mModel->outputPhi();
+ const std::span<const float> predictedPressure = mModel->outputPressure();
+
+ TfLiteMotionPredictorSample::Point axisFrom = mBuffers->axisFrom().position;
+ TfLiteMotionPredictorSample::Point axisTo = mBuffers->axisTo().position;
+
+ if (isDebug()) {
+ ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y);
+ ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y);
+ ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str());
+ ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str());
+ ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str());
+ ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str());
+ ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str());
+ ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str());
+ ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str());
+ ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str());
+ }
+
+ LOG_ALWAYS_FATAL_IF(!mLastEvent);
+ const MotionEvent& event = *mLastEvent;
+ bool hasPredictions = false;
+ std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>();
+ int64_t predictionTime = mBuffers->lastTimestamp();
+ const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
+
+ for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
+ const TfLiteMotionPredictorSample::Point point =
+ convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
+ // TODO(b/266747654): Stop predictions if confidence is < some threshold.
+
+ ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y);
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y);
+ // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
+
+ predictionTime += PREDICTION_INTERVAL_NANOS;
+ if (i == 0) {
+ hasPredictions = true;
+ prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
+ event.getDisplayId(), INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE,
+ event.getActionButton(), event.getFlags(), event.getEdgeFlags(),
+ event.getMetaState(), event.getButtonState(),
+ event.getClassification(), event.getTransform(),
+ event.getXPrecision(), event.getYPrecision(),
+ event.getRawXCursorPosition(), event.getRawYCursorPosition(),
+ event.getRawTransform(), event.getDownTime(), predictionTime,
+ event.getPointerCount(), event.getPointerProperties(), &coords);
+ } else {
+ prediction->addSample(predictionTime, &coords);
+ }
+
+ axisFrom = axisTo;
+ axisTo = point;
+ }
+ // TODO(b/266747511): Interpolate to futureTime?
+ if (!hasPredictions) {
+ return nullptr;
+ }
+ return prediction;
}
bool MotionPredictor::isPredictionAvailable(int32_t /*deviceId*/, int32_t source) {
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 10510d6..691e87c 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -30,6 +30,7 @@
#include <type_traits>
#include <utility>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/mapped_file.h>
#define ATRACE_TAG ATRACE_TAG_INPUT
@@ -60,6 +61,14 @@
constexpr char OUTPUT_PHI[] = "phi";
constexpr char OUTPUT_PRESSURE[] = "pressure";
+std::string getModelPath() {
+#if defined(__ANDROID__)
+ return "/system/etc/motion_predictor_model.fb";
+#else
+ return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
+#endif
+}
+
// A TFLite ErrorReporter that logs to logcat.
class LoggingErrorReporter : public tflite::ErrorReporter {
public:
@@ -206,9 +215,9 @@
mInputOrientation.pushBack(orientation);
}
-std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create(
- const char* modelPath) {
- const int fd = open(modelPath, O_RDONLY);
+std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create() {
+ const std::string modelPath = getModelPath();
+ const int fd = open(modelPath.c_str(), O_RDONLY);
if (fd == -1) {
PLOG(FATAL) << "Could not read model from " << modelPath;
}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 37faf91..42bdf57 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -43,6 +43,13 @@
"-Werror",
"-Wno-unused-parameter",
],
+ sanitize: {
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
shared_libs: [
"libbase",
"libbinder",
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index ce87c86..c61efbf 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -30,13 +30,6 @@
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
-const char MODEL_PATH[] =
-#if defined(__ANDROID__)
- "/system/etc/motion_predictor_model.fb";
-#else
- "motion_predictor_model.fb";
-#endif
-
constexpr int32_t DOWN = AMOTION_EVENT_ACTION_DOWN;
constexpr int32_t MOVE = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t UP = AMOTION_EVENT_ACTION_UP;
@@ -73,83 +66,74 @@
}
TEST(MotionPredictorTest, IsPredictionAvailable) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
ASSERT_TRUE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
TEST(MotionPredictorTest, Offset) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1,
[]() { return true /*enable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
predictor.record(getMotionEvent(MOVE, 0, 2, 35ms));
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(1u, predicted.size());
- ASSERT_GE(predicted[0]->getEventTime(), 41);
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_NE(nullptr, predicted);
+ ASSERT_GE(predicted->getEventTime(), 41);
}
TEST(MotionPredictorTest, FollowsGesture) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
// MOVE without a DOWN is ignored.
predictor.record(getMotionEvent(MOVE, 1, 3, 10ms));
- EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty());
+ EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
predictor.record(getMotionEvent(DOWN, 2, 5, 20ms));
predictor.record(getMotionEvent(MOVE, 2, 7, 30ms));
predictor.record(getMotionEvent(MOVE, 3, 9, 40ms));
- EXPECT_THAT(predictor.predict(50 * NSEC_PER_MSEC), SizeIs(1));
+ EXPECT_NE(nullptr, predictor.predict(50 * NSEC_PER_MSEC));
predictor.record(getMotionEvent(UP, 4, 11, 50ms));
- EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty());
+ EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
}
-TEST(MotionPredictorTest, MultipleDevicesTracked) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+TEST(MotionPredictorTest, MultipleDevicesNotSupported) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
- predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0));
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0)).ok());
- predictor.record(getMotionEvent(DOWN, 100, 300, 0ms, /*deviceId=*/1));
- predictor.record(getMotionEvent(MOVE, 100, 300, 10ms, /*deviceId=*/1));
- predictor.record(getMotionEvent(MOVE, 200, 500, 20ms, /*deviceId=*/1));
- predictor.record(getMotionEvent(MOVE, 300, 700, 30ms, /*deviceId=*/1));
+ ASSERT_FALSE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok());
+ ASSERT_FALSE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok());
+}
- {
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(2u, predicted.size());
+TEST(MotionPredictorTest, IndividualGesturesFromDifferentDevicesAreSupported) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; });
- // Order of the returned vector is not guaranteed.
- std::vector<int32_t> seenDeviceIds;
- for (const auto& prediction : predicted) {
- seenDeviceIds.push_back(prediction->getDeviceId());
- }
- EXPECT_THAT(seenDeviceIds, UnorderedElementsAre(0, 1));
- }
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(UP, 2, 5, 30ms, /*deviceId=*/0)).ok());
- // End the gesture for device 0.
- predictor.record(getMotionEvent(UP, 4, 9, 40ms, /*deviceId=*/0));
- predictor.record(getMotionEvent(MOVE, 400, 900, 40ms, /*deviceId=*/1));
-
- {
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(1u, predicted.size());
- ASSERT_EQ(predicted[0]->getDeviceId(), 1);
- }
+ // Now, send a gesture from a different device. Since we have no active gesture, the new gesture
+ // should be processed correctly.
+ ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok());
+ ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok());
}
TEST(MotionPredictorTest, FlagDisablesPrediction) {
- MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH,
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return false /*disable prediction*/; });
predictor.record(getMotionEvent(DOWN, 0, 1, 30ms));
predictor.record(getMotionEvent(MOVE, 0, 1, 35ms));
- std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC);
- ASSERT_EQ(0u, predicted.size());
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC);
+ ASSERT_EQ(nullptr, predicted);
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS));
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index 454f2aa..6e76ac1 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -21,7 +21,6 @@
#include <iterator>
#include <string>
-#include <android-base/file.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/TfLiteMotionPredictor.h>
@@ -33,14 +32,6 @@
using ::testing::ElementsAre;
using ::testing::FloatNear;
-std::string getModelPath() {
-#if defined(__ANDROID__)
- return "/system/etc/motion_predictor_model.fb";
-#else
- return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
-#endif
-}
-
TEST(TfLiteMotionPredictorTest, BuffersReadiness) {
TfLiteMotionPredictorBuffers buffers(/*inputLength=*/5);
ASSERT_FALSE(buffers.isReady());
@@ -92,8 +83,7 @@
}
TEST(TfLiteMotionPredictorTest, BuffersCopyTo) {
- std::unique_ptr<TfLiteMotionPredictorModel> model =
- TfLiteMotionPredictorModel::create(getModelPath().c_str());
+ std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
TfLiteMotionPredictorBuffers buffers(model->inputLength());
buffers.pushSample(/*timestamp=*/1,
@@ -137,8 +127,7 @@
}
TEST(TfLiteMotionPredictorTest, ModelInputOutputLength) {
- std::unique_ptr<TfLiteMotionPredictorModel> model =
- TfLiteMotionPredictorModel::create(getModelPath().c_str());
+ std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
ASSERT_GT(model->inputLength(), 0u);
const int inputLength = model->inputLength();
@@ -155,8 +144,7 @@
}
TEST(TfLiteMotionPredictorTest, ModelOutput) {
- std::unique_ptr<TfLiteMotionPredictorModel> model =
- TfLiteMotionPredictorModel::create(getModelPath().c_str());
+ std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
TfLiteMotionPredictorBuffers buffers(model->inputLength());
buffers.pushSample(/*timestamp=*/1, {.position = {.x = 100, .y = 200}, .pressure = 0.2});
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 78d1912..0c03ede 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -32,6 +32,7 @@
"jpegr.cpp",
"recoverymapmath.cpp",
"jpegrutils.cpp",
+ "multipictureformat.cpp",
],
shared_libs: [
@@ -40,6 +41,7 @@
"libjpegencoder",
"libjpegdecoder",
"liblog",
+ "libutils",
],
static_libs: ["libskia"],
diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS
index 133af5b..6ace354 100644
--- a/libs/jpegrecoverymap/OWNERS
+++ b/libs/jpegrecoverymap/OWNERS
@@ -1,4 +1,3 @@
arifdikici@google.com
-deakin@google.com
dichenzhang@google.com
kyslov@google.com
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
index 5455ba6..a433e8a 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h
@@ -38,6 +38,14 @@
JPEGR_TF_SRGB = 3,
} jpegr_transfer_function;
+// Target output formats for decoder
+typedef enum {
+ JPEGR_OUTPUT_SDR, // SDR in RGBA_8888 color format
+ JPEGR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear)
+ JPEGR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function)
+ JPEGR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function)
+} jpegr_output_format;
+
struct jpegr_info_struct {
size_t width;
size_t height;
@@ -195,20 +203,15 @@
* @param compressed_jpegr_image compressed JPEGR image
* @param dest destination of the uncompressed JPEGR image
* @param exif destination of the decoded EXIF metadata.
- * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
- * the primary image which is SDR. Setting of request_sdr and input source
- * (HDR or SDR) can be found in the table below:
- * | input source | request_sdr | output of decoding |
- * | HDR | true | SDR |
- * | HDR | false | HDR |
- * | SDR | true | SDR |
- * | SDR | false | SDR |
+ * @param output_format flag for setting output color format. if set to
+ * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
+ * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
* @return NO_ERROR if decoding succeeds, error code if error occurs.
*/
status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
jr_exif_ptr exif = nullptr,
- bool request_sdr = false);
+ jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR);
/*
* Gets Info from JPEGR file without decoding it.
@@ -249,12 +252,16 @@
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_recovery_map uncompressed recovery map
* @param metadata JPEG/R metadata extracted from XMP.
+ * @param output_format flag for setting output color format. if set to
+ * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
+ * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
* @param dest reconstructed HDR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_recovery_map,
jr_metadata_ptr metadata,
+ jpegr_output_format output_format,
jr_uncompressed_ptr dest);
private:
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
index 581806c..4145853 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h
@@ -18,6 +18,7 @@
#define ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
#include <jpegrecoverymap/jpegr.h>
+#include <utils/RefBase.h>
#include <sstream>
#include <stdint.h>
@@ -27,6 +28,26 @@
namespace android::jpegrecoverymap {
struct jpegr_metadata;
+/*
+ * Mutable data structure. Holds information for metadata.
+ */
+class DataStruct : public RefBase {
+private:
+ void* data;
+ int writePos;
+ int length;
+ ~DataStruct();
+
+public:
+ DataStruct(int s);
+ void* getData();
+ int getLength();
+ int getBytesWritten();
+ bool write8(uint8_t value);
+ bool write16(uint16_t value);
+ bool write32(uint32_t value);
+ bool write(const void* src, int size);
+};
/*
* Helper function used for writing data to destination.
@@ -51,12 +72,10 @@
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
/*
- * This method generates XMP metadata.
+ * This method generates XMP metadata for the primary image.
*
* below is an example of the XMP metadata that this function generates where
* secondary_image_length = 1000
- * max_content_boost = 8.0
- * min_content_boost = 0.5
*
* <x:xmpmeta
* xmlns:x="adobe:ns:meta/"
@@ -65,8 +84,7 @@
* xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
* <rdf:Description
* xmlns:Container="http://ns.google.com/photos/1.0/container/"
- * xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
- * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
+ * xmlns:Item="http://ns.google.com/photos/1.0/container/item/">
* <Container:Directory>
* <rdf:Seq>
* <rdf:li>
@@ -78,10 +96,7 @@
* <Container:Item
* Item:Semantic="RecoveryMap"
* Item:Mime="image/jpeg"
- * Item:Length="1000"
- * RecoveryMap:Version="1"
- * RecoveryMap:MaxContentBoost="8.0"
- * RecoveryMap:MinContentBoost="0.5"/>
+ * Item:Length="1000"/>
* </rdf:li>
* </rdf:Seq>
* </Container:Directory>
@@ -90,10 +105,40 @@
* </x:xmpmeta>
*
* @param secondary_image_length length of secondary image
+ * @return XMP metadata in type of string
+ */
+std::string generateXmpForPrimaryImage(int secondary_image_length);
+
+/*
+ * This method generates XMP metadata for the recovery map image.
+ *
+ * below is an example of the XMP metadata that this function generates where
+ * max_content_boost = 8.0
+ * min_content_boost = 0.5
+ *
+ * <x:xmpmeta
+ * xmlns:x="adobe:ns:meta/"
+ * x:xmptk="Adobe XMP Core 5.1.2">
+ * <rdf:RDF
+ * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ * <rdf:Description
+ * xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
+ * hdrgm:Version="1"
+ * hdrgm:GainMapMin="0.5"
+ * hdrgm:GainMapMax="8.5"
+ * hdrgm:Gamma="1"
+ * hdrgm:OffsetSDR="0"
+ * hdrgm:OffsetHDR="0"
+ * hdrgm:HDRCapacityMin="0.5"
+ * hdrgm:HDRCapacityMax="8.5"
+ * hdrgm:BaseRendition="SDR"/>
+ * </rdf:RDF>
+ * </x:xmpmeta>
+ *
* @param metadata JPEG/R metadata to encode as XMP
* @return XMP metadata in type of string
*/
-std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+ std::string generateXmpForSecondaryImage(jpegr_metadata& metadata);
} // namespace android::jpegrecoverymap
#endif //ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
new file mode 100644
index 0000000..7dca916
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
+#define ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
+
+#include <jpegrecoverymap/jpegrutils.h>
+
+namespace android::jpegrecoverymap {
+static constexpr uint32_t EndianSwap32(uint32_t value) {
+ return ((value & 0xFF) << 24) |
+ ((value & 0xFF00) << 8) |
+ ((value & 0xFF0000) >> 8) |
+ (value >> 24);
+}
+static inline uint16_t EndianSwap16(uint16_t value) {
+ return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
+}
+#define USE_BIG_ENDIAN true
+#if USE_BIG_ENDIAN
+ #define Endian_SwapBE32(n) EndianSwap32(n)
+ #define Endian_SwapBE16(n) EndianSwap16(n)
+#else
+ #define Endian_SwapBE32(n) (n)
+ #define Endian_SwapBE16(n) (n)
+#endif
+
+constexpr size_t kNumPictures = 2;
+constexpr size_t kMpEndianSize = 4;
+constexpr uint16_t kTagSerializedCount = 3;
+constexpr uint32_t kTagSize = 12;
+
+constexpr uint16_t kTypeLong = 0x4;
+constexpr uint16_t kTypeUndefined = 0x7;
+
+static constexpr uint8_t kMpfSig[] = {'M', 'P', 'F', '\0'};
+constexpr uint8_t kMpLittleEndian[kMpEndianSize] = {0x49, 0x49, 0x2A, 0x00};
+constexpr uint8_t kMpBigEndian[kMpEndianSize] = {0x4D, 0x4D, 0x00, 0x2A};
+
+constexpr uint16_t kVersionTag = 0xB000;
+constexpr uint16_t kVersionType = kTypeUndefined;
+constexpr uint32_t kVersionCount = 4;
+constexpr size_t kVersionSize = 4;
+constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'};
+
+constexpr uint16_t kNumberOfImagesTag = 0xB001;
+constexpr uint16_t kNumberOfImagesType = kTypeLong;
+constexpr uint32_t kNumberOfImagesCount = 1;
+
+constexpr uint16_t kMPEntryTag = 0xB002;
+constexpr uint16_t kMPEntryType = kTypeUndefined;
+constexpr uint32_t kMPEntrySize = 16;
+
+constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000;
+constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000;
+
+size_t calculateMpfSize();
+sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
+ int secondary_image_size, int secondary_image_offset);
+
+} // namespace android::jpegrecoverymap
+
+#endif //ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index c12cee9..8b5318f 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -115,6 +115,14 @@
return temp /= rhs;
}
+inline uint16_t floatToHalf(float f) {
+ uint32_t x = *((uint32_t*)&f);
+ uint16_t h = ((x >> 16) & 0x8000)
+ | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
+ | ((x >> 13) & 0x03ff);
+ return h;
+}
+
constexpr size_t kRecoveryFactorPrecision = 10;
constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
struct RecoveryLUT {
@@ -392,6 +400,13 @@
*/
uint32_t colorToRgba1010102(Color e_gamma);
+/*
+ * Convert from Color to F16.
+ *
+ * Alpha always set to 1.0.
+ */
+uint64_t colorToRgbaF16(Color e_gamma);
+
} // namespace android::jpegrecoverymap
#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index 828af2d..79b1ae3 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -19,6 +19,7 @@
#include <jpegrecoverymap/jpegdecoderhelper.h>
#include <jpegrecoverymap/recoverymapmath.h>
#include <jpegrecoverymap/jpegrutils.h>
+#include <jpegrecoverymap/multipictureformat.h>
#include <image_io/jpeg/jpeg_marker.h>
#include <image_io/jpeg/jpeg_info.h>
@@ -105,10 +106,10 @@
/* Encode API-0 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif) {
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif) {
if (uncompressed_p010_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -167,11 +168,11 @@
/* Encode API-1 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest,
- int quality,
- jr_exif_ptr exif) {
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
|| dest == nullptr) {
@@ -231,10 +232,10 @@
/* Encode API-2 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_compressed_ptr compressed_jpeg_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
|| compressed_jpeg_image == nullptr
@@ -276,9 +277,9 @@
/* Encode API-3 */
status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_compressed_ptr compressed_jpeg_image,
- jpegr_transfer_function hdr_tf,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| compressed_jpeg_image == nullptr
|| dest == nullptr) {
@@ -327,8 +328,7 @@
return NO_ERROR;
}
-status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
- jr_info_ptr jpegr_info) {
+status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info) {
if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -349,16 +349,16 @@
/* Decode API */
status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
- jr_uncompressed_ptr dest,
- jr_exif_ptr exif,
- bool request_sdr) {
+ jr_uncompressed_ptr dest,
+ jr_exif_ptr exif,
+ jpegr_output_format output_format) {
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
// TODO: fill EXIF data
(void) exif;
- if (request_sdr) {
+ if (output_format == JPEGR_OUTPUT_SDR) {
JpegDecoderHelper jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
true)) {
@@ -399,17 +399,17 @@
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
- if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
- jpeg_decoder.getXMPSize(), &metadata)) {
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(recovery_map_decoder.getXMPPtr()),
+ recovery_map_decoder.getXMPSize(), &metadata)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, output_format, dest));
return NO_ERROR;
}
status_t JpegR::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr dest) {
if (uncompressed_recovery_map == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -493,10 +493,10 @@
}
status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_p010_image,
- jpegr_transfer_function hdr_tf,
- jr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
+ jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_p010_image == nullptr
|| metadata == nullptr
@@ -637,9 +637,10 @@
}
status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
- jr_uncompressed_ptr uncompressed_recovery_map,
- jr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
+ jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
+ jpegr_output_format output_format,
+ jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_recovery_map == nullptr
|| metadata == nullptr
@@ -654,18 +655,12 @@
JobQueue jobQueue;
std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
- metadata, dest, &jobQueue, &idwTable,
+ metadata, dest, &jobQueue, &idwTable, output_format,
&recoveryLUT]() -> void {
const float hdr_ratio = metadata->maxContentBoost;
size_t width = uncompressed_yuv_420_image->width;
size_t height = uncompressed_yuv_420_image->height;
-#if USE_HLG_OETF_LUT
- ColorTransformFn hdrOetf = hlgOetfLUT;
-#else
- ColorTransformFn hdrOetf = hlgOetf;
-#endif
-
size_t rowStart, rowEnd;
while (jobQueue.dequeueJob(rowStart, rowEnd)) {
for (size_t y = rowStart; y < rowEnd; ++y) {
@@ -693,11 +688,44 @@
#else
Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);
#endif
- Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
- uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
-
+ rgb_hdr = rgb_hdr / metadata->maxContentBoost;
size_t pixel_idx = x + y * width;
- reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
+
+ switch (output_format) {
+ case JPEGR_OUTPUT_HDR_LINEAR:
+ {
+ uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
+ reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
+ break;
+ }
+ case JPEGR_OUTPUT_HDR_HLG:
+ {
+#if USE_HLG_OETF_LUT
+ ColorTransformFn hdrOetf = hlgOetfLUT;
+#else
+ ColorTransformFn hdrOetf = hlgOetf;
+#endif
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
+ break;
+ }
+ case JPEGR_OUTPUT_HDR_PQ:
+ {
+#if USE_HLG_OETF_LUT
+ ColorTransformFn hdrOetf = pqOetfLUT;
+#else
+ ColorTransformFn hdrOetf = pqOetf;
+#endif
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
+ break;
+ }
+ default:
+ {}
+ // Should be impossible to hit after input validation.
+ }
}
}
}
@@ -721,8 +749,8 @@
}
status_t JpegR::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr primary_image,
- jr_compressed_ptr recovery_map) {
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map) {
if (compressed_jpegr_image == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -771,7 +799,7 @@
status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr dest) {
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -790,11 +818,22 @@
// (Required, XMP package) APP1 (ff e1)
// 2 bytes of length (2 + 29 + length of xmp package)
// name space ("http://ns.adobe.com/xap/1.0/\0")
-// xmp
+// XMP
+//
+// (Required, MPF package) APP2 (ff e2)
+// 2 bytes of length
+// MPF
//
// (Required) primary image (without the first two bytes (SOI), may have other packages)
//
-// (Required) secondary image (the recovery map)
+// SOI (ff d8)
+//
+// (Required, XMP package) APP1 (ff e1)
+// 2 bytes of length (2 + 29 + length of xmp package)
+// name space ("http://ns.adobe.com/xap/1.0/\0")
+// XMP
+//
+// (Required) secondary image (the recovery map, without the first two bytes (SOI))
//
// Metadata versions we are using:
// ECMA TR-98 for JFIF marker
@@ -802,10 +841,10 @@
// Adobe XMP spec part 3 for XMP marker
// ICC v4.3 spec for ICC
status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
- jr_compressed_ptr compressed_recovery_map,
- jr_exif_ptr exif,
- jr_metadata_ptr metadata,
- jr_compressed_ptr dest) {
+ jr_compressed_ptr compressed_recovery_map,
+ jr_exif_ptr exif,
+ jr_metadata_ptr metadata,
+ jr_compressed_ptr dest) {
if (compressed_jpeg_image == nullptr
|| compressed_recovery_map == nullptr
|| metadata == nullptr
@@ -813,8 +852,25 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- int pos = 0;
+ const string nameSpace = "http://ns.adobe.com/xap/1.0/";
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ // calculate secondary image length first, because the length will be written into the primary
+ // image xmp
+ const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
+ const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */
+ + nameSpaceLength /* 29 bytes length of name space including \0 */
+ + xmp_secondary.size(); /* length of xmp packet */
+ const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */
+ + xmp_secondary_length
+ + compressed_recovery_map->length;
+ // primary image
+ const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size);
+ // same as primary
+ const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size();
+
+ int pos = 0;
+ // Begin primary image
// Write SOI
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
@@ -833,13 +889,7 @@
// Prepare and write XMP
{
- const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
- const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
- // 2 bytes: representing the length of the package
- // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
- // x bytes: length of xmp packet
- const int length = 2 + nameSpaceLength + xmp.size();
+ const int length = xmp_primary_length;
const uint8_t lengthH = ((length >> 8) & 0xff);
const uint8_t lengthL = (length & 0xff);
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
@@ -847,15 +897,57 @@
JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
- JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
+ JPEGR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
+ }
+
+ // Prepare and write MPF
+ {
+ const int length = 2 + calculateMpfSize();
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ int primary_image_size = pos + length + compressed_jpeg_image->length;
+ // between APP2 + package size + signature
+ // ff e2 00 58 4d 50 46 00
+ // 2 + 2 + 4 = 8 (bytes)
+ // and ff d8 sign of the secondary image
+ int secondary_image_offset = primary_image_size - pos - 8;
+ sp<DataStruct> mpf = generateMpf(primary_image_size,
+ 0, /* primary_image_offset */
+ secondary_image_size,
+ secondary_image_offset);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
}
// Write primary image
JPEGR_CHECK(Write(dest,
(uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
+ // Finish primary image
+
+ // Begin secondary image (recovery map)
+ // Write SOI
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
+
+ // Prepare and write XMP
+ {
+ const int length = xmp_secondary_length;
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
+ JPEGR_CHECK(Write(dest, (void*)xmp_secondary.c_str(), xmp_secondary.size(), pos));
+ }
// Write secondary image
- JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
+ JPEGR_CHECK(Write(dest,
+ (uint8_t*)compressed_recovery_map->data + 2, compressed_recovery_map->length - 2, pos));
// Set back length
dest->length = pos;
@@ -864,8 +956,7 @@
return NO_ERROR;
}
-status_t JpegR::toneMap(jr_uncompressed_ptr src,
- jr_uncompressed_ptr dest) {
+status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
if (src == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
diff --git a/libs/jpegrecoverymap/jpegrutils.cpp b/libs/jpegrecoverymap/jpegrutils.cpp
index 49526c8..38b78ad 100644
--- a/libs/jpegrecoverymap/jpegrutils.cpp
+++ b/libs/jpegrecoverymap/jpegrutils.cpp
@@ -15,18 +15,19 @@
*/
#include <jpegrecoverymap/jpegrutils.h>
+#include <utils/Log.h>
#include <image_io/xml/xml_reader.h>
#include <image_io/xml/xml_writer.h>
#include <image_io/base/message_handler.h>
#include <image_io/xml/xml_element_rules.h>
#include <image_io/xml/xml_handler.h>
#include <image_io/xml/xml_rule.h>
+#include <cmath>
using namespace photos_editing_formats::image_io;
using namespace std;
namespace android::jpegrecoverymap {
-
/*
* Helper function used for generating XMP metadata.
*
@@ -34,12 +35,62 @@
* @param suffix The suffix part of the name.
* @return A name of the form "prefix:suffix".
*/
-string Name(const string &prefix, const string &suffix) {
+static inline string Name(const string &prefix, const string &suffix) {
std::stringstream ss;
ss << prefix << ":" << suffix;
return ss.str();
}
+DataStruct::DataStruct(int s) {
+ data = malloc(s);
+ length = s;
+ memset(data, 0, s);
+ writePos = 0;
+}
+
+DataStruct::~DataStruct() {
+ if (data != nullptr) {
+ free(data);
+ }
+}
+
+void* DataStruct::getData() {
+ return data;
+}
+
+int DataStruct::getLength() {
+ return length;
+}
+
+int DataStruct::getBytesWritten() {
+ return writePos;
+}
+
+bool DataStruct::write8(uint8_t value) {
+ uint8_t v = value;
+ return write(&v, 1);
+}
+
+bool DataStruct::write16(uint16_t value) {
+ uint16_t v = value;
+ return write(&v, 2);
+}
+bool DataStruct::write32(uint32_t value) {
+ uint32_t v = value;
+ return write(&v, 4);
+}
+
+bool DataStruct::write(const void* src, int size) {
+ if (writePos + size > length) {
+ ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d",
+ writePos, size, length);
+ return false;
+ }
+ memcpy((uint8_t*) data + writePos, src, size);
+ writePos += size;
+ return true;
+}
+
/*
* Helper function used for writing data to destination.
*/
@@ -58,7 +109,7 @@
public:
XMPXmlHandler() : XmlHandler() {
- gContainerItemState = NotStrarted;
+ state = NotStrarted;
}
enum ParseState {
@@ -70,11 +121,11 @@
virtual DataMatchResult StartElement(const XmlTokenContext& context) {
string val;
if (context.BuildTokenValue(&val)) {
- if (!val.compare(gContainerItemName)) {
- gContainerItemState = Started;
+ if (!val.compare(containerName)) {
+ state = Started;
} else {
- if (gContainerItemState != Done) {
- gContainerItemState = NotStrarted;
+ if (state != Done) {
+ state = NotStrarted;
}
}
}
@@ -82,8 +133,8 @@
}
virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
- if (gContainerItemState == Started) {
- gContainerItemState = Done;
+ if (state == Started) {
+ state = Done;
lastAttributeName = "";
}
return context.GetResult();
@@ -91,7 +142,7 @@
virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
string val;
- if (gContainerItemState == Started) {
+ if (state == Started) {
if (context.BuildTokenValue(&val)) {
if (!val.compare(maxContentBoostAttrName)) {
lastAttributeName = maxContentBoostAttrName;
@@ -107,7 +158,7 @@
virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
string val;
- if (gContainerItemState == Started) {
+ if (state == Started) {
if (context.BuildTokenValue(&val, true)) {
if (!lastAttributeName.compare(maxContentBoostAttrName)) {
maxContentBoostStr = val;
@@ -120,11 +171,11 @@
}
bool getMaxContentBoost(float* max_content_boost) {
- if (gContainerItemState == Done) {
+ if (state == Done) {
stringstream ss(maxContentBoostStr);
float val;
if (ss >> val) {
- *max_content_boost = val;
+ *max_content_boost = exp2(val);
return true;
} else {
return false;
@@ -135,11 +186,11 @@
}
bool getMinContentBoost(float* min_content_boost) {
- if (gContainerItemState == Done) {
+ if (state == Done) {
stringstream ss(minContentBoostStr);
float val;
if (ss >> val) {
- *min_content_boost = val;
+ *min_content_boost = exp2(val);
return true;
} else {
return false;
@@ -150,13 +201,13 @@
}
private:
- static const string gContainerItemName;
+ static const string containerName;
static const string maxContentBoostAttrName;
string maxContentBoostStr;
static const string minContentBoostAttrName;
string minContentBoostStr;
string lastAttributeName;
- ParseState gContainerItemState;
+ ParseState state;
};
// GContainer XMP constants - URI and namespace prefix
@@ -168,8 +219,7 @@
const string kConItem = Name(kContainerPrefix, "Item");
// GContainer XMP constants - names for XMP handlers
-const string XMPXmlHandler::gContainerItemName = kConItem;
-
+const string XMPXmlHandler::containerName = "rdf:Description";
// Item XMP constants - URI and namespace prefix
const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
const string kItemPrefix = "Item";
@@ -185,17 +235,23 @@
const string kMimeImageJpeg = "image/jpeg";
// RecoveryMap XMP constants - URI and namespace prefix
-const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
-const string kRecoveryMapPrefix = "RecoveryMap";
+const string kRecoveryMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
+const string kRecoveryMapPrefix = "hdrgm";
// RecoveryMap XMP constants - element and attribute names
-const string kMapMaxContentBoost = Name(kRecoveryMapPrefix, "MaxContentBoost");
-const string kMapMinContentBoost = Name(kRecoveryMapPrefix, "MinContentBoost");
const string kMapVersion = Name(kRecoveryMapPrefix, "Version");
+const string kMapGainMapMin = Name(kRecoveryMapPrefix, "GainMapMin");
+const string kMapGainMapMax = Name(kRecoveryMapPrefix, "GainMapMax");
+const string kMapGamma = Name(kRecoveryMapPrefix, "Gamma");
+const string kMapOffsetSdr = Name(kRecoveryMapPrefix, "OffsetSDR");
+const string kMapOffsetHdr = Name(kRecoveryMapPrefix, "OffsetHDR");
+const string kMapHDRCapacityMin = Name(kRecoveryMapPrefix, "HDRCapacityMin");
+const string kMapHDRCapacityMax = Name(kRecoveryMapPrefix, "HDRCapacityMax");
+const string kMapBaseRendition = Name(kRecoveryMapPrefix, "BaseRendition");
// RecoveryMap XMP constants - names for XMP handlers
-const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost;
-const string XMPXmlHandler::minContentBoostAttrName = kMapMinContentBoost;
+const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
+const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -243,7 +299,7 @@
return true;
}
-string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+string generateXmpForPrimaryImage(int secondary_image_length) {
const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
const vector<string> kLiItem({string("rdf:li"), kConItem});
@@ -257,7 +313,6 @@
writer.StartWritingElement("rdf:Description");
writer.WriteXmlns(kContainerPrefix, kContainerUri);
writer.WriteXmlns(kItemPrefix, kItemUri);
- writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
writer.StartWritingElements(kConDirSeq);
size_t item_depth = writer.StartWritingElements(kLiItem);
writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
@@ -267,9 +322,33 @@
writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap);
writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
+string generateXmpForSecondaryImage(jpegr_metadata& metadata) {
+ const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kConItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
- writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
- writer.WriteAttributeNameAndValue(kMapMinContentBoost, metadata.minContentBoost);
+ writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
+ writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
+ writer.WriteAttributeNameAndValue(kMapGamma, "1");
+ writer.WriteAttributeNameAndValue(kMapOffsetSdr, "0");
+ writer.WriteAttributeNameAndValue(kMapOffsetHdr, "0");
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, "0");
+ writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, "2.3");
+ writer.WriteAttributeNameAndValue(kMapBaseRendition, "SDR");
writer.FinishWriting();
return ss.str();
diff --git a/libs/jpegrecoverymap/multipictureformat.cpp b/libs/jpegrecoverymap/multipictureformat.cpp
new file mode 100644
index 0000000..a219aef
--- /dev/null
+++ b/libs/jpegrecoverymap/multipictureformat.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+#include <jpegrecoverymap/multipictureformat.h>
+#include <jpegrecoverymap/jpegrutils.h>
+
+namespace android::jpegrecoverymap {
+size_t calculateMpfSize() {
+ return sizeof(kMpfSig) + // Signature
+ kMpEndianSize + // Endianness
+ sizeof(uint32_t) + // Index IFD Offset
+ sizeof(uint16_t) + // Tag count
+ kTagSerializedCount * kTagSize + // 3 tags at 12 bytes each
+ sizeof(uint32_t) + // Attribute IFD offset
+ kNumPictures * kMPEntrySize; // MP Entries for each image
+}
+
+sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
+ int secondary_image_size, int secondary_image_offset) {
+ size_t mpf_size = calculateMpfSize();
+ sp<DataStruct> dataStruct = new DataStruct(mpf_size);
+
+ dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
+#if USE_BIG_ENDIAN
+ dataStruct->write(static_cast<const void*>(kMpBigEndian), kMpEndianSize);
+#else
+ dataStruct->write(static_cast<const void*>(kMpLittleEndian), kMpEndianSize);
+#endif
+
+ // Set the Index IFD offset be the position after the endianness value and this offset.
+ constexpr uint32_t indexIfdOffset =
+ static_cast<uint16_t>(kMpEndianSize + sizeof(kMpfSig));
+ dataStruct->write32(Endian_SwapBE32(indexIfdOffset));
+
+ // We will write 3 tags (version, number of images, MP entries).
+ dataStruct->write16(Endian_SwapBE16(kTagSerializedCount));
+
+ // Write the version tag.
+ dataStruct->write16(Endian_SwapBE16(kVersionTag));
+ dataStruct->write16(Endian_SwapBE16(kVersionType));
+ dataStruct->write32(Endian_SwapBE32(kVersionCount));
+ dataStruct->write(kVersionExpected, kVersionSize);
+
+ // Write the number of images.
+ dataStruct->write16(Endian_SwapBE16(kNumberOfImagesTag));
+ dataStruct->write16(Endian_SwapBE16(kNumberOfImagesType));
+ dataStruct->write32(Endian_SwapBE32(kNumberOfImagesCount));
+ dataStruct->write32(Endian_SwapBE32(kNumPictures));
+
+ // Write the MP entries.
+ dataStruct->write16(Endian_SwapBE16(kMPEntryTag));
+ dataStruct->write16(Endian_SwapBE16(kMPEntryType));
+ dataStruct->write32(Endian_SwapBE32(kMPEntrySize * kNumPictures));
+ const uint32_t mpEntryOffset =
+ static_cast<uint32_t>(dataStruct->getBytesWritten() - // The bytes written so far
+ sizeof(kMpfSig) + // Excluding the MPF signature
+ sizeof(uint32_t) + // The 4 bytes for this offset
+ sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
+ dataStruct->write32(Endian_SwapBE32(mpEntryOffset));
+
+ // Write the attribute IFD offset (zero because we don't write it).
+ dataStruct->write32(0);
+
+ // Write the MP entries for primary image
+ dataStruct->write32(
+ Endian_SwapBE32(kMPEntryAttributeFormatJpeg | kMPEntryAttributeTypePrimary));
+ dataStruct->write32(Endian_SwapBE32(primary_image_size));
+ dataStruct->write32(Endian_SwapBE32(primary_image_offset));
+ dataStruct->write16(0);
+ dataStruct->write16(0);
+
+ // Write the MP entries for secondary image
+ dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg));
+ dataStruct->write32(Endian_SwapBE32(secondary_image_size));
+ dataStruct->write32(Endian_SwapBE32(secondary_image_offset));
+ dataStruct->write16(0);
+ dataStruct->write16(0);
+
+ return dataStruct;
+}
+
+} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 7812e18..20c32ed 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -631,4 +631,11 @@
| (0x3 << 30); // Set alpha to 1.0
}
+uint64_t colorToRgbaF16(Color e_gamma) {
+ return (uint64_t) floatToHalf(e_gamma.r)
+ | (((uint64_t) floatToHalf(e_gamma.g)) << 16)
+ | (((uint64_t) floatToHalf(e_gamma.b)) << 32)
+ | (((uint64_t) floatToHalf(1.0f)) << 48);
+}
+
} // namespace android::jpegrecoverymap
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 5a4edb2..61b3db9 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -40,6 +40,7 @@
"libjpegencoder",
"libjpegrecoverymap",
"libskia",
+ "libutils",
],
}
diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index 7a3133d..0a7d20a 100644
--- a/libs/jpegrecoverymap/tests/jpegr_test.cpp
+++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp
@@ -152,7 +152,7 @@
timerStart(&applyRecMapTime);
for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest));
+ ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG, dest));
}
timerStop(&applyRecMapTime);
@@ -170,18 +170,17 @@
jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
nullptr);
jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
- jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr, false);
+ jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr);
}
TEST_F(JpegRTest, writeXmpThenRead) {
jpegr_metadata metadata_expected;
metadata_expected.maxContentBoost = 1.25;
metadata_expected.minContentBoost = 0.75;
- int length_expected = 1000;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
- std::string xmp = generateXmp(1000, metadata_expected);
+ std::string xmp = generateXmpForSecondaryImage(metadata_expected);
std::vector<uint8_t> xmpData;
xmpData.reserve(nameSpaceLength + xmp.size());
@@ -220,7 +219,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -229,7 +228,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -237,7 +236,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -281,7 +280,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_input.jpgr";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -290,7 +289,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -298,7 +297,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -346,7 +345,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_jpeg_input.jpgr";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -355,7 +354,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -363,7 +362,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -427,7 +426,7 @@
}
if (SAVE_ENCODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::string filePath = "/sdcard/Documents/encoded_from_p010_jpeg_input.jpgr";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -436,7 +435,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -444,7 +443,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb";
std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
if (!imageFile.is_open()) {
ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b7b2926..5306529 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -24,14 +24,49 @@
#include <private/android/AHardwareBufferHelpers.h>
+#include <android/binder_libbinder.h>
+#include <dlfcn.h>
#include <log/log.h>
#include <ui/GraphicBuffer.h>
-#include <gui/Surface.h>
-#include <gui/view/Surface.h>
-#include <android/binder_libbinder.h>
using namespace android;
+#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__)
+#error libnativewindow can only be built for system
+#endif
+
+using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window,
+ Parcel* _Nonnull parcel);
+
+using android_view_Surface_readFromParcel =
+ status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow);
+
+struct SurfaceParcelables {
+ android_view_Surface_writeToParcel write = nullptr;
+ android_view_Surface_readFromParcel read = nullptr;
+};
+
+const SurfaceParcelables* getSurfaceParcelFunctions() {
+ static SurfaceParcelables funcs = []() -> SurfaceParcelables {
+ SurfaceParcelables ret;
+ void* dl = dlopen("libgui.so", RTLD_NOW);
+ LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so");
+ ret.write =
+ (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.write,
+ "libgui.so missing android_view_Surface_writeToParcel; "
+ "loaded wrong libgui?");
+ ret.read =
+ (android_view_Surface_readFromParcel)dlsym(dl,
+ "android_view_Surface_readFromParcel");
+ LOG_ALWAYS_FATAL_IF(!ret.read,
+ "libgui.so missing android_view_Surface_readFromParcel; "
+ "loaded wrong libgui?");
+ return ret;
+ }();
+ return &funcs;
+}
+
static int32_t query(ANativeWindow* window, int what) {
int value;
int res = window->query(window, what, &value);
@@ -64,13 +99,6 @@
return false;
}
}
-static sp<IGraphicBufferProducer> IGraphicBufferProducer_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getIGraphicBufferProducer(window);
-}
-
-static sp<IBinder> SurfaceControlHandle_from_ANativeWindow(ANativeWindow* window) {
- return Surface::getSurfaceControlHandle(window);
-}
/**************************************************************************************************
* NDK
@@ -355,38 +383,24 @@
binder_status_t ANativeWindow_readFromParcel(
const AParcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) {
- const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
-
- // Use a android::view::Surface to unparcel the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- status_t ret = shimSurface->readFromParcel(nativeParcel);
- if (ret != OK) {
- ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__);
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->read == nullptr) {
+ ALOGE("Failed to load Surface_readFromParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- sp<Surface> surface = sp<Surface>::make(
- shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle);
- ANativeWindow* anw = surface.get();
- ANativeWindow_acquire(anw);
- *outWindow = anw;
- return STATUS_OK;
+ const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
+ return funcs->read(nativeParcel, outWindow);
}
binder_status_t ANativeWindow_writeToParcel(
ANativeWindow* _Nonnull window, AParcel* _Nonnull parcel) {
- int value;
- int err = (*window->query)(window, NATIVE_WINDOW_CONCRETE_TYPE, &value);
- if (err != OK || value != NATIVE_WINDOW_SURFACE) {
- ALOGE("Error: ANativeWindow is not backed by Surface");
- return STATUS_BAD_VALUE;
+ auto funcs = getSurfaceParcelFunctions();
+ if (funcs->write == nullptr) {
+ ALOGE("Failed to load Surface_writeToParcel implementation");
+ return STATUS_FAILED_TRANSACTION;
}
- // Use a android::view::Surface to parcelize the window
- std::shared_ptr<android::view::Surface> shimSurface = std::shared_ptr<android::view::Surface>();
- shimSurface->graphicBufferProducer = IGraphicBufferProducer_from_ANativeWindow(window);
- shimSurface->surfaceControlHandle = SurfaceControlHandle_from_ANativeWindow(window);
-
Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel);
- return shimSurface->writeToParcel(nativeParcel);
+ return funcs->write(window, nativeParcel);
}
/**************************************************************************************************
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index 84771c0..210dca5 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -39,7 +39,7 @@
}
ExternalTexture::~ExternalTexture() {
- mRenderEngine.unmapExternalTextureBuffer(mBuffer);
+ mRenderEngine.unmapExternalTextureBuffer(std::move(mBuffer));
}
} // namespace android::renderengine::impl
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 13f766c..0d7df10 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -800,7 +800,7 @@
return NO_ERROR;
}
-void GLESRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void GLESRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
mImageManager->releaseAsync(buffer->getId(), nullptr);
}
@@ -1262,7 +1262,7 @@
// Do not cache protected EGLImage, protected memory is limited.
if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
- unmapExternalTextureBuffer(gBuf);
+ unmapExternalTextureBuffer(std::move(gBuf));
}
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 1b34921..402ff52 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -101,7 +101,7 @@
size_t getMaxViewportDims() const override;
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
EXCLUDES(mRenderingMutex);
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) EXCLUDES(mRenderingMutex);
bool canSkipPostRenderCleanup() const override;
void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display,
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 39621cd..0d910c9 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -231,7 +231,7 @@
// asynchronously, but the caller can expect that map/unmap calls are performed in a manner
// that's conflict serializable, i.e. unmap a buffer should never occur before binding the
// buffer if the caller called mapExternalTextureBuffer before calling unmap.
- virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
+ virtual void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) = 0;
// A thread safe query to determine if any post rendering cleanup is necessary. Returning true
// is a signal that calling the postRenderCleanup method would be a no-op and that callers can
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index e3ce85d..d3035e2 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -63,7 +63,7 @@
protected:
// mock renderengine still needs to implement these, but callers should never need to call them.
void mapExternalTextureBuffer(const sp<GraphicBuffer>&, bool) {}
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>&) {}
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&&) {}
};
} // namespace mock
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 413811e..5965d41 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -423,7 +423,7 @@
}
}
-void SkiaRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId());
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 1973c7d..dd6646b 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -130,7 +130,7 @@
private:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) override final;
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override final;
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override final;
bool canSkipPostRenderCleanup() const override final;
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 8d99f3d..936e316 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -444,8 +444,11 @@
ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
VkQueue graphicsQueue;
- VK_GET_DEV_PROC(device, GetDeviceQueue);
- vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue);
+ VK_GET_DEV_PROC(device, GetDeviceQueue2);
+ const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr,
+ deviceQueueCreateFlags,
+ (uint32_t)graphicsQueueIndex, 0};
+ vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue);
VK_GET_DEV_PROC(device, DeviceWaitIdle);
VK_GET_DEV_PROC(device, DestroyDevice);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 8aa41b3..6a1561a 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -230,16 +230,17 @@
mCondition.notify_one();
}
-void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
ATRACE_CALL();
// This function is designed so it can run asynchronously, so we do not need to wait
// for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
- instance.unmapExternalTextureBuffer(buffer);
- });
+ mFunctionCalls.push(
+ [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable {
+ ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ instance.unmapExternalTextureBuffer(std::move(buffer));
+ });
}
mCondition.notify_one();
}
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 168e2d2..6eb108e 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -69,7 +69,7 @@
protected:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
- void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+ void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override;
bool canSkipPostRenderCleanup() const override;
void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display,
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index e2aac8c..019d6cb 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -139,10 +139,12 @@
}
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
- uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) {
+ int deviceId, uint32_t size, int32_t type, int32_t format,
+ const native_handle_t *resource) {
Parcel data, reply;
data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
data.writeString16(opPackageName);
+ data.writeInt32(deviceId);
data.writeUint32(size);
data.writeInt32(type);
data.writeInt32(format);
@@ -237,6 +239,7 @@
case CREATE_SENSOR_DIRECT_CONNECTION: {
CHECK_INTERFACE(ISensorServer, data, reply);
const String16& opPackageName = data.readString16();
+ const int deviceId = data.readInt32();
uint32_t size = data.readUint32();
int32_t type = data.readInt32();
int32_t format = data.readInt32();
@@ -246,8 +249,8 @@
return BAD_VALUE;
}
native_handle_set_fdsan_tag(resource);
- sp<ISensorEventConnection> ch =
- createSensorDirectConnection(opPackageName, size, type, format, resource);
+ sp<ISensorEventConnection> ch = createSensorDirectConnection(
+ opPackageName, deviceId, size, type, format, resource);
native_handle_close_with_tag(resource);
native_handle_delete(resource);
reply->writeStrongBinder(IInterface::asBinder(ch));
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 44a208d..ba190e0 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -315,6 +315,12 @@
int SensorManager::createDirectChannel(
size_t size, int channelType, const native_handle_t *resourceHandle) {
+ static constexpr int DEFAULT_DEVICE_ID = 0;
+ return createDirectChannel(DEFAULT_DEVICE_ID, size, channelType, resourceHandle);
+}
+
+int SensorManager::createDirectChannel(
+ int deviceId, size_t size, int channelType, const native_handle_t *resourceHandle) {
Mutex::Autolock _l(mLock);
if (assertStateLocked() != NO_ERROR) {
return NO_INIT;
@@ -327,7 +333,7 @@
}
sp<ISensorEventConnection> conn =
- mSensorServer->createSensorDirectConnection(mOpPackageName,
+ mSensorServer->createSensorDirectConnection(mOpPackageName, deviceId,
static_cast<uint32_t>(size),
static_cast<int32_t>(channelType),
SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle);
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index 3295196..5815728 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -50,7 +50,8 @@
virtual int32_t isDataInjectionEnabled() = 0;
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
- uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0;
+ int deviceId, uint32_t size, int32_t type, int32_t format,
+ const native_handle_t *resource) = 0;
virtual int setOperationParameter(
int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index c31f648..bb44cb8 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -66,6 +66,8 @@
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
bool isDataInjectionEnabled();
int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData);
+ int createDirectChannel(
+ int deviceId, size_t size, int channelType, const native_handle_t *channelData);
void destroyDirectChannel(int channelNativeHandle);
int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel);
int setOperationParameter(int handle, int type, const Vector<float> &floats, const Vector<int32_t> &ints);
diff --git a/opengl/OWNERS b/opengl/OWNERS
index 379f763..3d60a1d 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -1,11 +1,6 @@
-abdolrashidi@google.com
-cclao@google.com
chrisforbes@google.com
cnorthrop@google.com
ianelliott@google.com
jessehall@google.com
-lfy@google.com
lpy@google.com
-romanl@google.com
vantablack@google.com
-yuxinhu@google.com
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index b00ee33..3dc93ee 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -110,38 +110,6 @@
}
}
- // Check the device config to decide whether multifile should be used
- if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) {
- mMultifileMode = true;
- ALOGV("Using multifile EGL blobcache");
- }
-
- // Allow forcing the mode for debug purposes
- std::string mode = base::GetProperty("debug.egl.blobcache.multifile", "");
- if (mode == "true") {
- ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
- mMultifileMode = true;
- } else if (mode == "false") {
- ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
- mMultifileMode = false;
- }
-
- if (mMultifileMode) {
- mCacheByteLimit = static_cast<size_t>(
- base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit",
- kMultifileCacheByteLimit));
-
- // Check for a debug value
- int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1);
- if (debugCacheSize >= 0) {
- ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit",
- mCacheByteLimit, debugCacheSize);
- mCacheByteLimit = debugCacheSize;
- }
-
- ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit);
- }
-
mInitialized = true;
}
@@ -167,6 +135,8 @@
return;
}
+ updateMode();
+
if (mInitialized) {
if (mMultifileMode) {
MultifileBlobCache* mbc = getMultifileBlobCacheLocked();
@@ -200,6 +170,8 @@
return 0;
}
+ updateMode();
+
if (mInitialized) {
if (mMultifileMode) {
MultifileBlobCache* mbc = getMultifileBlobCacheLocked();
@@ -247,6 +219,51 @@
return 0;
}
+void egl_cache_t::updateMode() {
+ // We don't set the mode in the constructor because these checks have
+ // a non-trivial cost, and not all processes that instantiate egl_cache_t
+ // will use it.
+
+ // If we've already set the mode, skip these checks
+ static bool checked = false;
+ if (checked) {
+ return;
+ }
+ checked = true;
+
+ // Check the device config to decide whether multifile should be used
+ if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) {
+ mMultifileMode = true;
+ ALOGV("Using multifile EGL blobcache");
+ }
+
+ // Allow forcing the mode for debug purposes
+ std::string mode = base::GetProperty("debug.egl.blobcache.multifile", "");
+ if (mode == "true") {
+ ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
+ mMultifileMode = true;
+ } else if (mode == "false") {
+ ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str());
+ mMultifileMode = false;
+ }
+
+ if (mMultifileMode) {
+ mCacheByteLimit = static_cast<size_t>(
+ base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit",
+ kMultifileCacheByteLimit));
+
+ // Check for a debug value
+ int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1);
+ if (debugCacheSize >= 0) {
+ ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit",
+ mCacheByteLimit, debugCacheSize);
+ mCacheByteLimit = debugCacheSize;
+ }
+
+ ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit);
+ }
+}
+
BlobCache* egl_cache_t::getBlobCacheLocked() {
if (mBlobCache == nullptr) {
mBlobCache.reset(new FileBlobCache(kMaxMonolithicKeySize, kMaxMonolithicValueSize,
diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h
index 1399368..ae6d381 100644
--- a/opengl/libs/EGL/egl_cache.h
+++ b/opengl/libs/EGL/egl_cache.h
@@ -88,6 +88,9 @@
egl_cache_t(const egl_cache_t&); // not implemented
void operator=(const egl_cache_t&); // not implemented
+ // Check system properties to determine which blobcache mode should be used
+ void updateMode();
+
// getBlobCacheLocked returns the BlobCache object being used to store the
// key/value blob pairs. If the BlobCache object has not yet been created,
// this will do so, loading the serialized cache contents from disk if
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ab6a116..78cdd0d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4036,18 +4036,20 @@
/**
* If one of the meta shortcuts is detected, process them here:
- * Meta + Backspace -> generate BACK
- * Meta + Enter -> generate HOME
- * This will potentially overwrite keyCode and metaState.
+ * Meta + Backspace; Meta + Grave; Meta + Left arrow -> generate BACK
+ * Most System shortcuts are handled in PhoneWindowManager.java except 'Back' shortcuts. Unlike
+ * Back, other shortcuts DO NOT need to be sent to applications and are fully handled by the system.
+ * But for Back key and Back shortcuts, we need to send KEYCODE_BACK to applications which can
+ * potentially handle the back key presses.
+ * Note: We don't send any Meta based KeyEvents to applications, so we need to convert to a KeyEvent
+ * where meta modifier is off before sending. Currently only use case is 'Back'.
*/
void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action,
int32_t& keyCode, int32_t& metaState) {
if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) {
int32_t newKeyCode = AKEYCODE_UNKNOWN;
- if (keyCode == AKEYCODE_DEL) {
+ if (keyCode == AKEYCODE_DEL || keyCode == AKEYCODE_GRAVE || keyCode == AKEYCODE_DPAD_LEFT) {
newKeyCode = AKEYCODE_BACK;
- } else if (keyCode == AKEYCODE_ENTER) {
- newKeyCode = AKEYCODE_HOME;
}
if (newKeyCode != AKEYCODE_UNKNOWN) {
std::scoped_lock _l(mLock);
@@ -4072,14 +4074,15 @@
}
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
- "policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
- args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
- args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
- args->downTime);
- }
+ ALOGD_IF(debugInboundEventDetails(),
+ "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
+ ", deviceId=%d, source=%s, displayId=%" PRId32
+ "policyFlags=0x%x, action=%s, flags=0x%x, keyCode=%s, scanCode=0x%x, metaState=0x%x, "
+ "downTime=%" PRId64,
+ args->id, args->eventTime, args->deviceId,
+ inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags,
+ KeyEvent::actionToString(args->action), args->flags, KeyEvent::getLabel(args->keyCode),
+ args->scanCode, args->metaState, args->downTime);
if (!validateKeyEvent(args->action)) {
return;
}
@@ -4151,22 +4154,21 @@
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
if (debugInboundEventDetails()) {
- ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+ ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
"displayId=%" PRId32 ", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
"yCursorPosition=%f, downTime=%" PRId64,
- args->id, args->eventTime, args->deviceId, args->source, args->displayId,
- args->policyFlags, MotionEvent::actionToString(args->action).c_str(),
- args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags,
- args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime);
+ args->id, args->eventTime, args->deviceId,
+ inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags,
+ MotionEvent::actionToString(args->action).c_str(), args->actionButton, args->flags,
+ args->metaState, args->buttonState, args->edgeFlags, args->xPrecision,
+ args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime);
for (uint32_t i = 0; i < args->pointerCount; i++) {
- ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+ ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
+ i, args->pointerProperties[i].id,
+ motionToolTypeToString(args->pointerProperties[i].toolType),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
@@ -4178,9 +4180,12 @@
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
- LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
- args->pointerProperties),
- "Invalid event: %s", args->dump().c_str());
+
+ if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties)) {
+ LOG(ERROR) << "Invalid event: " << args->dump();
+ return;
+ }
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
diff --git a/services/inputflinger/docs/input_coordinates.md b/services/inputflinger/docs/input_coordinates.md
new file mode 100644
index 0000000..7795710
--- /dev/null
+++ b/services/inputflinger/docs/input_coordinates.md
@@ -0,0 +1,113 @@
+# Input Coordinate Processing in InputFlinger
+
+This document aims to illustrate why we need to take care when converting
+between the discrete and continuous coordinate spaces, especially when
+performing rotations.
+
+The Linux evdev protocol works over **discrete integral** values. The same is
+true for displays, which output discrete pixels. WindowManager also tracks
+window bounds in pixels in the rotated logical display.
+
+However, our `MotionEvent` APIs
+report **floating point** axis values in a **continuous space**. This disparity
+is important to note when working in InputFlinger, which has to make sure the
+discrete raw coordinates are converted to the continuous space correctly in all
+scenarios.
+
+## Disparity between continuous and discrete coordinates during rotation
+
+Let's consider an example of device that has a 3 x 4 screen.
+
+### Natural orientation: No rotation
+
+If the user interacts with the highlighted pixel, the touchscreen would report
+the discreet coordinates (0, 2).
+
+```
+ ┌─────┬─────┬─────┐
+ │ 0,0 │ 1,0 │ 2,0 │
+ ├─────┼─────┼─────┤
+ │ 0,1 │ 1,1 │ 2,1 │
+ ├─────┼─────┼─────┤
+ │█0,2█│ 1,2 │ 2,2 │
+ ├─────┼─────┼─────┤
+ │ 0,3 │ 1,3 │ 2,3 │
+ └─────┴─────┴─────┘
+```
+
+When converted to the continuous space, the point (0, 2) corresponds to the
+location shown below.
+
+```
+ 0 1 2 3
+ 0 ┌─────┬─────┬─────┐
+ │ │ │ │
+ 1 ├─────┼─────┼─────┤
+ │ │ │ │
+ 2 █─────┼─────┼─────┤
+ │ │ │ │
+ 3 ├─────┼─────┼─────┤
+ │ │ │ │
+ 4 └─────┴─────┴─────┘
+```
+
+### Rotated orientation: 90-degree counter-clockwise rotation
+
+When the device is rotated and the same place on the touchscreen is touched, the
+input device will still report the same coordinates of (0, 2).
+
+In the rotated display, that now corresponds to the pixel (2, 2).
+
+```
+ ┌─────┬─────┬─────┬─────┐
+ │ 0,0 │ 1,0 │ 2,0 │ 3,0 │
+ ├─────┼─────┼─────┼─────┤
+ │ 0,1 │ 1,1 │ 2,1 │ 3,1 │
+ ├─────┼─────┼─────┼─────┤
+ │ 0,2 │ 1,2 │█2,2█│ 3,2 │
+ └─────┴─────┴─────┴─────┘
+```
+
+*It is important to note that rotating the device 90 degrees is NOT equivalent
+to rotating the continuous coordinate space by 90 degrees.*
+
+The point (2, 2) now corresponds to a different location in the continuous space
+than before, even though the user was interacting at the same place on the
+touchscreen.
+
+```
+ 0 1 2 3 4
+ 0 ┌─────┬─────┬─────┬─────┐
+ │ │ │ │ │
+ 1 ├─────┼─────┼─────┼─────┤
+ │ │ │ │ │
+ 2 ├─────┼─────█─────┼─────┤
+ │ │ │ │ │
+ 3 └─────┴─────┴─────┴─────┘
+```
+
+If we were to simply (incorrectly) rotate the continuous space from before by
+90 degrees, the touched point would correspond to the location (2, 3), shown
+below. This new point is outside the bounds of the display, since it does not
+correspond to any pixel at that location.
+
+It should be impossible for a touchscreen to generate points outside the bounds
+of the display, because we assume that the area of the touchscreen maps directly
+to the area of the display. Therefore, that point is an invalid coordinate that
+cannot be generated by an input device.
+
+```
+ 0 1 2 3 4
+ 0 ┌─────┬─────┬─────┬─────┐
+ │ │ │ │ ╏
+ 1 ├─────┼─────┼─────┼─────┤
+ │ │ │ │ ╏
+ 2 ├─────┼─────┼─────┼─────┤
+ │ │ │ │ ╏
+ 3 └-----┴-----█-----┴-----┘
+```
+
+The same logic applies to windows as well. When performing hit tests to
+determine if a point in the continuous space falls inside a window's bounds,
+hit test must be performed in the correct orientation, since points on the right
+and bottom edges of the window do not fall within the window bounds.
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6fd4ff3..9fe652c 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -277,7 +277,11 @@
mHasMic = mClasses.test(InputDeviceClass::MIC);
if (!isIgnored()) {
- if (!changes) { // first time only
+ // Full configuration should happen the first time configure is called
+ // and when the device type is changed. Changing a device type can
+ // affect various other parameters so should result in a
+ // reconfiguration.
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
mConfiguration.clear();
for_each_subdevice([this](InputDeviceContext& context) {
PropertyMap configuration;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 3ba6cd0..b53fc73 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -293,7 +293,10 @@
mConfig = *config;
- if (!changes) { // first time only
+ // Full configuration should happen the first time configure is called and
+ // when the device type is changed. Changing a device type can affect
+ // various other parameters so should result in a reconfiguration.
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
// Configure basic parameters.
configureParameters();
@@ -328,7 +331,8 @@
InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
- InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
+ InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE |
+ InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
// Configure device sources, display dimensions, orientation and
// scaling factors.
configureInputDevice(when, &resetNeeded);
@@ -838,38 +842,60 @@
}
void TouchInputMapper::computeInputTransforms() {
- const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+ constexpr auto isRotated = [](const ui::Transform::RotationFlags& rotation) {
+ return rotation == ui::Transform::ROT_90 || rotation == ui::Transform::ROT_270;
+ };
- ui::Size rotatedRawSize = rawSize;
- if (mInputDeviceOrientation == ui::ROTATION_270 || mInputDeviceOrientation == ui::ROTATION_90) {
- std::swap(rotatedRawSize.width, rotatedRawSize.height);
- }
- const auto rotationFlags = ui::Transform::toRotationFlags(-mInputDeviceOrientation);
- mRawRotation = ui::Transform{rotationFlags};
+ // See notes about input coordinates in the inputflinger docs:
+ // //frameworks/native/services/inputflinger/docs/input_coordinates.md
// Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0).
- ui::Transform undoRawOffset;
- undoRawOffset.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
+ ui::Transform undoOffsetInRaw;
+ undoOffsetInRaw.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
- // Step 2: Rotate the raw coordinates to the expected orientation.
- ui::Transform rotate;
- // When rotating raw coordinates, the raw size will be used as an offset.
- // Account for the extra unit added to the raw range when the raw size was calculated.
- rotate.set(rotationFlags, rotatedRawSize.width - 1, rotatedRawSize.height - 1);
+ // Step 2: Rotate the raw coordinates to account for input device orientation. The coordinates
+ // will now be in the same orientation as the display in ROTATION_0.
+ // Note: Negating an ui::Rotation value will give its inverse rotation.
+ const auto inputDeviceOrientation = ui::Transform::toRotationFlags(-mParameters.orientation);
+ const ui::Size orientedRawSize = isRotated(inputDeviceOrientation)
+ ? ui::Size{mRawPointerAxes.getRawHeight(), mRawPointerAxes.getRawWidth()}
+ : ui::Size{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+ // When rotating raw values, account for the extra unit added when calculating the raw range.
+ const auto orientInRaw = ui::Transform(inputDeviceOrientation, orientedRawSize.width - 1,
+ orientedRawSize.height - 1);
- // Step 3: Scale the raw coordinates to the display space.
- ui::Transform scaleToDisplay;
- const float xScale = static_cast<float>(mDisplayBounds.width) / rotatedRawSize.width;
- const float yScale = static_cast<float>(mDisplayBounds.height) / rotatedRawSize.height;
- scaleToDisplay.set(xScale, 0, 0, yScale);
+ // Step 3: Rotate the raw coordinates to account for the display rotation. The coordinates will
+ // now be in the same orientation as the rotated display. There is no need to rotate the
+ // coordinates to the display rotation if the device is not orientation-aware.
+ const auto viewportRotation = ui::Transform::toRotationFlags(-mViewport.orientation);
+ const auto rotatedRawSize = mParameters.orientationAware && isRotated(viewportRotation)
+ ? ui::Size{orientedRawSize.height, orientedRawSize.width}
+ : orientedRawSize;
+ // When rotating raw values, account for the extra unit added when calculating the raw range.
+ const auto rotateInRaw = mParameters.orientationAware
+ ? ui::Transform(viewportRotation, rotatedRawSize.width - 1, rotatedRawSize.height - 1)
+ : ui::Transform();
- mRawToDisplay = (scaleToDisplay * (rotate * undoRawOffset));
+ // Step 4: Scale the raw coordinates to the display space.
+ // - Here, we assume that the raw surface of the touch device maps perfectly to the surface
+ // of the display panel. This is usually true for touchscreens.
+ // - From this point onward, we are no longer in the discrete space of the raw coordinates but
+ // are in the continuous space of the logical display.
+ ui::Transform scaleRawToDisplay;
+ const float xScale = static_cast<float>(mViewport.deviceWidth) / rotatedRawSize.width;
+ const float yScale = static_cast<float>(mViewport.deviceHeight) / rotatedRawSize.height;
+ scaleRawToDisplay.set(xScale, 0, 0, yScale);
- // Calculate the transform that takes raw coordinates to the rotated display space.
- ui::Transform displayToRotatedDisplay;
- displayToRotatedDisplay.set(ui::Transform::toRotationFlags(-mViewport.orientation),
- mViewport.deviceWidth, mViewport.deviceHeight);
- mRawToRotatedDisplay = displayToRotatedDisplay * mRawToDisplay;
+ // Step 5: Undo the display rotation to bring us back to the un-rotated display coordinate space
+ // that InputReader uses.
+ const auto undoRotateInDisplay =
+ ui::Transform(viewportRotation, mViewport.deviceWidth, mViewport.deviceHeight)
+ .inverse();
+
+ // Now put it all together!
+ mRawToRotatedDisplay = (scaleRawToDisplay * (rotateInRaw * (orientInRaw * undoOffsetInRaw)));
+ mRawToDisplay = (undoRotateInDisplay * mRawToRotatedDisplay);
+ mRawRotation = ui::Transform{mRawToDisplay.getOrientation()};
}
void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
@@ -945,6 +971,9 @@
mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop,
mViewport.physicalRight, mViewport.physicalBottom};
+ // TODO(b/257118693): Remove the dependence on the old orientation/rotation logic that
+ // uses mInputDeviceOrientation. The new logic uses the transforms calculated in
+ // computeInputTransforms().
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
// orientation-aware, then we need to apply the inverse rotation of the display so that
@@ -1650,7 +1679,8 @@
mPointerController->setButtonState(mCurrentRawState.buttonState);
mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
- mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mCurrentCookedState.cookedPointerData.touchingIdBits |
+ mCurrentCookedState.cookedPointerData.hoveringIdBits,
mViewport.displayId);
}
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 8ab6748..d3af402 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -31,6 +31,15 @@
namespace {
+/**
+ * Log details of each gesture output by the gestures library.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
+ * restarting the shell)
+ */
+const bool DEBUG_TOUCHPAD_GESTURES =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+
// Describes a segment of the acceleration curve.
struct CurveSegment {
// The maximum pointer speed which this segment should apply. The last segment in a curve should
@@ -251,6 +260,7 @@
std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs) {
+ ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
mProcessing = true;
mGestureInterpreter->PushHardwareState(&schs.state);
mProcessing = false;
@@ -259,7 +269,7 @@
}
void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
- ALOGD("Gesture ready: %s", gesture->String().c_str());
+ ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
if (!mProcessing) {
ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
return;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 2e175b8..c091a51 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -80,18 +80,32 @@
schs.fingers.clear();
for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
- if (slot.isInUse()) {
- FingerState& fingerState = schs.fingers.emplace_back();
- fingerState = {};
- fingerState.touch_major = slot.getTouchMajor();
- fingerState.touch_minor = slot.getTouchMinor();
- fingerState.width_major = slot.getToolMajor();
- fingerState.width_minor = slot.getToolMinor();
- fingerState.pressure = slot.getPressure();
- fingerState.orientation = slot.getOrientation();
- fingerState.position_x = slot.getX();
- fingerState.position_y = slot.getY();
- fingerState.tracking_id = slot.getTrackingId();
+ if (!slot.isInUse()) {
+ continue;
+ }
+ // Some touchpads continue to report contacts even after they've identified them as palms.
+ // We want to exclude these contacts from the HardwareStates, but still need to report a
+ // tracking ID of -1 if a finger turns into a palm.
+ const bool isPalm = slot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM;
+ if (isPalm && mFingerSlots.find(i) == mFingerSlots.end()) {
+ continue;
+ }
+
+ FingerState& fingerState = schs.fingers.emplace_back();
+ fingerState = {};
+ fingerState.touch_major = slot.getTouchMajor();
+ fingerState.touch_minor = slot.getTouchMinor();
+ fingerState.width_major = slot.getToolMajor();
+ fingerState.width_minor = slot.getToolMinor();
+ fingerState.pressure = slot.getPressure();
+ fingerState.orientation = slot.getOrientation();
+ fingerState.position_x = slot.getX();
+ fingerState.position_y = slot.getY();
+ fingerState.tracking_id = isPalm ? -1 : slot.getTrackingId();
+ if (fingerState.tracking_id == -1) {
+ mFingerSlots.erase(i);
+ } else {
+ mFingerSlots.insert(i);
}
}
schs.state.fingers = schs.fingers.data();
@@ -103,6 +117,7 @@
void HardwareStateConverter::reset() {
mCursorButtonAccumulator.reset(mDeviceContext);
mTouchButtonAccumulator.reset();
+ mFingerSlots.clear();
mMscTimestamp = 0;
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 8831299..d6787b7 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -17,6 +17,7 @@
#pragma once
#include <optional>
+#include <set>
#include <utils/Timers.h>
@@ -53,6 +54,7 @@
MultiTouchMotionAccumulator mMotionAccumulator;
TouchButtonAccumulator mTouchButtonAccumulator;
int32_t mMscTimestamp = 0;
+ std::set<size_t> mFingerSlots;
};
} // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index af40fed..97138c7 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -86,6 +86,13 @@
],
},
},
+ sanitize: {
+ undefined: true,
+ all_undefined: true,
+ diag: {
+ undefined: true,
+ },
+ },
static_libs: [
"libc++fs",
"libgmock",
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 7921881..36b9bab 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -191,6 +191,68 @@
EXPECT_EQ(0u, finger2.flags);
}
+TEST_F(HardwareStateConverterTest, OnePalm) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.finger_cnt);
+}
+
+TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(1, schs->state.finger_cnt);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ ASSERT_EQ(1, schs->state.finger_cnt);
+ EXPECT_EQ(-1, schs->state.fingers[0].tracking_id);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 53);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 97);
+
+ schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(0, schs->state.finger_cnt);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 55);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 95);
+ schs = processSync(conv, time);
+ ASSERT_TRUE(schs.has_value());
+ ASSERT_EQ(1, schs->state.finger_cnt);
+ const FingerState& newFinger = schs->state.fingers[0];
+ EXPECT_EQ(123, newFinger.tracking_id);
+ EXPECT_NEAR(55, newFinger.position_x, EPSILON);
+ EXPECT_NEAR(95, newFinger.position_y, EPSILON);
+}
+
TEST_F(HardwareStateConverterTest, ButtonPressed) {
const nsecs_t time = ARBITRARY_TIME;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index a02ef05..ae30006 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -54,7 +54,8 @@
if (!changes ||
(changes &
(InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
+ InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
mReader->requestRefreshConfiguration(changes);
mReader->loopOnce();
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index fe7af80..0855683 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6724,6 +6724,29 @@
ASSERT_FALSE(fakePointerController->isPointerShown());
}
+TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) {
+ // Initialize the device without setting device source to touch navigation.
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Ensure that the device is created as a touchscreen, not touch navigation.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+ // Add device type association after the device was created.
+ mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+
+ // Send update to the mapper.
+ std::list<NotifyArgs> unused2 =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_DEVICE_TYPE /*changes*/);
+
+ // Check whether device type update was successful.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -6731,28 +6754,30 @@
// The values inside DisplayViewport are expected to be pre-rotated. This updates the current
// DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
// rotated equivalent of the given un-rotated physical display bounds.
- void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
+ void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay,
+ int32_t naturalDisplayWidth = DISPLAY_WIDTH,
+ int32_t naturalDisplayHeight = DISPLAY_HEIGHT) {
uint32_t inverseRotationFlags;
- auto width = DISPLAY_WIDTH;
- auto height = DISPLAY_HEIGHT;
+ auto rotatedWidth = naturalDisplayWidth;
+ auto rotatedHeight = naturalDisplayHeight;
switch (orientation) {
case ui::ROTATION_90:
inverseRotationFlags = ui::Transform::ROT_270;
- std::swap(width, height);
+ std::swap(rotatedWidth, rotatedHeight);
break;
case ui::ROTATION_180:
inverseRotationFlags = ui::Transform::ROT_180;
break;
case ui::ROTATION_270:
inverseRotationFlags = ui::Transform::ROT_90;
- std::swap(width, height);
+ std::swap(rotatedWidth, rotatedHeight);
break;
case ui::ROTATION_0:
inverseRotationFlags = ui::Transform::ROT_0;
break;
}
- const ui::Transform rotation(inverseRotationFlags, width, height);
+ const ui::Transform rotation(inverseRotationFlags, rotatedWidth, rotatedHeight);
const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
std::optional<DisplayViewport> internalViewport =
@@ -6771,8 +6796,8 @@
v.physicalRight = rotatedPhysicalDisplay.right;
v.physicalBottom = rotatedPhysicalDisplay.bottom;
- v.deviceWidth = width;
- v.deviceHeight = height;
+ v.deviceWidth = rotatedWidth;
+ v.deviceHeight = rotatedHeight;
v.isActive = true;
v.uniqueId = UNIQUE_ID;
@@ -6886,6 +6911,197 @@
}
}
+// --- TouchscreenPrecisionTests ---
+
+// This test suite is used to ensure that touchscreen devices are scaled and configured correctly
+// in various orientations and with different display rotations. We configure the touchscreen to
+// have a higher resolution than that of the display by an integer scale factor in each axis so that
+// we can enforce that coordinates match precisely as expected.
+class TouchscreenPrecisionTestsFixture : public TouchDisplayProjectionTest,
+ public ::testing::WithParamInterface<ui::Rotation> {
+public:
+ void SetUp() override {
+ SingleTouchInputMapperTest::SetUp();
+
+ // Prepare the raw axes to have twice the resolution of the display in the X axis and
+ // four times the resolution of the display in the Y axis.
+ prepareButtons();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, PRECISION_RAW_X_MIN, PRECISION_RAW_X_MAX,
+ 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, PRECISION_RAW_Y_MIN, PRECISION_RAW_Y_MAX,
+ 0, 0);
+ }
+
+ static const int32_t PRECISION_RAW_X_MIN = TouchInputMapperTest::RAW_X_MIN;
+ static const int32_t PRECISION_RAW_X_MAX = PRECISION_RAW_X_MIN + DISPLAY_WIDTH * 2 - 1;
+ static const int32_t PRECISION_RAW_Y_MIN = TouchInputMapperTest::RAW_Y_MIN;
+ static const int32_t PRECISION_RAW_Y_MAX = PRECISION_RAW_Y_MIN + DISPLAY_HEIGHT * 4 - 1;
+
+ static const std::array<Point, 4> kRawCorners;
+};
+
+const std::array<Point, 4> TouchscreenPrecisionTestsFixture::kRawCorners = {{
+ {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MIN}, // left-top
+ {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MIN}, // right-top
+ {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MAX}, // right-bottom
+ {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MAX}, // left-bottom
+}};
+
+// Tests for how the touchscreen is oriented relative to the natural orientation of the display.
+// For example, if a touchscreen is configured with an orientation of 90 degrees, it is a portrait
+// touchscreen panel that is used on a device whose natural display orientation is in landscape.
+TEST_P(TouchscreenPrecisionTestsFixture, OrientationPrecision) {
+ enum class Orientation {
+ ORIENTATION_0 = ui::toRotationInt(ui::ROTATION_0),
+ ORIENTATION_90 = ui::toRotationInt(ui::ROTATION_90),
+ ORIENTATION_180 = ui::toRotationInt(ui::ROTATION_180),
+ ORIENTATION_270 = ui::toRotationInt(ui::ROTATION_270),
+ ftl_last = ORIENTATION_270,
+ };
+ using Orientation::ORIENTATION_0, Orientation::ORIENTATION_90, Orientation::ORIENTATION_180,
+ Orientation::ORIENTATION_270;
+ static const std::map<Orientation, std::array<vec2, 4> /*mappedCorners*/> kMappedCorners = {
+ {ORIENTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}},
+ {ORIENTATION_90, {{{0, 479.5}, {0, 0}, {799.75, 0}, {799.75, 479.5}}}},
+ {ORIENTATION_180, {{{479.5, 799.75}, {0, 799.75}, {0, 0}, {479.5, 0}}}},
+ {ORIENTATION_270, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}},
+ };
+
+ const auto touchscreenOrientation = static_cast<Orientation>(ui::toRotationInt(GetParam()));
+
+ // Configure the touchscreen as being installed in the one of the four different orientations
+ // relative to the display.
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.orientation", ftl::enum_string(touchscreenOrientation).c_str());
+ prepareDisplay(ui::ROTATION_0);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // If the touchscreen is installed in a rotated orientation relative to the display (i.e. in
+ // orientations of either 90 or 270) this means the display's natural resolution will be
+ // flipped.
+ const bool displayRotated =
+ touchscreenOrientation == ORIENTATION_90 || touchscreenOrientation == ORIENTATION_270;
+ const int32_t width = displayRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ const int32_t height = displayRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ const Rect physicalFrame{0, 0, width, height};
+ configurePhysicalDisplay(ui::ROTATION_0, physicalFrame, width, height);
+
+ const auto& expectedPoints = kMappedCorners.at(touchscreenOrientation);
+ const float expectedPrecisionX = displayRotated ? 4 : 2;
+ const float expectedPrecisionY = displayRotated ? 2 : 4;
+
+ // Test all four corners.
+ for (int i = 0; i < 4; i++) {
+ const auto& raw = kRawCorners[i];
+ processDown(mapper, raw.x, raw.y);
+ processSync(mapper);
+ const auto& expected = expectedPoints[i];
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(expected.x, expected.y),
+ WithPrecision(expectedPrecisionX, expectedPrecisionY))))
+ << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+ << "with touchscreen orientation "
+ << ftl::enum_string(touchscreenOrientation).c_str() << ", expected point ("
+ << expected.x << ", " << expected.y << ").";
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(expected.x, expected.y))));
+ }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionWhenOrientationAware) {
+ static const std::map<ui::Rotation /*rotation*/, std::array<vec2, 4> /*mappedCorners*/>
+ kMappedCorners = {
+ {ui::ROTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}},
+ {ui::ROTATION_90, {{{0.5, 0}, {480, 0}, {480, 799.75}, {0.5, 799.75}}}},
+ {ui::ROTATION_180, {{{0.5, 0.25}, {480, 0.25}, {480, 800}, {0.5, 800}}}},
+ {ui::ROTATION_270, {{{0, 0.25}, {479.5, 0.25}, {479.5, 800}, {0, 800}}}},
+ };
+
+ const ui::Rotation displayRotation = GetParam();
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(displayRotation);
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ const auto& expectedPoints = kMappedCorners.at(displayRotation);
+
+ // Test all four corners.
+ for (int i = 0; i < 4; i++) {
+ const auto& expected = expectedPoints[i];
+ const auto& raw = kRawCorners[i];
+ processDown(mapper, raw.x, raw.y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(expected.x, expected.y), WithPrecision(2, 4))))
+ << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+ << "with display rotation " << ui::toCString(displayRotation)
+ << ", expected point (" << expected.x << ", " << expected.y << ").";
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(expected.x, expected.y))));
+ }
+}
+
+TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionOrientationAwareInOri270) {
+ static const std::map<ui::Rotation /*orientation*/, std::array<vec2, 4> /*mappedCorners*/>
+ kMappedCorners = {
+ {ui::ROTATION_0, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}},
+ {ui::ROTATION_90, {{{800, 0}, {800, 479.5}, {0.25, 479.5}, {0.25, 0}}}},
+ {ui::ROTATION_180, {{{800, 0.5}, {800, 480}, {0.25, 480}, {0.25, 0.5}}}},
+ {ui::ROTATION_270, {{{799.75, 0.5}, {799.75, 480}, {0, 480}, {0, 0.5}}}},
+ };
+
+ const ui::Rotation displayRotation = GetParam();
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ // Ori 270, so width and height swapped
+ const Rect physicalFrame{0, 0, DISPLAY_HEIGHT, DISPLAY_WIDTH};
+ prepareDisplay(displayRotation);
+ configurePhysicalDisplay(displayRotation, physicalFrame, DISPLAY_HEIGHT, DISPLAY_WIDTH);
+
+ const auto& expectedPoints = kMappedCorners.at(displayRotation);
+
+ // Test all four corners.
+ for (int i = 0; i < 4; i++) {
+ const auto& expected = expectedPoints[i];
+ const auto& raw = kRawCorners[i];
+ processDown(mapper, raw.x, raw.y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(expected.x, expected.y), WithPrecision(4, 2))))
+ << "Failed to process raw point (" << raw.x << ", " << raw.y << ") "
+ << "with display rotation " << ui::toCString(displayRotation)
+ << ", expected point (" << expected.x << ", " << expected.y << ").";
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(expected.x, expected.y))));
+ }
+}
+
+// Run the precision tests for all rotations.
+INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFixture,
+ ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180,
+ ui::ROTATION_270),
+ [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) {
+ return ftl::enum_string(testParamInfo.param);
+ });
+
// --- ExternalStylusFusionTest ---
class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index b9d9607..edd14f8 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -176,4 +176,10 @@
return arg.downTime == downTime;
}
+MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") {
+ *result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision
+ << ", but got " << arg.xPrecision << " and " << arg.yPrecision;
+ return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
+}
+
} // namespace android
diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp
index 4676167..9e41a93 100644
--- a/services/memtrackproxy/MemtrackProxy.cpp
+++ b/services/memtrackproxy/MemtrackProxy.cpp
@@ -97,9 +97,14 @@
return calling_pid == request_pid;
}
-MemtrackProxy::MemtrackProxy()
- : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()),
- memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {}
+MemtrackProxy::MemtrackProxy() {
+ memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance();
+
+ // Only check for a HIDL implementation if we failed to get the AIDL service
+ if (!memtrack_aidl_instance_) {
+ memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance();
+ }
+}
ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type,
std::vector<MemtrackRecord>* _aidl_return) {
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 4ac9651..4fff8bb 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -28,10 +28,10 @@
SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName)
+ const String16& opPackageName, int deviceId)
: mService(service), mUid(uid), mMem(*mem),
mHalChannelHandle(halChannelHandle),
- mOpPackageName(opPackageName), mDestroyed(false) {
+ mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -180,8 +180,7 @@
};
Mutex::Autolock _l(mConnectionLock);
- SensorDevice& dev(SensorDevice::getInstance());
- int ret = dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+ int ret = configure(handle, &config);
if (rateLevel == SENSOR_DIRECT_RATE_STOP) {
if (ret == NO_ERROR) {
@@ -224,7 +223,6 @@
std::unordered_map<int, int>& existingConnections =
(!temporarilyStopped) ? mActivated : mActivatedBackup;
- SensorDevice& dev(SensorDevice::getInstance());
for (auto &i : existingConnections) {
int handle = i.first;
int rateLevel = i.second;
@@ -239,8 +237,8 @@
// Only reconfigure the channel if it's ongoing
if (!temporarilyStopped) {
// Stopping before reconfiguring is the well-tested path in CTS
- dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
- dev.configureDirectChannel(handle, getHalChannelHandle(), &capConfig);
+ configure(handle, &stopConfig);
+ configure(handle, &capConfig);
}
}
}
@@ -258,7 +256,6 @@
const struct sensors_direct_cfg_t stopConfig = {
.rate_level = SENSOR_DIRECT_RATE_STOP
};
- SensorDevice& dev(SensorDevice::getInstance());
for (auto &i : mMicRateBackup) {
int handle = i.first;
int rateLevel = i.second;
@@ -273,13 +270,23 @@
// Only reconfigure the channel if it's ongoing
if (!temporarilyStopped) {
// Stopping before reconfiguring is the well-tested path in CTS
- dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig);
- dev.configureDirectChannel(handle, getHalChannelHandle(), &config);
+ configure(handle, &stopConfig);
+ configure(handle, &config);
}
}
mMicRateBackup.clear();
}
+int SensorService::SensorDirectConnection::configure(
+ int handle, const sensors_direct_cfg_t* config) {
+ if (mDeviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ SensorDevice& dev(SensorDevice::getInstance());
+ return dev.configureDirectChannel(handle, getHalChannelHandle(), config);
+ } else {
+ return mService->configureRuntimeSensorDirectChannel(handle, this, config);
+ }
+}
+
void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
Mutex::Autolock _l(mConnectionLock);
stopAllLocked(backupRecord);
@@ -290,9 +297,8 @@
.rate_level = SENSOR_DIRECT_RATE_STOP
};
- SensorDevice& dev(SensorDevice::getInstance());
for (auto &i : mActivated) {
- dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+ configure(i.first, &config);
}
if (backupRecord && mActivatedBackup.empty()) {
@@ -306,8 +312,6 @@
if (!mActivatedBackup.empty()) {
stopAllLocked(false);
- SensorDevice& dev(SensorDevice::getInstance());
-
// recover list of report from backup
ALOG_ASSERT(mActivated.empty(),
"mActivated must be empty if mActivatedBackup was non-empty");
@@ -319,7 +323,7 @@
struct sensors_direct_cfg_t config = {
.rate_level = i.second
};
- dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+ configure(i.first, &config);
}
}
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index d39a073..bfaf811 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -39,7 +39,7 @@
public:
SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName);
+ const String16& opPackageName, int deviceId);
void dump(String8& result) const;
void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
@@ -53,6 +53,7 @@
void onSensorAccessChanged(bool hasAccess);
void onMicSensorAccessChanged(bool isMicToggleOn);
userid_t getUserId() const { return mUserId; }
+ int getDeviceId() const { return mDeviceId; }
protected:
virtual ~SensorDirectConnection();
@@ -68,6 +69,9 @@
private:
bool hasSensorAccess() const;
+ // Sends the configuration to the relevant sensor device.
+ int configure(int handle, const sensors_direct_cfg_t* config);
+
// Stops all active sensor direct report requests.
//
// If backupRecord is true, stopped requests can be recovered
@@ -95,6 +99,7 @@
const sensors_direct_mem_t mMem;
const int32_t mHalChannelHandle;
const String16 mOpPackageName;
+ const int mDeviceId;
mutable Mutex mConnectionLock;
std::unordered_map<int, int> mActivated;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3a0329c..0fb3cad 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -181,7 +181,7 @@
handle, sensor.type, sensor.name);
sp<RuntimeSensor::SensorCallback> runtimeSensorCallback(
- new RuntimeSensorCallbackProxy(std::move(callback)));
+ new RuntimeSensorCallbackProxy(callback));
sensor_t runtimeSensor = sensor;
// force the handle to be consistent
runtimeSensor.handle = handle;
@@ -193,11 +193,15 @@
return mSensors.getNonSensor().getHandle();
}
+ if (mRuntimeSensorCallbacks.find(deviceId) == mRuntimeSensorCallbacks.end()) {
+ mRuntimeSensorCallbacks.emplace(deviceId, callback);
+ }
return handle;
}
status_t SensorService::unregisterRuntimeSensor(int handle) {
ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle);
+ int deviceId = getDeviceIdFromHandle(handle);
{
Mutex::Autolock _l(mLock);
if (!unregisterDynamicSensorLocked(handle)) {
@@ -210,6 +214,20 @@
for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
connection->removeSensor(handle);
}
+
+ // If this was the last sensor for this device, remove its callback.
+ bool deviceHasSensors = false;
+ mSensors.forEachEntry(
+ [&deviceId, &deviceHasSensors] (const SensorServiceUtil::SensorList::Entry& e) -> bool {
+ if (e.deviceId == deviceId) {
+ deviceHasSensors = true;
+ return false; // stop iterating
+ }
+ return true;
+ });
+ if (!deviceHasSensors) {
+ mRuntimeSensorCallbacks.erase(deviceId);
+ }
return OK;
}
@@ -1517,7 +1535,7 @@
}
sp<ISensorEventConnection> SensorService::createSensorDirectConnection(
- const String16& opPackageName, uint32_t size, int32_t type, int32_t format,
+ const String16& opPackageName, int deviceId, uint32_t size, int32_t type, int32_t format,
const native_handle *resource) {
resetTargetSdkVersionCache(opPackageName);
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
@@ -1593,14 +1611,25 @@
native_handle_set_fdsan_tag(clone);
sp<SensorDirectConnection> conn;
- SensorDevice& dev(SensorDevice::getInstance());
- int channelHandle = dev.registerDirectChannel(&mem);
+ int channelHandle = 0;
+ if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ SensorDevice& dev(SensorDevice::getInstance());
+ channelHandle = dev.registerDirectChannel(&mem);
+ } else {
+ auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+ if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) {
+ ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+ } else {
+ int fd = dup(clone->data[0]);
+ channelHandle = runtimeSensorCallback->second->onDirectChannelCreated(fd);
+ }
+ }
if (channelHandle <= 0) {
ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
} else {
mem.handle = clone;
- conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName);
+ conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
}
if (conn == nullptr) {
@@ -1614,6 +1643,24 @@
return conn;
}
+int SensorService::configureRuntimeSensorDirectChannel(
+ int sensorHandle, const SensorDirectConnection* c, const sensors_direct_cfg_t* config) {
+ int deviceId = c->getDeviceId();
+ int sensorDeviceId = getDeviceIdFromHandle(sensorHandle);
+ if (sensorDeviceId != c->getDeviceId()) {
+ ALOGE("Cannot configure direct channel created for device %d with a sensor that belongs"
+ "to device %d", c->getDeviceId(), sensorDeviceId);
+ return BAD_VALUE;
+ }
+ auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+ if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) {
+ ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+ return BAD_VALUE;
+ }
+ return runtimeSensorCallback->second->onDirectChannelConfigured(
+ c->getHalChannelHandle(), sensorHandle, config->rate_level);
+}
+
int SensorService::setOperationParameter(
int32_t handle, int32_t type,
const Vector<float> &floats, const Vector<int32_t> &ints) {
@@ -1769,8 +1816,18 @@
void SensorService::cleanupConnection(SensorDirectConnection* c) {
Mutex::Autolock _l(mLock);
- SensorDevice& dev(SensorDevice::getInstance());
- dev.unregisterDirectChannel(c->getHalChannelHandle());
+ int deviceId = c->getDeviceId();
+ if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ SensorDevice& dev(SensorDevice::getInstance());
+ dev.unregisterDirectChannel(c->getHalChannelHandle());
+ } else {
+ auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId);
+ if (runtimeSensorCallback != mRuntimeSensorCallbacks.end()) {
+ runtimeSensorCallback->second->onDirectChannelDestroyed(c->getHalChannelHandle());
+ } else {
+ ALOGE("Runtime sensor callback for deviceId %d not found", deviceId);
+ }
+ }
mConnectionHolder.removeDirectConnection(c);
}
@@ -1848,6 +1905,19 @@
return mSensors.getInterface(handle);
}
+int SensorService::getDeviceIdFromHandle(int handle) const {
+ int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID;
+ mSensors.forEachEntry(
+ [&deviceId, handle] (const SensorServiceUtil::SensorList::Entry& e) -> bool {
+ if (e.si->getSensor().getHandle() == handle) {
+ deviceId = e.deviceId;
+ return false; // stop iterating
+ }
+ return true;
+ });
+ return deviceId;
+}
+
status_t SensorService::enable(const sp<SensorEventConnection>& connection,
int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
const String16& opPackageName) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3f6a895..fe72a69 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -154,6 +154,10 @@
virtual status_t onConfigurationChanged(int handle, bool enabled,
int64_t samplingPeriodNanos,
int64_t batchReportLatencyNanos) = 0;
+ virtual int onDirectChannelCreated(int fd) = 0;
+ virtual void onDirectChannelDestroyed(int channelHandle) = 0;
+ virtual int onDirectChannelConfigured(int channelHandle, int sensorHandle,
+ int rateLevel) = 0;
};
static char const* getServiceName() ANDROID_API { return "sensorservice"; }
@@ -187,6 +191,9 @@
status_t unregisterRuntimeSensor(int handle) ANDROID_API;
status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
+ int configureRuntimeSensorDirectChannel(int sensorHandle, const SensorDirectConnection* c,
+ const sensors_direct_cfg_t* config);
+
// Returns true if a sensor should be throttled according to our rate-throttling rules.
static bool isSensorInCappedSet(int sensorType);
@@ -370,7 +377,8 @@
int requestedMode, const String16& opPackageName, const String16& attributionTag);
virtual int isDataInjectionEnabled();
virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName,
- uint32_t size, int32_t type, int32_t format, const native_handle *resource);
+ int deviceId, uint32_t size, int32_t type, int32_t format,
+ const native_handle *resource);
virtual int setOperationParameter(
int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -380,6 +388,7 @@
String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
+ int getDeviceIdFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
@@ -517,6 +526,7 @@
std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
Mode mCurrentOperatingMode;
std::queue<sensors_event_t> mRuntimeSensorEventQueue;
+ std::unordered_map</*deviceId*/int, sp<RuntimeSensorCallback>> mRuntimeSensorCallbacks;
// true if the head tracker sensor type is currently restricted to system usage only
// (can only be unrestricted for testing, via shell cmd)
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
index 1b63d76..ee8ceb3 100644
--- a/services/sensorservice/aidl/fuzzer/fuzzer.cpp
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -16,7 +16,7 @@
#include <fuzzbinder/libbinder_ndk_driver.h>
#include <fuzzer/FuzzedDataProvider.h>
-#include <ServiceManager.h>
+#include <fakeservicemanager/FakeServiceManager.h>
#include <android-base/logging.h>
#include <android/binder_interface_utils.h>
#include <fuzzbinder/random_binder.h>
@@ -29,7 +29,7 @@
[[clang::no_destroy]] static std::once_flag gSmOnce;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+ static android::sp<android::FakeServiceManager> fakeServiceManager = new android::FakeServiceManager();
std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
fakeServiceManager->clear();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 0b4d20c..eae5871 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -213,9 +213,7 @@
float currentSdrHdrRatio = 1.f;
float desiredSdrHdrRatio = 1.f;
- bool isInternalDisplayOverlay = false;
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
-
virtual ~LayerFECompositionState();
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7d94316..175dd1d 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -538,8 +538,8 @@
return;
}
- bool computeAboveCoveredExcludingOverlays =
- coverage.aboveCoveredLayersExcludingOverlays && !layerFEState->isInternalDisplayOverlay;
+ bool computeAboveCoveredExcludingOverlays = coverage.aboveCoveredLayersExcludingOverlays &&
+ !layerFEState->outputFilter.toInternalDisplay;
/*
* opaqueRegion: area of a surface that is fully opaque.
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 8555fd6..1a56ab7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -147,6 +147,7 @@
MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
getOverlaySupport, (), (const, override));
+ MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9ad2edb..aa83883 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -1312,6 +1312,18 @@
false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) {
+ mLayerFEState.compositionType = Composition::REFRESH_RATE_INDICATOR;
+
+ expectGeometryCommonCalls();
+ expectPerFrameCommonCalls();
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
/*
* OutputLayer::uncacheBuffers
*/
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9f882f1..3cdb3d5 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -408,8 +408,8 @@
capabilities.getDesiredMinLuminance());
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate,
- bool showInMiddle) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
+ bool showRenderRate, bool showInMiddle) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
@@ -428,11 +428,22 @@
features |= RefreshRateOverlay::Features::ShowInMiddle;
}
+ if (setByHwc) {
+ features |= RefreshRateOverlay::Features::SetByHwc;
+ }
+
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+ updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+}
+
+void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
+ ATRACE_CALL();
+ if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) {
+ mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
+ }
}
bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
@@ -441,7 +452,7 @@
const auto newMode =
mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
if (newMode) {
- mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
+ updateRefreshRateOverlayRate(newMode->modePtr->getFps(), newMode->fps);
return true;
}
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 29bbad5..d9c3e1c 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -237,8 +237,9 @@
}
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate,
+ void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
bool showInMiddle) REQUIRES(kMainThreadContext);
+ void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
void animateRefreshRateOverlay();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 4194a7e..bd2680f 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1428,6 +1428,19 @@
return Error::NONE;
}
+Error AidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display displayId, bool enabled) {
+ const auto status =
+ mAidlComposerClient->setRefreshRateChangedCallbackDebugEnabled(translate<int64_t>(
+ displayId),
+ enabled);
+ if (!status.isOk()) {
+ ALOGE("setRefreshRateChangedCallbackDebugEnabled failed %s",
+ status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
Error AidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
Error error = Error::NONE;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d163ff2..8313c09 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -239,6 +239,7 @@
void onHotplugDisconnect(Display) override;
Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) override;
+ Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 9b9b7fd..c65c572 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -294,6 +294,7 @@
std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0;
virtual Error setHdrConversionStrategy(
::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0;
+ virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 470bf76..28148ac 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -808,6 +808,21 @@
return NO_ERROR;
}
+status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId displayId,
+ bool enabled) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mComposer->setRefreshRateChangedCallbackDebugEnabled(mDisplayData[displayId]
+ .hwcDisplay->getId(),
+ enabled);
+ if (error != hal::Error::NONE) {
+ ALOGE("Error in setting refresh refresh rate change callback debug enabled %s",
+ to_string(error).c_str());
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
status_t HWComposer::getDisplayDecorationSupport(
PhysicalDisplayId displayId,
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 95568eb..7a3f41c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -297,6 +297,7 @@
virtual status_t setHdrConversionStrategy(
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) = 0;
+ virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -453,6 +454,7 @@
status_t setHdrConversionStrategy(
aidl::android::hardware::graphics::common::HdrConversionStrategy,
aidl::android::hardware::graphics::common::Hdr*) override;
+ status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 9bc62b6..23de4fa 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1358,6 +1358,10 @@
return Error::UNSUPPORTED;
}
+Error HidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display, bool) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::getClientTargetProperty(
Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
IComposerClient::ClientTargetProperty property;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 2bab1fe..d04652b 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -348,6 +348,7 @@
override;
Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy,
Hdr*) override;
+ Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
index 6d492c0..6659825 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -24,9 +24,13 @@
std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+uint32_t LayerCreationArgs::getInternalLayerId(uint32_t id) {
+ return id | INTERNAL_LAYER_PREFIX;
+}
+
LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
uint32_t flags, gui::LayerMetadata metadataArg,
- std::optional<uint32_t> id)
+ std::optional<uint32_t> id, bool internalLayer)
: flinger(flinger),
client(std::move(client)),
name(std::move(name)),
@@ -46,10 +50,15 @@
if (id) {
sequence = *id;
- sSequence = *id + 1;
+ if (internalLayer) {
+ sequence = getInternalLayerId(*id);
+ } else {
+ sSequence = *id + 1;
+ }
} else {
sequence = sSequence++;
- if (sequence == UNASSIGNED_LAYER_ID) {
+ if (sequence >= INTERNAL_LAYER_PREFIX) {
+ sSequence = 1;
ALOGW("Layer sequence id rolled over.");
sequence = sSequence++;
}
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 9d2aaab..2cd6b55 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -25,6 +25,7 @@
#include <optional>
constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
+constexpr uint32_t INTERNAL_LAYER_PREFIX = 1u << 31;
namespace android {
class SurfaceFlinger;
@@ -35,10 +36,11 @@
struct LayerCreationArgs {
static std::atomic<uint32_t> sSequence;
+ static uint32_t getInternalLayerId(uint32_t id);
LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name,
- uint32_t flags, gui::LayerMetadata,
- std::optional<uint32_t> id = std::nullopt);
+ uint32_t flags, gui::LayerMetadata, std::optional<uint32_t> id = std::nullopt,
+ bool internalLayer = false);
LayerCreationArgs(const LayerCreationArgs&);
android::SurfaceFlinger* flinger;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index afe557e..c30465f 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -130,6 +130,14 @@
return mLayer;
}
+const LayerHierarchy* LayerHierarchy::getRelativeParent() const {
+ return mRelativeParent;
+}
+
+const LayerHierarchy* LayerHierarchy::getParent() const {
+ return mParent;
+}
+
std::string LayerHierarchy::getDebugStringShort() const {
std::string debug = "LayerHierarchy{";
debug += ((mLayer) ? mLayer->getDebugString() : "root") + " ";
@@ -449,7 +457,7 @@
traversalPath.id = layerId;
traversalPath.variant = variant;
if (variant == LayerHierarchy::Variant::Mirror) {
- traversalPath.mirrorRootId = layerId;
+ traversalPath.mirrorRootId = mParentPath.id;
} else if (variant == LayerHierarchy::Variant::Relative) {
if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
layerId) != traversalPath.relativeRootIds.end()) {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 2ab897b..3dd89ba 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -128,16 +128,24 @@
// Traverse the hierarchy and visit all child variants.
void traverse(const Visitor& visitor) const {
TraversalPath root = TraversalPath::ROOT;
+ if (mLayer) {
+ root.id = mLayer->id;
+ }
traverse(visitor, root);
}
// Traverse the hierarchy in z-order, skipping children that have relative parents.
void traverseInZOrder(const Visitor& visitor) const {
TraversalPath root = TraversalPath::ROOT;
+ if (mLayer) {
+ root.id = mLayer->id;
+ }
traverseInZOrder(visitor, root);
}
const RequestedLayerState* getLayer() const;
+ const LayerHierarchy* getRelativeParent() const;
+ const LayerHierarchy* getParent() const;
std::string getDebugString(const char* prefix = "") const;
std::string getDebugStringShort() const;
// Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 66197be..33cc429 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -37,7 +37,6 @@
mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
for (auto& newLayer : newLayers) {
RequestedLayerState& layer = *newLayer.get();
- LLOGV(layer.id, "%s layer %s", __func__, layer.getDebugStringShort().c_str());
auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
if (!inserted) {
LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
@@ -68,6 +67,7 @@
if (layer.isRoot()) {
updateDisplayMirrorLayers(layer);
}
+ LLOGV(layer.id, "%s", layer.getDebugString().c_str());
mLayers.emplace_back(std::move(newLayer));
}
}
@@ -81,6 +81,7 @@
continue;
}
RequestedLayerState& layer = it->second.owner;
+ LLOGV(layer.id, "%s", layer.getDebugString().c_str());
layer.handleAlive = false;
if (!layer.canBeDestroyed()) {
continue;
@@ -148,7 +149,7 @@
while (it != mLayers.end()) {
RequestedLayerState* layer = it->get();
if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
- LLOGV(layer->id, "destroyed layer %s", layer->getDebugStringShort().c_str());
+ LLOGV(layer->id, "destroyed %s", layer->getDebugStringShort().c_str());
std::iter_swap(it, mLayers.end() - 1);
mDestroyedLayers.emplace_back(std::move(mLayers.back()));
if (it == mLayers.end() - 1) {
@@ -186,41 +187,48 @@
continue;
}
+ if (transaction.flags & ISurfaceComposer::eAnimation) {
+ layer->changes |= RequestedLayerState::Changes::Animation;
+ }
+
uint32_t oldParentId = layer->parentId;
uint32_t oldRelativeParentId = layer->relativeParentId;
uint32_t oldTouchCropId = layer->touchCropId;
layer->merge(resolvedComposerState);
if (layer->what & layer_state_t::eBackgroundColorChanged) {
- if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColorAlpha != 0) {
+ if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) {
LayerCreationArgs backgroundLayerArgs{nullptr,
nullptr,
layer->name + "BackgroundColorLayer",
ISurfaceComposerClient::eFXSurfaceEffect,
- {}};
+ {},
+ layer->id,
+ /*internalLayer=*/true};
std::vector<std::unique_ptr<RequestedLayerState>> newLayers;
newLayers.emplace_back(
std::make_unique<RequestedLayerState>(backgroundLayerArgs));
RequestedLayerState* backgroundLayer = newLayers.back().get();
+ backgroundLayer->bgColorLayer = true;
backgroundLayer->handleAlive = false;
backgroundLayer->parentId = layer->id;
backgroundLayer->z = std::numeric_limits<int32_t>::min();
- backgroundLayer->color.rgb = layer->color.rgb;
- backgroundLayer->color.a = layer->bgColorAlpha;
+ backgroundLayer->color = layer->bgColor;
backgroundLayer->dataspace = layer->bgColorDataspace;
-
layer->bgColorLayerId = backgroundLayer->id;
addLayers({std::move(newLayers)});
- } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID &&
- layer->bgColorAlpha == 0) {
+ } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && layer->bgColor.a == 0) {
RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
- bgColorLayer->parentId = UNASSIGNED_LAYER_ID;
- onHandlesDestroyed({layer->bgColorLayerId});
+ layer->bgColorLayerId = UNASSIGNED_LAYER_ID;
+ bgColorLayer->parentId = unlinkLayer(bgColorLayer->parentId, bgColorLayer->id);
+ onHandlesDestroyed({bgColorLayer->id});
} else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) {
RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
- bgColorLayer->color.rgb = layer->color.rgb;
- bgColorLayer->color.a = layer->bgColorAlpha;
+ bgColorLayer->color = layer->bgColor;
bgColorLayer->dataspace = layer->bgColorDataspace;
+ bgColorLayer->what |= layer_state_t::eColorChanged |
+ layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged;
+ bgColorLayer->changes |= RequestedLayerState::Changes::Content;
mGlobalChanges |= RequestedLayerState::Changes::Content;
}
}
@@ -256,8 +264,7 @@
listener->onLayerAdded(*layer);
}
}
- layer->what = 0;
- layer->changes.clear();
+ layer->clearChanges();
}
for (auto& destroyedLayer : mDestroyedLayers) {
diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h
index 47e1e30..4943483 100644
--- a/services/surfaceflinger/FrontEnd/LayerLog.h
+++ b/services/surfaceflinger/FrontEnd/LayerLog.h
@@ -25,3 +25,5 @@
#else
#define LLOGV(LAYER_ID, x, ...) ALOGV("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
#endif
+
+#define LLOGD(LAYER_ID, x, ...) ALOGD("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index dbb7fbf..8a45093 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -27,14 +27,22 @@
LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
const LayerHierarchy::TraversalPath& path)
: path(path) {
+ static uint32_t sUniqueSequenceId = 0;
+ // Provide a unique id for clones otherwise keeping using the sequence id.
+ // The seq id can still be useful for debugging if its available.
+ uniqueSequence = (path.isClone()) ? sUniqueSequenceId++ : state.id;
sequence = static_cast<int32_t>(state.id);
name = state.name;
textureName = state.textureName;
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
- inputInfo.id = static_cast<int32_t>(state.id);
+ inputInfo.id = static_cast<int32_t>(uniqueSequence);
inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
inputInfo.ownerPid = state.ownerPid;
+ changes = RequestedLayerState::Changes::Created;
+ mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
+ ? path
+ : LayerHierarchy::TraversalPath::ROOT;
}
// As documented in libhardware header, formats in the range
@@ -139,7 +147,8 @@
// visible
std::stringstream reason;
if (sidebandStream != nullptr) reason << " sidebandStream";
- if (externalTexture != nullptr) reason << " buffer";
+ if (externalTexture != nullptr)
+ reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber;
if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
@@ -164,7 +173,8 @@
std::string LayerSnapshot::getDebugString() const {
std::stringstream debug;
debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {"
- << getIsVisibleReason() << "} changes=" << changes.string() << "}";
+ << getIsVisibleReason() << "} changes=" << changes.string()
+ << " layerStack=" << outputFilter.layerStack.id << "}";
return debug.str();
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 5d74203..6fb2f57 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -57,6 +57,12 @@
bool isHiddenByPolicyFromParent = false;
bool isHiddenByPolicyFromRelativeParent = false;
ftl::Flags<RequestedLayerState::Changes> changes;
+ // Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique.
+ // For mirrored layers, snapshots will have the same sequence so this unique id provides
+ // an alternative identifier when needed.
+ uint32_t uniqueSequence;
+ // Layer id used to create this snapshot. Multiple snapshots will have the same sequence if they
+ // generated from the same layer, for example when mirroring.
int32_t sequence;
std::string name;
uint32_t textureName;
@@ -84,6 +90,9 @@
scheduler::LayerInfo::FrameRate frameRate;
ui::Transform::RotationFlags fixedTransformHint;
bool handleSkipScreenshotFlag = false;
+ int32_t frameRateSelectionPriority;
+ LayerHierarchy::TraversalPath mirrorRootPath;
+ bool unreachable = true;
ChildState childState;
static bool isOpaqueFormat(PixelFormat format);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index d740350..a16de1b 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -274,7 +274,7 @@
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
const bool visibleForInput =
- (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
+ snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
}
@@ -296,6 +296,26 @@
gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
+void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+ const LayerSnapshotBuilder::Args& args) {
+ snapshot.metadata.clear();
+ for (const auto& [key, mandatory] : args.supportedLayerGenericMetadata) {
+ auto compatIter = args.genericLayerMetadataKeyMap.find(key);
+ if (compatIter == std::end(args.genericLayerMetadataKeyMap)) {
+ continue;
+ }
+ const uint32_t id = compatIter->second;
+ auto it = requested.metadata.mMap.find(id);
+ if (it == std::end(requested.metadata.mMap)) {
+ continue;
+ }
+
+ snapshot.metadata.emplace(key,
+ compositionengine::GenericLayerMetadataEntry{mandatory,
+ it->second});
+ }
+}
+
void clearChanges(LayerSnapshot& snapshot) {
snapshot.changes.clear();
snapshot.contentDirty = false;
@@ -308,6 +328,7 @@
LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
LayerSnapshot snapshot;
+ snapshot.path = LayerHierarchy::TraversalPath::ROOT;
snapshot.changes = ftl::Flags<RequestedLayerState::Changes>();
snapshot.isHiddenByPolicyFromParent = false;
snapshot.isHiddenByPolicyFromRelativeParent = false;
@@ -337,21 +358,17 @@
LayerSnapshotBuilder::LayerSnapshotBuilder() : mRootSnapshot(getRootSnapshot()) {}
LayerSnapshotBuilder::LayerSnapshotBuilder(Args args) : LayerSnapshotBuilder() {
- args.forceUpdate = true;
+ args.forceUpdate = ForceUpdateFlags::ALL;
updateSnapshots(args);
}
bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
- if (args.forceUpdate || args.displayChanges) {
+ if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) {
// force update requested, or we have display changes, so skip the fast path
return false;
}
if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
- // there are no changes, so just clear the change flags from before.
- for (auto& snapshot : mSnapshots) {
- clearChanges(*snapshot);
- }
return true;
}
@@ -376,14 +393,12 @@
// Walk through the snapshots, clearing previous change flags and updating the snapshots
// if needed.
for (auto& snapshot : mSnapshots) {
- clearChanges(*snapshot);
auto it = layersWithChanges.find(snapshot->path.id);
if (it != layersWithChanges.end()) {
ALOGV("%s fast path snapshot changes = %s", __func__,
mRootSnapshot.changes.string().c_str());
LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
- updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root,
- /*newSnapshot=*/false);
+ updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
}
}
return true;
@@ -393,37 +408,45 @@
ATRACE_NAME("UpdateSnapshots");
if (args.parentCrop) {
mRootSnapshot.geomLayerBounds = *args.parentCrop;
- } else if (args.forceUpdate || args.displayChanges) {
+ } else if (args.forceUpdate == ForceUpdateFlags::ALL || args.displayChanges) {
mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
}
if (args.displayChanges) {
mRootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren |
RequestedLayerState::Changes::Geometry;
}
+ if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
+ mRootSnapshot.changes |=
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
+ }
LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
- for (auto& [childHierarchy, variant] : args.root.mChildren) {
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
- childHierarchy->getLayer()->id,
- variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+ if (args.root.getLayer()) {
+ // The hierarchy can have a root layer when used for screenshots otherwise, it will have
+ // multiple children.
+ LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
+ LayerHierarchy::Variant::Attached);
+ updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot);
+ } else {
+ for (auto& [childHierarchy, variant] : args.root.mChildren) {
+ LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
+ childHierarchy->getLayer()->id,
+ variant);
+ updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+ }
}
- sortSnapshotsByZ(args);
+ const bool hasUnreachableSnapshots = sortSnapshotsByZ(args);
clearChanges(mRootSnapshot);
// Destroy unreachable snapshots
- if (args.layerLifecycleManager.getDestroyedLayers().empty()) {
+ if (!hasUnreachableSnapshots) {
return;
}
- std::unordered_set<uint32_t> destroyedLayerIds;
- for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) {
- destroyedLayerIds.emplace(destroyedLayer->id);
- }
auto it = mSnapshots.begin();
while (it < mSnapshots.end()) {
auto& traversalPath = it->get()->path;
- if (destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
+ if (!it->get()->unreachable) {
it++;
continue;
}
@@ -436,6 +459,10 @@
}
void LayerSnapshotBuilder::update(const Args& args) {
+ for (auto& snapshot : mSnapshots) {
+ clearChanges(*snapshot);
+ }
+
if (tryFastUpdate(args)) {
return;
}
@@ -449,8 +476,9 @@
LayerSnapshot* snapshot = getSnapshot(traversalPath);
const bool newSnapshot = snapshot == nullptr;
if (newSnapshot) {
- snapshot = createSnapshot(traversalPath, *layer);
+ snapshot = createSnapshot(traversalPath, *layer, parentSnapshot);
}
+ scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
if (traversalPath.isRelative()) {
bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
@@ -458,7 +486,7 @@
if (traversalPath.isAttached()) {
resetRelativeState(*snapshot);
}
- updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath, newSnapshot);
+ updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
}
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
@@ -469,6 +497,10 @@
updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
updateChildState(*snapshot, childSnapshot, args);
}
+
+ if (oldFrameRate == snapshot->frameRate) {
+ snapshot->changes.clear(RequestedLayerState::Changes::FrameRate);
+ }
return *snapshot;
}
@@ -485,27 +517,34 @@
return it == mIdToSnapshot.end() ? nullptr : it->second;
}
-LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& id,
- const RequestedLayerState& layer) {
- mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, id));
+LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path,
+ const RequestedLayerState& layer,
+ const LayerSnapshot& parentSnapshot) {
+ mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, path));
LayerSnapshot* snapshot = mSnapshots.back().get();
snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1;
- mIdToSnapshot[id] = snapshot;
+ if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
+ snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
+ }
+ mIdToSnapshot[path] = snapshot;
return snapshot;
}
-void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
- if (!mResortSnapshots && !args.forceUpdate &&
+bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
+ if (!mResortSnapshots && args.forceUpdate == ForceUpdateFlags::NONE &&
!args.layerLifecycleManager.getGlobalChanges().any(
RequestedLayerState::Changes::Hierarchy |
RequestedLayerState::Changes::Visibility)) {
// We are not force updating and there are no hierarchy or visibility changes. Avoid sorting
// the snapshots.
- return;
+ return false;
}
-
mResortSnapshots = false;
+ for (auto& snapshot : mSnapshots) {
+ snapshot->unreachable = true;
+ }
+
size_t globalZ = 0;
args.root.traverseInZOrder(
[this, &globalZ](const LayerHierarchy&,
@@ -515,11 +554,7 @@
return false;
}
- if (snapshot->isHiddenByPolicy() &&
- !snapshot->changes.test(RequestedLayerState::Changes::Visibility)) {
- return false;
- }
-
+ snapshot->unreachable = false;
if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
updateVisibility(*snapshot, snapshot->getIsVisible());
size_t oldZ = snapshot->globalZ;
@@ -537,12 +572,17 @@
return true;
});
mNumInterestingSnapshots = (int)globalZ;
+ bool hasUnreachableSnapshots = false;
while (globalZ < mSnapshots.size()) {
mSnapshots[globalZ]->globalZ = globalZ;
/* mark unreachable snapshots as explicitly invisible */
updateVisibility(*mSnapshots[globalZ], false);
+ if (mSnapshots[globalZ]->unreachable) {
+ hasUnreachableSnapshots = true;
+ }
globalZ++;
}
+ return hasUnreachableSnapshots;
}
void LayerSnapshotBuilder::updateRelativeState(LayerSnapshot& snapshot,
@@ -569,7 +609,8 @@
if (snapshot.childState.hasValidFrameRate) {
return;
}
- if (args.forceUpdate || childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
+ if (args.forceUpdate == ForceUpdateFlags::ALL ||
+ childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes
// for the same reason we are allowing touch boost for those layers. See
// RefreshRateSelector::rankFrameRates for details.
@@ -612,35 +653,32 @@
void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
- const LayerHierarchy::TraversalPath& path,
- bool newSnapshot) {
+ const LayerHierarchy::TraversalPath& path) {
// Always update flags and visibility
ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes &
(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
- RequestedLayerState::Changes::AffectsChildren);
- snapshot.changes = parentChanges | requested.changes;
+ RequestedLayerState::Changes::AffectsChildren |
+ RequestedLayerState::Changes::FrameRate);
+ snapshot.changes |= parentChanges | requested.changes;
snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
(args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
// TODO(b/238781169) scope down the changes to only buffer updates.
- snapshot.hasReadyFrame =
- (snapshot.contentDirty || requested.autoRefresh) && (requested.externalTexture);
- // TODO(b/238781169) how is this used? ag/15523870
- snapshot.sidebandStreamHasFrame = false;
+ snapshot.hasReadyFrame = requested.hasReadyFrame();
+ snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
snapshot.surfaceDamage);
-
- const bool forceUpdate = newSnapshot || args.forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Visibility |
- RequestedLayerState::Changes::Created);
- snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID
- ? parentSnapshot.outputFilter.layerStack
- : requested.layerStack;
+ snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
+ ? requested.layerStack
+ : parentSnapshot.outputFilter.layerStack;
uint32_t displayRotationFlags =
getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
+ const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
+ snapshot.changes.any(RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::Created);
// always update the buffer regardless of visibility
if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
@@ -673,7 +711,8 @@
snapshot.desiredSdrHdrRatio = requested.desiredSdrHdrRatio;
}
- if (snapshot.isHiddenByPolicyFromParent && !newSnapshot) {
+ if (snapshot.isHiddenByPolicyFromParent &&
+ !snapshot.changes.test(RequestedLayerState::Changes::Created)) {
if (forceUpdate ||
snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
RequestedLayerState::Changes::Geometry |
@@ -690,9 +729,6 @@
snapshot.isSecure =
parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
- snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID
- ? parentSnapshot.outputFilter.layerStack
- : requested.layerStack;
snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay ||
(requested.flags & layer_state_t::eLayerSkipScreenshot);
snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
@@ -708,11 +744,6 @@
snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
? requested.gameMode
: parentSnapshot.gameMode;
- snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
- (requested.requestedFrameRate.type ==
- scheduler::LayerInfo::FrameRateCompatibility::NoVote))
- ? requested.requestedFrameRate
- : parentSnapshot.frameRate;
snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
? requested.fixedTransformHint
: parentSnapshot.fixedTransformHint;
@@ -722,12 +753,27 @@
(requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
}
+ if (forceUpdate ||
+ snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
+ RequestedLayerState::Changes::Hierarchy)) {
+ snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
+ (requested.requestedFrameRate.type ==
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote))
+ ? requested.requestedFrameRate
+ : parentSnapshot.frameRate;
+ }
+
+ if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) {
+ updateMetadata(snapshot, requested, args);
+ }
+
if (forceUpdate || requested.changes.get() != 0) {
snapshot.compositionType = requested.getCompositionType();
snapshot.dimmingEnabled = requested.dimmingEnabled;
snapshot.layerOpaqueFlagSet =
(requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
snapshot.cachingHint = requested.cachingHint;
+ snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
}
if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
@@ -765,18 +811,16 @@
}
snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
- snapshot.isOpaque = snapshot.isContentOpaque() && !snapshot.roundedCorner.hasRoundedCorners() &&
+ snapshot.contentOpaque = snapshot.isContentOpaque();
+ snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
snapshot.blendMode = getBlendMode(snapshot, requested);
- // TODO(b/238781169) pass this from flinger
- // snapshot.fps;
- // snapshot.metadata;
LLOGV(snapshot.sequence,
- "%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s",
- args.forceUpdate ? "Force " : "", requested.id, requested.name.c_str(),
- parentSnapshot.changes.string().c_str(), snapshot.changes.string().c_str(),
- requested.changes.string().c_str(), std::to_string(requested.what).c_str(),
- snapshot.getDebugString().c_str(), parentSnapshot.getDebugString().c_str());
+ "%supdated %s changes:%s parent:%s requested:%s requested:%s from parent %s",
+ args.forceUpdate == ForceUpdateFlags::ALL ? "Force " : "",
+ snapshot.getDebugString().c_str(), snapshot.changes.string().c_str(),
+ parentSnapshot.changes.string().c_str(), requested.changes.string().c_str(),
+ std::to_string(requested.what).c_str(), parentSnapshot.getDebugString().c_str());
}
void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
@@ -911,9 +955,14 @@
snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
} else {
snapshot.inputInfo = {};
+ // b/271132344 revisit this and see if we can always use the layers uid/pid
+ snapshot.inputInfo.name = requested.name;
+ snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
+ snapshot.inputInfo.ownerPid = requested.ownerPid;
}
- snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
+ snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
+ snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
if (!needsInputInfo(snapshot, requested)) {
return;
}
@@ -936,7 +985,9 @@
}
snapshot.inputInfo.alpha = snapshot.color.a;
- snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode;
+ snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
+ ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+ : parentSnapshot.inputInfo.touchOcclusionMode;
if (requested.dropInputMode == gui::DropInputMode::ALL ||
parentSnapshot.dropInputMode == gui::DropInputMode::ALL) {
snapshot.dropInputMode = gui::DropInputMode::ALL;
@@ -975,7 +1026,7 @@
// touches from going outside the cloned area.
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
- auto clonedRootSnapshot = getSnapshot(path.getMirrorRoot());
+ auto clonedRootSnapshot = getSnapshot(snapshot.mirrorRootPath);
if (clonedRootSnapshot) {
const Rect rect =
displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 0902ab8..3997a0a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -35,10 +35,15 @@
// snapshots when there are only buffer updates.
class LayerSnapshotBuilder {
public:
+ enum class ForceUpdateFlags {
+ NONE,
+ ALL,
+ HIERARCHY,
+ };
struct Args {
LayerHierarchy root;
const LayerLifecycleManager& layerLifecycleManager;
- bool forceUpdate = false;
+ ForceUpdateFlags forceUpdate = ForceUpdateFlags::NONE;
bool includeMetadata = false;
const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays;
// Set to true if there were display changes since last update.
@@ -48,6 +53,8 @@
bool forceFullDamage = false;
std::optional<FloatRect> parentCrop = std::nullopt;
std::unordered_set<uint32_t> excludeLayerIds;
+ const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
+ const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
};
LayerSnapshotBuilder();
@@ -92,8 +99,7 @@
LayerHierarchy::TraversalPath& traversalPath,
const LayerSnapshot& parentSnapshot);
void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
- const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&,
- bool newSnapshot);
+ const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
bool parentIsRelative, const Args& args);
static void resetRelativeState(LayerSnapshot& snapshot);
@@ -106,9 +112,11 @@
void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
const Args& args);
- void sortSnapshotsByZ(const Args& args);
+ // Return true if there are unreachable snapshots
+ bool sortSnapshotsByZ(const Args& args);
LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
- const RequestedLayerState& layer);
+ const RequestedLayerState& layer,
+ const LayerSnapshot& parentSnapshot);
void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
const Args& args);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 09523d3..e2cbe28 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -165,8 +165,13 @@
if (hadBufferOrSideStream != hasBufferOrSideStream) {
changes |= RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::VisibleRegion |
- RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input |
- RequestedLayerState::Changes::Buffer;
+ RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
+ }
+ if (clientState.what & layer_state_t::eBufferChanged) {
+ changes |= RequestedLayerState::Changes::Buffer;
+ }
+ if (clientState.what & layer_state_t::eSidebandStreamChanged) {
+ changes |= RequestedLayerState::Changes::SidebandStream;
}
}
if (what & (layer_state_t::eAlphaChanged)) {
@@ -197,7 +202,9 @@
static const mat4 identityMatrix = mat4();
hasColorTransform = colorTransform != identityMatrix;
}
- if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
+ if (clientState.what &
+ (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
+ layer_state_t::eLayerStackChanged)) {
changes |= RequestedLayerState::Changes::Z;
}
if (clientState.what & layer_state_t::eReparent) {
@@ -453,4 +460,22 @@
return backgroundBlurRadius > 0 || blurRegions.size() > 0;
}
+bool RequestedLayerState::hasFrameUpdate() const {
+ return what & layer_state_t::CONTENT_DIRTY &&
+ (externalTexture || bgColorLayerId != UNASSIGNED_LAYER_ID);
+}
+
+bool RequestedLayerState::hasReadyFrame() const {
+ return hasFrameUpdate() || changes.test(Changes::SidebandStream) || autoRefresh;
+}
+
+bool RequestedLayerState::hasSidebandStreamFrame() const {
+ return hasFrameUpdate() && sidebandStream.get();
+}
+
+void RequestedLayerState::clearChanges() {
+ what = 0;
+ changes.clear();
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 6840b25..6f5485d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -52,10 +52,14 @@
FrameRate = 1u << 13,
VisibleRegion = 1u << 14,
Buffer = 1u << 15,
+ SidebandStream = 1u << 16,
+ Animation = 1u << 17,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
void merge(const ResolvedComposerState&);
+ void clearChanges();
+
// Currently we only care about the primary display
ui::Transform getTransform(uint32_t displayRotationFlags) const;
ui::Size getUnrotatedBufferSize(uint32_t displayRotationFlags) const;
@@ -72,6 +76,9 @@
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
bool hasBlur() const;
+ bool hasFrameUpdate() const;
+ bool hasReadyFrame() const;
+ bool hasSidebandStreamFrame() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
@@ -110,6 +117,7 @@
uint32_t touchCropId = UNASSIGNED_LAYER_ID;
uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
ftl::Flags<RequestedLayerState::Changes> changes;
+ bool bgColorLayer = false;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 96d8e21..0f2af2f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -681,6 +681,9 @@
} else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
snapshot->compositionType =
aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+ } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) {
+ snapshot->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR;
} else {
// Normal buffer layers
snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
@@ -1254,7 +1257,7 @@
return parentFrameRate;
}();
- *transactionNeeded |= setFrameRateForLayerTree(frameRate);
+ *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
// The frame rate is propagated to the children
bool childrenHaveFrameRate = false;
@@ -1268,7 +1271,7 @@
if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
childrenHaveFrameRate) {
*transactionNeeded |=
- setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+ setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
}
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
@@ -1420,7 +1423,7 @@
return surfaceFrame;
}
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
@@ -1433,9 +1436,21 @@
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
- using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
- mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
+ mFlinger->mScheduler
+ ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+ scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
+ return true;
+}
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+ if (mDrawingState.frameRateForLayerTree == frameRate) {
+ return false;
+ }
+
+ mDrawingState.frameRateForLayerTree = frameRate;
+ mFlinger->mScheduler
+ ->recordLayerHistory(sequence, layerProps, systemTime(),
+ scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
return true;
}
@@ -2097,15 +2112,7 @@
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
- ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
-
- // Only populate for the primary display.
- if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
- const auto compositionType = getCompositionType(*display);
- layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
- LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
- [&]() { return layerProto->mutable_visible_region(); });
- }
+ writeCompositionStateToProto(layerProto);
}
for (const sp<Layer>& layer : mDrawingChildren) {
@@ -2115,6 +2122,18 @@
return layerProto;
}
+void Layer::writeCompositionStateToProto(LayerProto* layerProto) {
+ ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
+
+ // Only populate for the primary display.
+ if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
+ const auto compositionType = getCompositionType(*display);
+ layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+ LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
+ [&]() { return layerProto->mutable_visible_region(); });
+ }
+}
+
void Layer::writeToProtoDrawingState(LayerProto* layerInfo) {
const ui::Transform transform = getTransform();
auto buffer = getExternalTexture();
@@ -2512,7 +2531,20 @@
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
const DisplayDevice* display) const {
if (!display) return nullptr;
- return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
+ if (!mFlinger->mLayerLifecycleManagerEnabled) {
+ return display->getCompositionDisplay()->getOutputLayerForLayer(
+ getCompositionEngineLayerFE());
+ }
+ sp<LayerFE> layerFE;
+ frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)};
+ for (auto& [p, layer] : mLayerFEs) {
+ if (p == path) {
+ layerFE = layer;
+ }
+ }
+
+ if (!layerFE) return nullptr;
+ return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
}
Region Layer::getVisibleRegion(const DisplayDevice* display) const {
@@ -3021,6 +3053,10 @@
mLastClientCompositionFence);
mLastClientCompositionFence = nullptr;
}
+ } else {
+ // if we are latching a buffer for the first time then clear the mLastLatchTime since
+ // we don't want to incorrectly classify a frame if we miss the desired present time.
+ updateLastLatchTime(0);
}
mDrawingState.producerId = bufferData.producerId;
@@ -3040,7 +3076,7 @@
} else {
mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
}
-
+ mDrawingState.latchedVsyncId = info.vsyncId;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -3050,18 +3086,9 @@
mDrawingState.desiredPresentTime = desiredPresentTime;
mDrawingState.isAutoTimestamp = isAutoTimestamp;
- const nsecs_t presentTime = [&] {
- if (!isAutoTimestamp) return desiredPresentTime;
-
- const auto prediction =
- mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
- if (prediction.has_value()) return prediction->presentTime;
-
- return static_cast<nsecs_t>(0);
- }();
-
- using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+ if (mFlinger->mLegacyFrontEndEnabled) {
+ recordLayerHistoryBufferUpdate(getLayerProps());
+ }
setFrameTimelineVsyncForBufferTransaction(info, postTime);
@@ -3078,6 +3105,32 @@
return true;
}
+void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimestamp) {
+ mDrawingState.desiredPresentTime = desiredPresentTime;
+ mDrawingState.isAutoTimestamp = isAutoTimestamp;
+}
+
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+ const nsecs_t presentTime = [&] {
+ if (!mDrawingState.isAutoTimestamp) return mDrawingState.desiredPresentTime;
+
+ const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+ mDrawingState.latchedVsyncId);
+ if (prediction.has_value()) return prediction->presentTime;
+
+ return static_cast<nsecs_t>(0);
+ }();
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ scheduler::LayerHistory::LayerUpdateType::Buffer);
+}
+
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+ const nsecs_t presentTime =
+ mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ scheduler::LayerHistory::LayerUpdateType::AnimationTX);
+}
+
bool Layer::setDataspace(ui::Dataspace dataspace) {
mDrawingState.dataspaceRequested = true;
if (mDrawingState.dataspace == dataspace) return false;
@@ -3275,11 +3328,11 @@
(c.buffer != nullptr || c.bgColorLayer != nullptr);
}
-void Layer::updateTexImage(nsecs_t latchTime) {
+void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
const State& s(getDrawingState());
if (!s.buffer) {
- if (s.bgColorLayer) {
+ if (bgColorOnly) {
for (auto& handle : mDrawingState.callbackHandles) {
handle->latchTime = latchTime;
}
@@ -3476,7 +3529,7 @@
}
if (s.what & layer_state_t::eBackgroundColorChanged) {
- if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) {
+ if (mDrawingState.bgColorLayer || s.bgColor.a != 0) {
ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
return false;
}
@@ -3800,6 +3853,11 @@
}
bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+ const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
+ return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
+}
+
+bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
getDrawingState().frameNumber);
@@ -3816,8 +3874,7 @@
mFlinger->onLayerUpdate();
return false;
}
-
- updateTexImage(latchTime);
+ updateTexImage(latchTime, bgColorOnly);
if (mDrawingState.buffer == nullptr) {
return false;
}
@@ -4021,7 +4078,6 @@
snapshot->bufferSize = getBufferSize(mDrawingState);
snapshot->externalTexture = mBufferInfo.mBuffer;
snapshot->hasReadyFrame = hasReadyFrame();
- snapshot->isInternalDisplayOverlay = isInternalDisplayOverlay();
preparePerFrameCompositionState();
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 492cf9a..2fb122c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -228,6 +228,7 @@
float currentSdrHdrRatio = 1.f;
float desiredSdrHdrRatio = 1.f;
gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+ int64_t latchedVsyncId = 0;
};
explicit Layer(const LayerCreationArgs& args);
@@ -305,6 +306,7 @@
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+ void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
bool setCachingHint(gui::CachingHint cachingHint);
@@ -428,6 +430,9 @@
*/
bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/);
+ bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+ bool bgColorOnly);
+
/*
* Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
* This is used if the buffer is just latched and releases to free up the buffer
@@ -605,6 +610,7 @@
bool isRemovedFromCurrentState() const;
LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags);
+ void writeCompositionStateToProto(LayerProto* layerProto);
// Write states that are modified by the main thread. This includes drawing
// state as well as buffer data. This should be called in the main or tracing
@@ -847,6 +853,20 @@
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
+ bool setFrameRateForLayerTreeLegacy(FrameRate);
+ bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
+ void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
+ void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+ auto getLayerProps() const {
+ return scheduler::LayerProps{
+ .visible = isVisible(),
+ .bounds = getBounds(),
+ .transform = getTransform(),
+ .setFrameRateVote = getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = getFrameRateSelectionPriority(),
+ };
+ };
+ bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
protected:
// For unit tests
@@ -1019,7 +1039,6 @@
void updateTreeHasFrameRateVote();
bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded);
- bool setFrameRateForLayerTree(FrameRate);
void setZOrderRelativeOf(const wp<Layer>& relativeOf);
bool isTrustedOverlay() const;
gui::DropInputMode getDropInputMode() const;
@@ -1047,7 +1066,7 @@
bool hasFrameUpdate() const;
- void updateTexImage(nsecs_t latchTime);
+ void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false);
// Crop that applies to the buffer
Rect computeBufferCrop(const State& s);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0506c47..55281fa 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -247,6 +247,153 @@
outRegion.right = proto.right();
outRegion.bottom = proto.bottom();
}
+
+void LayerProtoHelper::writeHierarchyToProto(
+ LayersProto& outLayersProto, const frontend::LayerHierarchy& root,
+ const frontend::LayerSnapshotBuilder& snapshotBuilder,
+ const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags) {
+ using Variant = frontend::LayerHierarchy::Variant;
+ frontend::LayerSnapshot defaultSnapshot;
+
+ LayerProto* layerProto = outLayersProto.add_layers();
+ const frontend::RequestedLayerState& layer = *root.getLayer();
+ frontend::LayerSnapshot* snapshot = snapshotBuilder.getSnapshot(layer.id);
+
+ if (!snapshot) {
+ defaultSnapshot.uniqueSequence = layer.id;
+ snapshot = &defaultSnapshot;
+ }
+ writeSnapshotToProto(layerProto, layer, *snapshot, traceFlags);
+ for (const auto& [child, variant] : root.mChildren) {
+ if (variant == Variant::Attached || variant == Variant::Detached) {
+ layerProto->add_children(child->getLayer()->id);
+ } else if (variant == Variant::Relative) {
+ layerProto->add_relatives(child->getLayer()->id);
+ }
+ }
+
+ auto parent = root.getParent();
+ if (parent && parent->getLayer()) {
+ layerProto->set_parent(parent->getLayer()->id);
+ } else {
+ layerProto->set_parent(-1);
+ }
+
+ auto relativeParent = root.getRelativeParent();
+ if (relativeParent && relativeParent->getLayer()) {
+ layerProto->set_z_order_relative_of(relativeParent->getLayer()->id);
+ } else {
+ layerProto->set_z_order_relative_of(-1);
+ }
+
+ if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+ auto it = legacyLayers.find(layer.id);
+ if (it != legacyLayers.end()) {
+ it->second->writeCompositionStateToProto(layerProto);
+ }
+ }
+
+ for (const auto& [child, variant] : root.mChildren) {
+ // avoid visiting relative layers twice
+ if (variant == Variant::Detached) {
+ continue;
+ }
+ writeHierarchyToProto(outLayersProto, *child, snapshotBuilder, legacyLayers, traceFlags);
+ }
+}
+
+void LayerProtoHelper::writeSnapshotToProto(LayerProto* layerInfo,
+ const frontend::RequestedLayerState& requestedState,
+ const frontend::LayerSnapshot& snapshot,
+ uint32_t traceFlags) {
+ const ui::Transform transform = snapshot.geomLayerTransform;
+ auto buffer = requestedState.externalTexture;
+ if (buffer != nullptr) {
+ LayerProtoHelper::writeToProto(*buffer,
+ [&]() { return layerInfo->mutable_active_buffer(); });
+ LayerProtoHelper::writeToProtoDeprecated(ui::Transform(requestedState.bufferTransform),
+ layerInfo->mutable_buffer_transform());
+ }
+ layerInfo->set_invalidate(snapshot.contentDirty);
+ layerInfo->set_is_protected(snapshot.hasProtectedContent);
+ layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(snapshot.dataspace)));
+ layerInfo->set_curr_frame(requestedState.bufferData->frameNumber);
+ layerInfo->set_requested_corner_radius(requestedState.cornerRadius);
+ layerInfo->set_corner_radius(
+ (snapshot.roundedCorner.radius.x + snapshot.roundedCorner.radius.y) / 2.0);
+ layerInfo->set_background_blur_radius(snapshot.backgroundBlurRadius);
+ layerInfo->set_is_trusted_overlay(snapshot.isTrustedOverlay);
+ LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+ LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+ [&]() { return layerInfo->mutable_position(); });
+ LayerProtoHelper::writeToProto(snapshot.geomLayerBounds,
+ [&]() { return layerInfo->mutable_bounds(); });
+ LayerProtoHelper::writeToProto(snapshot.surfaceDamage,
+ [&]() { return layerInfo->mutable_damage_region(); });
+
+ if (requestedState.hasColorTransform) {
+ LayerProtoHelper::writeToProto(snapshot.colorTransform,
+ layerInfo->mutable_color_transform());
+ }
+
+ LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+ [&]() { return layerInfo->mutable_source_bounds(); });
+ LayerProtoHelper::writeToProto(snapshot.transformedBounds,
+ [&]() { return layerInfo->mutable_screen_bounds(); });
+ LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
+ [&]() { return layerInfo->mutable_corner_radius_crop(); });
+ layerInfo->set_shadow_radius(snapshot.shadowRadius);
+
+ layerInfo->set_id(snapshot.uniqueSequence);
+ layerInfo->set_name(requestedState.name);
+ layerInfo->set_type("Layer");
+
+ LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+ [&]() { return layerInfo->mutable_transparent_region(); });
+
+ layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
+ layerInfo->set_z(requestedState.z);
+
+ ui::Transform requestedTransform = requestedState.getTransform(0);
+ LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+ return layerInfo->mutable_requested_position();
+ });
+
+ LayerProtoHelper::writeToProto(requestedState.crop,
+ [&]() { return layerInfo->mutable_crop(); });
+
+ layerInfo->set_is_opaque(snapshot.contentOpaque);
+ if (requestedState.externalTexture)
+ layerInfo->set_pixel_format(
+ decodePixelFormat(requestedState.externalTexture->getPixelFormat()));
+ LayerProtoHelper::writeToProto(snapshot.color, [&]() { return layerInfo->mutable_color(); });
+ LayerProtoHelper::writeToProto(requestedState.color,
+ [&]() { return layerInfo->mutable_requested_color(); });
+ layerInfo->set_flags(requestedState.flags);
+
+ LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+ layerInfo->mutable_requested_transform());
+
+ layerInfo->set_is_relative_of(requestedState.isRelativeOf);
+
+ layerInfo->set_owner_uid(requestedState.ownerUid);
+
+ if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
+ LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
+ [&]() { return layerInfo->mutable_input_window_info(); });
+ }
+
+ if (traceFlags & LayerTracing::TRACE_EXTRA) {
+ auto protoMap = layerInfo->mutable_metadata();
+ for (const auto& entry : requestedState.metadata.mMap) {
+ (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+ }
+ }
+
+ LayerProtoHelper::writeToProto(requestedState.destinationFrame,
+ [&]() { return layerInfo->mutable_destination_frame(); });
+}
+
} // namespace surfaceflinger
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 6ade143..de4bd01 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -58,6 +58,15 @@
static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
static void writeToProto(const android::BlurRegion region, BlurRegion*);
static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
+ static void writeHierarchyToProto(LayersProto& layersProto,
+ const frontend::LayerHierarchy& root,
+ const frontend::LayerSnapshotBuilder& snapshotBuilder,
+ const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers,
+ uint32_t traceFlags);
+
+ static void writeSnapshotToProto(LayerProto* outProto,
+ const frontend::RequestedLayerState& requestedState,
+ const frontend::LayerSnapshot& snapshot, uint32_t traceFlags);
};
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 0ade467..9a4261d 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -29,7 +29,6 @@
#include <SkBlendMode.h>
#include <SkRect.h>
#include <SkSurface.h>
-#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
#undef LOG_TAG
@@ -46,15 +45,6 @@
constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
constexpr int kBufferHeight = kDigitHeight;
-SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
- constexpr float kFrameRate = 0.f;
- constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
- constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
-
- return SurfaceComposerClient::Transaction().setFrameRate(surface, kFrameRate, kCompatibility,
- kSeamlessness);
-}
-
} // namespace
SurfaceControlHolder::~SurfaceControlHolder() {
@@ -242,7 +232,7 @@
return;
}
- createTransaction(mSurfaceControl->get())
+ createTransaction()
.setLayer(mSurfaceControl->get(), INT32_MAX - 2)
.setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
@@ -272,14 +262,14 @@
}
}();
- createTransaction(mSurfaceControl->get())
- .setTransform(mSurfaceControl->get(), transform)
- .apply();
+ createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
BufferCache::const_iterator it =
mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
- const int minFps = mFpsRange.min.getIntValue();
+ // HWC minFps is not known by the framework in order
+ // to consider lower rates we set minFps to 0.
+ const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
// Clamp to the range. The current displayFps may be outside of this range if the display
@@ -327,7 +317,7 @@
frame.offsetBy(width >> 1, height >> 4);
}
- createTransaction(mSurfaceControl->get())
+ createTransaction()
.setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
.setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -335,14 +325,14 @@
}
void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
- createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
+ createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
}
void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
mDisplayFps = displayFps;
mRenderFps = renderFps;
const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
- createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
@@ -351,7 +341,23 @@
const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
- createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+SurfaceComposerClient::Transaction RefreshRateOverlay::createTransaction() const {
+ constexpr float kFrameRate = 0.f;
+ constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+ const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+ SurfaceComposerClient::Transaction transaction;
+ if (isSetByHwc()) {
+ transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
+ layer_state_t::eLayerIsRefreshRateIndicator);
+ }
+ transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+ return transaction;
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index b68a88c..0b89b8e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -21,6 +21,7 @@
#include <ftl/flags.h>
#include <ftl/small_map.h>
+#include <gui/SurfaceComposerClient.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
#include <ui/Transform.h>
@@ -55,6 +56,7 @@
Spinner = 1 << 0,
RenderRate = 1 << 1,
ShowInMiddle = 1 << 2,
+ SetByHwc = 1 << 3,
};
RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
@@ -63,6 +65,7 @@
void setViewport(ui::Size);
void changeRefreshRate(Fps, Fps);
void animate();
+ bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
private:
using Buffers = std::vector<sp<GraphicBuffer>>;
@@ -82,6 +85,8 @@
const Buffers& getOrCreateBuffers(Fps, Fps);
+ SurfaceComposerClient::Transaction createTransaction() const;
+
struct Key {
int displayFps;
int renderFps;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 839500f..327ca3f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -285,46 +285,72 @@
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
- auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
- bool stopLayerFound = false;
- auto filterVisitor = [&](Layer* layer) {
- // We don't want to capture any layers beyond the stop layer
- if (stopLayerFound) return;
+ auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds,
+ const ui::Transform transform, bool& outStopTraversal) -> bool {
+ // Likewise if we just found a stop layer, set the flag and abort
+ for (const auto& [area, stopLayerId, listener] : descriptors) {
+ if (stopLayerId != UNASSIGNED_LAYER_ID && layerId == stopLayerId) {
+ outStopTraversal = true;
+ return false;
+ }
+ }
- // Likewise if we just found a stop layer, set the flag and abort
- for (const auto& [area, stopLayerId, listener] : descriptors) {
- if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) {
- stopLayerFound = true;
+ // Compute the layer's position on the screen
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
+
+ // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
+ Rect ignore;
+ if (!transformed.intersect(sampledBounds, &ignore)) return false;
+
+ // If the layer doesn't intersect a sampling area, skip capturing it
+ bool intersectsAnyArea = false;
+ for (const auto& [area, stopLayer, listener] : descriptors) {
+ if (transformed.intersect(area, &ignore)) {
+ intersectsAnyArea = true;
+ listeners.insert(listener);
+ }
+ }
+ if (!intersectsAnyArea) return false;
+
+ ALOGV("Traversing [%s] [%d, %d, %d, %d]", layerName, bounds.left, bounds.top, bounds.right,
+ bounds.bottom);
+
+ return true;
+ };
+
+ std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots;
+ if (mFlinger.mLayerLifecycleManagerEnabled) {
+ auto filterFn = [&](const frontend::LayerSnapshot& snapshot,
+ bool& outStopTraversal) -> bool {
+ const Rect bounds =
+ frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+ snapshot.transparentRegionHint);
+ const ui::Transform transform = snapshot.geomLayerTransform;
+ return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
+ outStopTraversal);
+ };
+ getLayerSnapshots =
+ mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+ filterFn);
+ } else {
+ auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
+ bool stopLayerFound = false;
+ auto filterVisitor = [&](Layer* layer) {
+ // We don't want to capture any layers beyond the stop layer
+ if (stopLayerFound) return;
+
+ if (!layerFilterFn(layer->getDebugName(), layer->getSequence(),
+ Rect(layer->getBounds()), layer->getTransform(),
+ stopLayerFound)) {
return;
}
- }
-
- // Compute the layer's position on the screen
- const Rect bounds = Rect(layer->getBounds());
- const ui::Transform transform = layer->getTransform();
- constexpr bool roundOutwards = true;
- Rect transformed = transform.transform(bounds, roundOutwards);
-
- // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
- Rect ignore;
- if (!transformed.intersect(sampledBounds, &ignore)) return;
-
- // If the layer doesn't intersect a sampling area, skip capturing it
- bool intersectsAnyArea = false;
- for (const auto& [area, stopLayer, listener] : descriptors) {
- if (transformed.intersect(area, &ignore)) {
- intersectsAnyArea = true;
- listeners.insert(listener);
- }
- }
- if (!intersectsAnyArea) return;
-
- ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
- bounds.top, bounds.right, bounds.bottom);
- visitor(layer);
+ visitor(layer);
+ };
+ mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
};
- mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
- };
+ getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ }
std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
@@ -344,7 +370,6 @@
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
}
- auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e853833..beaf972 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -118,27 +118,17 @@
}
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
- LayerUpdateType updateType) {
+void LayerHistory::record(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+ nsecs_t now, LayerUpdateType updateType) {
std::lock_guard lock(mLock);
- auto id = layer->getSequence();
-
auto [found, layerPair] = findLayer(id);
if (found == LayerStatus::NotFound) {
// Offscreen layer
- ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
+ ALOGV("%s: %d not registered", __func__, id);
return;
}
const auto& info = layerPair->second;
- const auto layerProps = LayerInfo::LayerProps{
- .visible = layer->isVisible(),
- .bounds = layer->getBounds(),
- .transform = layer->getTransform(),
- .setFrameRateVote = layer->getFrameRateForLayerTree(),
- .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
- };
-
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
// Set frame rate to attached choreographer.
@@ -149,7 +139,7 @@
while (it != range.second) {
sp<EventThreadConnection> choreographerConnection = it->second.promote();
if (choreographerConnection) {
- choreographerConnection->frameRate = layer->getFrameRateForLayerTree().rate;
+ choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
it++;
} else {
it = mAttachedChoreographers.erase(it);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 68e7030..69caf9f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -38,6 +38,7 @@
namespace scheduler {
class LayerInfo;
+struct LayerProps;
class LayerHistory {
public:
@@ -63,7 +64,8 @@
};
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
+ void record(int32_t id, const LayerProps& props, nsecs_t presentTime, nsecs_t now,
+ LayerUpdateType updateType);
// Updates the default frame rate compatibility which takes effect when the app
// does not set a preference for refresh rate.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 0142ccd..5a90d58 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -44,14 +44,17 @@
mOwnerUid(ownerUid),
mDefaultVote(defaultVote),
mLayerVote({defaultVote, Fps()}),
- mRefreshRateHistory(name) {}
+ mLayerProps(std::make_unique<LayerProps>()),
+ mRefreshRateHistory(name) {
+ ;
+}
void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingModeChange, LayerProps props) {
+ bool pendingModeChange, const LayerProps& props) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
- mLayerProps = props;
+ *mLayerProps = props;
switch (updateType) {
case LayerUpdateType::AnimationTX:
mLastAnimationTime = std::max(lastPresentTime, now);
@@ -305,6 +308,26 @@
return mTraceTags.at(type).c_str();
}
+LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const {
+ return mLayerProps->setFrameRateVote;
+}
+
+bool LayerInfo::isVisible() const {
+ return mLayerProps->visible;
+}
+
+int32_t LayerInfo::getFrameRateSelectionPriority() const {
+ return mLayerProps->frameRateSelectionPriority;
+}
+
+FloatRect LayerInfo::getBounds() const {
+ return mLayerProps->bounds;
+}
+
+ui::Transform LayerInfo::getTransform() const {
+ return mLayerProps->transform;
+}
+
LayerInfo::RefreshRateHistory::HeuristicTraceTagData
LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
const std::string prefix = "LFPS ";
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 93485be..a3523ac 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -37,7 +37,7 @@
namespace scheduler {
using namespace std::chrono_literals;
-
+struct LayerProps;
// Maximum period between presents for a layer to be considered active.
constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
@@ -132,19 +132,11 @@
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
- struct LayerProps {
- bool visible = false;
- FloatRect bounds;
- ui::Transform transform;
- FrameRate setFrameRateVote;
- int32_t frameRateSelectionPriority = -1;
- };
-
// Records the last requested present time. It also stores information about when
// the layer was last updated. If the present time is farther in the future than the
// updated time, the updated time is the present time.
void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
- bool pendingModeChange, LayerProps props);
+ bool pendingModeChange, const LayerProps& props);
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
@@ -168,13 +160,11 @@
// updated time, the updated time is the present time.
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
- FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
- bool isVisible() const { return mLayerProps.visible; }
- int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
-
- FloatRect getBounds() const { return mLayerProps.bounds; }
-
- ui::Transform getTransform() const { return mLayerProps.transform; }
+ FrameRate getSetFrameRateVote() const;
+ bool isVisible() const;
+ int32_t getFrameRateSelectionPriority() const;
+ FloatRect getBounds() const;
+ ui::Transform getTransform() const;
// Returns a C string for tracing a vote
const char* getTraceTag(LayerHistory::LayerVoteType type) const;
@@ -294,7 +284,7 @@
static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
- LayerProps mLayerProps;
+ std::unique_ptr<LayerProps> mLayerProps;
RefreshRateHistory mRefreshRateHistory;
@@ -304,5 +294,13 @@
static bool sTraceEnabled;
};
+struct LayerProps {
+ bool visible = false;
+ FloatRect bounds;
+ ui::Transform transform;
+ LayerInfo::FrameRate setFrameRateVote;
+ int32_t frameRateSelectionPriority = -1;
+};
+
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index bc3a1a0..f18dfdc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -487,10 +487,10 @@
mLayerHistory.deregisterLayer(layer);
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
LayerHistory::LayerUpdateType updateType) {
if (pacesetterSelectorPtr()->canSwitch()) {
- mLayerHistory.record(layer, presentTime, systemTime(), updateType);
+ mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3e93ef4..62a5fb2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -219,8 +219,8 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
- EXCLUDES(mDisplayLock);
+ void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+ LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
void setDefaultFrameRateCompatibility(Layer*);
void deregisterLayer(Layer*);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 38b82b5..786706a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -456,6 +456,7 @@
android::hardware::details::setTrebleTestingOverride(true);
}
+ // TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner
mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
mRefreshRateOverlayRenderRate =
property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
@@ -473,7 +474,7 @@
{.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
.early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
mLayerLifecycleManagerEnabled =
- base::GetBoolProperty("debug.sf.enable_layer_lifecycle_manager"s, false);
+ base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false);
mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
base::GetBoolProperty("debug.sf.enable_legacy_frontend"s, true);
}
@@ -2098,8 +2099,24 @@
mScheduler->forceNextResync();
}
-void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {
- // TODO(b/202734676) update refresh rate value on the RefreshRateOverlay
+void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
+ ATRACE_CALL();
+ if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
+ const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos);
+ ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue());
+ static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+ {
+ {
+ const auto display = getDisplayDeviceLocked(*displayId);
+ FTL_FAKE_GUARD(kMainThreadContext,
+ display->updateRefreshRateOverlayRate(fps,
+ display->getActiveMode()
+ .fps,
+ /* setByHwc */ true));
+ }
+ }
+ }));
+ }
}
void SurfaceFlinger::setVsyncEnabled(PhysicalDisplayId id, bool enabled) {
@@ -2178,6 +2195,38 @@
return mustComposite;
}
+void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
+ using Changes = frontend::RequestedLayerState::Changes;
+ if (snapshot.path.isClone() ||
+ !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) {
+ return;
+ }
+
+ const auto layerProps = scheduler::LayerProps{
+ .visible = snapshot.isVisible,
+ .bounds = snapshot.geomLayerBounds,
+ .transform = snapshot.geomLayerTransform,
+ .setFrameRateVote = snapshot.frameRate,
+ .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
+ };
+
+ auto it = mLegacyLayers.find(snapshot.sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
+
+ if (snapshot.changes.test(Changes::Animation)) {
+ it->second->recordLayerHistoryAnimationTx(layerProps);
+ }
+
+ if (snapshot.changes.test(Changes::FrameRate)) {
+ it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
+ }
+
+ if (snapshot.changes.test(Changes::Buffer)) {
+ it->second->recordLayerHistoryBufferUpdate(layerProps);
+ }
+}
+
bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update,
bool transactionsFlushed, bool& outTransactionsAreEmpty) {
using Changes = frontend::RequestedLayerState::Changes;
@@ -2199,23 +2248,27 @@
mLayerLifecycleManager.getDestroyedLayers());
}
- applyAndCommitDisplayTransactionStates(update.transactions);
+ bool mustComposite = false;
+ mustComposite |= applyAndCommitDisplayTransactionStates(update.transactions);
{
ATRACE_NAME("LayerSnapshotBuilder:update");
- frontend::LayerSnapshotBuilder::Args args{.root = mLayerHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLayerLifecycleManager,
- .displays = mFrontEndDisplayInfos,
- .displayChanges = mFrontEndDisplayInfosChanged,
- .globalShadowSettings =
- mDrawingState.globalShadowSettings,
- .supportsBlur = mSupportsBlur,
- .forceFullDamage = mForceFullDamage};
+ frontend::LayerSnapshotBuilder::Args
+ args{.root = mLayerHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLayerLifecycleManager,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = mFrontEndDisplayInfosChanged,
+ .globalShadowSettings = mDrawingState.globalShadowSettings,
+ .supportsBlur = mSupportsBlur,
+ .forceFullDamage = mForceFullDamage,
+ .supportedLayerGenericMetadata =
+ getHwComposer().getSupportedLayerGenericMetadata(),
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
mLayerSnapshotBuilder.update(args);
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
- Changes::Hierarchy)) {
+ Changes::Hierarchy | Changes::Visibility)) {
mUpdateInputInfo = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
@@ -2223,23 +2276,38 @@
mVisibleRegionsDirty = true;
}
outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
- const bool mustComposite = mLayerLifecycleManager.getGlobalChanges().get() != 0;
- {
- ATRACE_NAME("LLM:commitChanges");
- mLayerLifecycleManager.commitChanges();
- }
+ mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
+ bool newDataLatched = false;
if (!mLegacyFrontEndEnabled) {
ATRACE_NAME("DisplayCallbackAndStatsUpdates");
applyTransactions(update.transactions, vsyncId);
+ const nsecs_t latchTime = systemTime();
+ bool unused = false;
- bool newDataLatched = false;
- for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (!snapshot->changes.test(Changes::Buffer)) continue;
- auto it = mLegacyLayers.find(snapshot->sequence);
+ for (auto& layer : mLayerLifecycleManager.getLayers()) {
+ if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) &&
+ layer->bgColorLayer) {
+ sp<Layer> bgColorLayer = getFactory().createEffectLayer(
+ LayerCreationArgs(this, nullptr, layer->name,
+ ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
+ std::make_optional(layer->parentId), true));
+ mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
+ }
+ if (!layer->hasReadyFrame()) continue;
+
+ auto it = mLegacyLayers.find(layer->id);
LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
+ layer->getDebugString().c_str());
+ const bool bgColorOnly =
+ !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
+ it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
mLayersWithQueuedFrames.emplace(it->second);
+ }
+
+ for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ updateLayerHistory(*snapshot);
+ if (!snapshot->hasReadyFrame) continue;
newDataLatched = true;
if (!snapshot->isVisible) break;
@@ -2252,13 +2320,20 @@
mLegacyLayers.erase(destroyedLayer->id);
}
+ {
+ ATRACE_NAME("LLM:commitChanges");
+ mLayerLifecycleManager.commitChanges();
+ }
+
+ commitTransactions();
+
// enter boot animation on first buffer latch
if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
ALOGI("Enter boot animation");
mBootStage = BootStage::BOOTANIMATION;
}
- commitTransactions();
}
+ mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
return mustComposite;
}
@@ -2896,14 +2971,19 @@
}
// We avoid any reverse traversal upwards so this shouldn't be too expensive
- mDrawingState.traverse([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) {
if (!layer->hasTrustedPresentationListener()) {
return;
}
- const std::optional<const DisplayDevice*> displayOpt =
- layerStackToDisplay.get(layer->getLayerSnapshot()->outputFilter.layerStack);
+ const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled)
+ ? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
+ : layer->getLayerSnapshot();
+ std::optional<const DisplayDevice*> displayOpt = std::nullopt;
+ if (snapshot) {
+ displayOpt = layerStackToDisplay.get(snapshot->outputFilter.layerStack);
+ }
const DisplayDevice* display = displayOpt.value_or(nullptr);
- layer->updateTrustedPresentationState(display, layer->getLayerSnapshot(),
+ layer->updateTrustedPresentationState(display, snapshot,
nanoseconds_to_milliseconds(callTime), false);
});
}
@@ -3924,8 +4004,14 @@
mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
} else {
layer->useEmptyDamage();
- // If the layer has frames we will update the latch time when latching the buffer.
- layer->updateLastLatchTime(latchTime);
+ if (!layer->hasBuffer()) {
+ // The last latch time is used to classify a missed frame as buffer stuffing
+ // instead of a missed frame. This is used to identify scenarios where we
+ // could not latch a buffer or apply a transaction due to backpressure.
+ // We only update the latch time for buffer less layers here, the latch time
+ // is updated for buffer layers when the buffer is latched.
+ layer->updateLastLatchTime(latchTime);
+ }
}
});
mForceTransactionDisplayChange = false;
@@ -4405,10 +4491,14 @@
}
if ((flags & eAnimation) && resolvedState.state.surface) {
if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
- using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
- mScheduler->recordLayerHistory(layer.get(),
- isAutoTimestamp ? 0 : desiredPresentTime,
- LayerUpdateType::AnimationTX);
+ const auto layerProps = scheduler::LayerProps{
+ .visible = layer->isVisible(),
+ .bounds = layer->getBounds(),
+ .transform = layer->getTransform(),
+ .setFrameRateVote = layer->getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ };
+ layer->recordLayerHistoryAnimationTx(layerProps);
}
}
}
@@ -4480,6 +4570,7 @@
for (const auto& [_, display] : mDisplays) {
mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
}
+ needsTraversal = true;
}
return needsTraversal;
@@ -4659,7 +4750,7 @@
}
}
if (what & layer_state_t::eBackgroundColorChanged) {
- if (layer->setBackgroundColor(s.color.rgb, s.bgColorAlpha, s.bgColorDataspace)) {
+ if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) {
flags |= eTraversalNeeded;
}
}
@@ -4868,6 +4959,10 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
+ if ((what & layer_state_t::eBufferChanged) == 0) {
+ layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+ }
+
if (what & layer_state_t::eTrustedPresentationInfoChanged) {
if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
s.trustedPresentationListener)) {
@@ -4904,8 +4999,6 @@
uint64_t transactionId) {
layer_state_t& s = composerState.state;
s.sanitize(permissions);
- const nsecs_t latchTime = systemTime();
- bool unused;
std::vector<ListenerCallbacks> filteredListeners;
for (auto& listener : s.listeners) {
@@ -4958,6 +5051,12 @@
sp<CallbackHandle>::make(listener, callbackIds, s.surface));
}
}
+ // TODO(b/238781169) remove after screenshot refactor, currently screenshots
+ // requires to read drawing state from binder thread. So we need to fix that
+ // before removing this.
+ if (what & layer_state_t::eCropChanged) {
+ if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eSidebandStreamChanged) {
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
@@ -4965,7 +5064,6 @@
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
frameTimelineInfo)) {
- layer->latchBuffer(unused, latchTime);
flags |= eTraversalNeeded;
}
mLayersWithQueuedFrames.emplace(layer);
@@ -4973,6 +5071,10 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
+ if ((what & layer_state_t::eBufferChanged) == 0) {
+ layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+ }
+
if (what & layer_state_t::eTrustedPresentationInfoChanged) {
if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
s.trustedPresentationListener)) {
@@ -4980,11 +5082,12 @@
}
}
- const auto& snapshot = mLayerSnapshotBuilder.getSnapshot(layer->getSequence());
+ const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence());
bool willPresentCurrentTransaction =
- snapshot && (snapshot->hasReadyFrame || snapshot->sidebandStreamHasFrame);
+ requestedLayerState && requestedLayerState->hasReadyFrame();
if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
flags |= eTraversalNeeded;
+
return flags;
}
@@ -5073,7 +5176,7 @@
args.name, args.flags, -1 /* parentId */);
}
- {
+ if (mLegacyFrontEndEnabled) {
std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
}
@@ -5183,8 +5286,18 @@
const sp<IBinder> token = display->getDisplayToken().promote();
LOG_ALWAYS_FATAL_IF(token == nullptr);
+ TransactionState state;
+ state.inputWindowCommands = mInputWindowCommands;
+ nsecs_t now = systemTime();
+ state.desiredPresentTime = now;
+ state.postTime = now;
+ state.permissions = layer_state_t::ACCESS_SURFACE_FLINGER;
+ state.originPid = mPid;
+ state.originUid = static_cast<int>(getuid());
+ uint64_t transactionId = (((uint64_t)mPid) << 32) | mUniqueTransactionId++;
+ state.id = transactionId;
+
// reset screen orientation and use primary layer stack
- std::vector<ResolvedComposerState> state;
Vector<DisplayState> displays;
DisplayState d;
d.what = DisplayState::eDisplayProjectionChanged |
@@ -5196,15 +5309,17 @@
d.layerStackSpaceRect.makeInvalid();
d.width = 0;
d.height = 0;
- displays.add(d);
+ state.displays.add(d);
- nsecs_t now = systemTime();
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(state);
- int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
// It should be on the main thread, apply it directly.
- applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
- /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
- {}, mPid, getuid(), transactionId);
+ if (mLegacyFrontEndEnabled) {
+ applyTransactionsLocked(transactions, /*vsyncId=*/{0});
+ } else {
+ applyAndCommitDisplayTransactionStates(transactions);
+ }
setPowerModeInternal(display, hal::PowerMode::ON);
}
@@ -5643,14 +5758,27 @@
}
}
- LayersProto layersProto;
- for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
- if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
- continue;
+ if (mLegacyFrontEndEnabled) {
+ LayersProto layersProto;
+ for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+ if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
+ continue;
+ }
+ layer->writeToProto(layersProto, traceFlags);
}
- layer->writeToProto(layersProto, traceFlags);
+ return layersProto;
}
+ const frontend::LayerHierarchy& root = mLayerHierarchyBuilder.getHierarchy();
+ LayersProto layersProto;
+ for (auto& [child, variant] : root.mChildren) {
+ if (variant != frontend::LayerHierarchy::Variant::Attached ||
+ stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
+ continue;
+ }
+ LayerProtoHelper::writeHierarchyToProto(layersProto, *child, mLayerSnapshotBuilder,
+ mLegacyLayers, traceFlags);
+ }
return layersProto;
}
@@ -6732,7 +6860,8 @@
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
- getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, args.uid);
+ getLayerSnapshots =
+ getLayerSnapshotsForScreenshots(layerStack, args.uid, /*snapshotFilterFn=*/nullptr);
} else {
auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, args.uid, visitor);
@@ -6774,7 +6903,8 @@
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
- getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID);
+ getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+ /*snapshotFilterFn=*/nullptr);
} else {
auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
@@ -6942,7 +7072,7 @@
->schedule([=]() {
bool protectedLayerFound = false;
auto layers = getLayerSnapshots();
- for (auto& [layer, layerFe] : layers) {
+ for (auto& [_, layerFe] : layers) {
protectedLayerFound |=
(layerFe->mSnapshot->isVisible &&
layerFe->mSnapshot->hasProtectedContent);
@@ -7037,12 +7167,14 @@
ATRACE_CALL();
auto layers = getLayerSnapshots();
- for (auto& [layer, layerFE] : layers) {
+ for (auto& [_, layerFE] : layers) {
frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
layerFE->mSnapshot->geomLayerTransform =
renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+ layerFE->mSnapshot->geomInverseLayerTransform =
+ layerFE->mSnapshot->geomLayerTransform.inverse();
}
// We allow the system server to take screenshots of secure layers for
@@ -7153,6 +7285,16 @@
return presentFuture;
}
+void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) const {
+ if (mLayerLifecycleManagerEnabled) {
+ for (auto& layer : mLegacyLayers) {
+ visitor(layer.second.get());
+ }
+ } else {
+ mDrawingState.traverse(visitor);
+ }
+}
+
// ---------------------------------------------------------------------------
void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
@@ -7441,10 +7583,20 @@
}
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
+ bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+ if (setByHwc) {
+ const auto status =
+ getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+ if (status != NO_ERROR) {
+ ALOGE("Error updating the refresh rate changed callback debug enabled");
+ return;
+ }
+ }
+
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+ device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
mRefreshRateOverlayRenderRate,
mRefreshRateOverlayShowInMiddle);
}
@@ -7749,6 +7901,7 @@
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly, int64_t vsyncId) {
std::vector<std::pair<Layer*, LayerFE*>> layers;
if (mLayerLifecycleManagerEnabled) {
+ nsecs_t currentTime = systemTime();
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
if (cursorOnly &&
@@ -7767,6 +7920,7 @@
snapshot->getDebugString().c_str());
auto& legacyLayer = it->second;
sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+ snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
layerFE->mSnapshot = std::move(snapshot);
refreshArgs.layers.push_back(layerFE);
layers.emplace_back(legacyLayer.get(), layerFE.get());
@@ -7791,29 +7945,40 @@
}
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
-SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
- uint32_t uid) {
- return [this, layerStack, uid]() {
+SurfaceFlinger::getLayerSnapshotsForScreenshots(
+ std::optional<ui::LayerStack> layerStack, uint32_t uid,
+ std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+ snapshotFilterFn) {
+ return [&, layerStack, uid]() {
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
- continue;
- }
- if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) {
- continue;
- }
- if (!snapshot->isVisible || !snapshot->hasSomethingToDraw()) {
- continue;
- }
+ bool stopTraversal = false;
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (stopTraversal) {
+ return;
+ }
+ if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
+ return;
+ }
+ if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) {
+ return;
+ }
+ if (!snapshot->hasSomethingToDraw()) {
+ return;
+ }
+ if (snapshotFilterFn && !snapshotFilterFn(*snapshot, stopTraversal)) {
+ return;
+ }
- auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
- auto& legacyLayer = it->second;
- sp<LayerFE> layerFE = getFactory().createLayerFE(legacyLayer->getName());
- layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
- layers.emplace_back(legacyLayer.get(), std::move(layerFE));
- }
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
+ Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get();
+ sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name);
+ layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
+ layers.emplace_back(legacyLayer, std::move(layerFE));
+ });
return layers;
};
@@ -7823,21 +7988,27 @@
SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t uid,
std::unordered_set<uint32_t> excludeLayerIds,
bool childrenOnly, const FloatRect& parentCrop) {
- return [this, excludeLayerIds = std::move(excludeLayerIds), uid, rootLayerId, childrenOnly,
+ return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly,
parentCrop]() {
+ auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly);
frontend::LayerSnapshotBuilder::Args
- args{.root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly),
+ args{.root = root,
.layerLifecycleManager = mLayerLifecycleManager,
+ .forceUpdate = frontend::LayerSnapshotBuilder::ForceUpdateFlags::HIERARCHY,
.displays = mFrontEndDisplayInfos,
.displayChanges = true,
.globalShadowSettings = mDrawingState.globalShadowSettings,
.supportsBlur = mSupportsBlur,
.forceFullDamage = mForceFullDamage,
.parentCrop = {parentCrop},
- .excludeLayerIds = std::move(excludeLayerIds)};
+ .excludeLayerIds = std::move(excludeLayerIds),
+ .supportedLayerGenericMetadata =
+ getHwComposer().getSupportedLayerGenericMetadata(),
+ .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
mLayerSnapshotBuilder.update(args);
- auto getLayerSnapshotsFn = getLayerSnapshotsForScreenshots({}, uid);
+ auto getLayerSnapshotsFn =
+ getLayerSnapshotsForScreenshots({}, uid, /*snapshotFilterFn=*/nullptr);
std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
args.root = mLayerHierarchyBuilder.getHierarchy();
args.parentCrop.reset();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 843d2bf..338531f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -726,6 +726,7 @@
REQUIRES(kMainThreadContext);
bool updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
+ void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
LifecycleUpdate flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger();
@@ -1127,6 +1128,7 @@
ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
REQUIRES(mStateLock);
+ void traverseLegacyLayers(const LayerVector::Visitor& visitor) const;
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
@@ -1395,7 +1397,9 @@
[](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
}
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
- std::optional<ui::LayerStack> layerStack, uint32_t uid);
+ std::optional<ui::LayerStack> layerStack, uint32_t uid,
+ std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+ snapshotFilterFn);
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds,
bool childrenOnly, const FloatRect& parentCrop);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 475c76b..ba08cee 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -221,12 +221,12 @@
}
}
if (layer.what & layer_state_t::eBackgroundColorChanged) {
- proto.set_bg_color_alpha(layer.bgColorAlpha);
+ proto.set_bg_color_alpha(layer.bgColor.a);
proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace));
proto::LayerState_Color3* colorProto = proto.mutable_color();
- colorProto->set_r(layer.color.r);
- colorProto->set_g(layer.color.g);
- colorProto->set_b(layer.color.b);
+ colorProto->set_r(layer.bgColor.r);
+ colorProto->set_g(layer.bgColor.g);
+ colorProto->set_b(layer.bgColor.b);
}
if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
proto.set_color_space_agnostic(layer.colorSpaceAgnostic);
@@ -510,12 +510,12 @@
layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
}
if (proto.what() & layer_state_t::eBackgroundColorChanged) {
- layer.bgColorAlpha = proto.bg_color_alpha();
+ layer.bgColor.a = proto.bg_color_alpha();
layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
const proto::LayerState_Color3& colorProto = proto.color();
- layer.color.r = colorProto.r();
- layer.color.g = colorProto.g();
- layer.color.b = colorProto.b();
+ layer.bgColor.r = colorProto.r();
+ layer.bgColor.g = colorProto.g();
+ layer.bgColor.b = colorProto.b();
}
if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) {
layer.colorSpaceAgnostic = proto.color_space_agnostic();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index f6b2c8e..f17d2e1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -220,9 +220,9 @@
sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger());
for (int i = 0; i < historySize; ++i) {
- historyV1.record(layer1.get(), time1, time1,
+ historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1,
scheduler::LayerHistory::LayerUpdateType::Buffer);
- historyV1.record(layer2.get(), time2, time2,
+ historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2,
scheduler::LayerHistory::LayerUpdateType::Buffer);
time1 += mFdp.PickValueInArray(kVsyncPeriods);
time2 += mFdp.PickValueInArray(kVsyncPeriods);
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index bf7cae9..0b8c51e 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -544,6 +544,7 @@
.apply();
{
+ SCOPED_TRACE("final color");
auto shot = screenshot();
shot->expectColor(Rect(0, 0, width, height), finalColor);
shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index e69db7c..0ea0824 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -18,6 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <android-base/properties.h>
#include <private/android_filesystem_config.h>
#include "LayerTransactionTest.h"
#include "utils/TransactionUtils.h"
@@ -119,15 +120,20 @@
shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
}
- // Remove child layer
+ if (base::GetBoolProperty("debug.sf.enable_legacy_frontend", true)) {
+ GTEST_SKIP() << "Skipping test because mirroring behavior changes with legacy frontend";
+ }
+
+ // Remove child layer and verify we can still mirror the layer when
+ // its offscreen.
Transaction().reparent(mChildLayer, nullptr).apply();
{
SCOPED_TRACE("Removed Child Layer");
auto shot = screenshot();
// Grandchild mirror
- shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+ shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK);
// Child mirror
- shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
}
// Add grandchild layer to offscreen layer
@@ -136,9 +142,9 @@
SCOPED_TRACE("Added Grandchild Layer");
auto shot = screenshot();
// Grandchild mirror
- shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+ shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
// Child mirror
- shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+ shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
}
// Add child layer
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index e9b1fbb..d0ab105 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -187,8 +187,6 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
- // Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
gui::DisplayCaptureArgs captureArgs;
captureArgs.displayToken = mDisplay;
captureArgs.width = 50;
@@ -224,4 +222,17 @@
mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
}
+// Expect no filtering because parent's position transform shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
+ Transaction().setPosition(mParent, 100, 100).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mParent->getHandle();
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+ mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index df1d0a1..012a4ad 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -103,7 +103,9 @@
"SurfaceFlinger_DestroyDisplayTest.cpp",
"SurfaceFlinger_DisplayModeSwitching.cpp",
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
@@ -113,7 +115,6 @@
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
- "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SchedulerTest.cpp",
"SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 763426a..77dc868 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -263,6 +263,37 @@
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
}
+TEST_F(LayerHierarchyTest, reparentRelativeLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ reparentLayer(11, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ setZ(11, 0);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
// mirror tests
TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
@@ -437,10 +468,11 @@
updateBackgroundColor(1, 0.5);
UPDATE_AND_VERIFY(hierarchyBuilder);
-
- std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ std::vector<uint32_t> expectedTraversalPath = {1, bgLayerId, 11, 111, 12,
+ 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
- expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ expectedTraversalPath = {bgLayerId, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
expectedTraversalPath = {};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 852cb91..b9a6159 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -172,7 +172,7 @@
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
- transactions.back().states.front().state.bgColorAlpha = alpha;
+ transactions.back().states.front().state.bgColor.a = alpha;
transactions.back().states.front().state.surface = mHandles[id];
mLifecycleManager.applyTransactions(transactions);
}
@@ -274,6 +274,22 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setTouchableRegion(uint32_t id, Region region) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+ transactions.back().states.front().state.surface = mHandles[id];
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ inputInfo->touchableRegion = region;
+ inputInfo->token = sp<BBinder>::make();
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8397f8d..b767276 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -112,7 +112,8 @@
Fps desiredRefreshRate, int numFrames) {
LayerHistory::Summary summary;
for (int i = 0; i < numFrames; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += frameRate.getPeriodNsecs();
summary = summarizeLayerHistory(time);
@@ -155,7 +156,8 @@
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -177,7 +179,8 @@
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
auto summary = summarizeLayerHistory(time);
@@ -205,7 +208,8 @@
// Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -214,7 +218,8 @@
// Max is returned since we have enough history but there is no timestamp votes.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -232,7 +237,8 @@
nsecs_t time = systemTime();
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
auto summary = summarizeLayerHistory(time);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
// Layer is still considered inactive so we expect to get Min
@@ -240,7 +246,8 @@
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+ LayerHistory::LayerUpdateType::Buffer);
summary = summarizeLayerHistory(time);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -257,7 +264,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -280,7 +288,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -307,7 +316,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -335,7 +345,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
}
@@ -363,7 +374,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -395,7 +407,8 @@
nsecs_t time = systemTime();
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
}
@@ -441,7 +454,8 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
summary = summarizeLayerHistory(time);
}
@@ -453,13 +467,15 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
@@ -472,7 +488,8 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -488,10 +505,12 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
}
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -504,7 +523,8 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
summary = summarizeLayerHistory(time);
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
@@ -530,7 +550,8 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += LO_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -551,7 +572,8 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
summary = summarizeLayerHistory(time);
}
@@ -582,7 +604,8 @@
// the very first updates makes the layer frequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -593,7 +616,8 @@
}
// the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
@@ -607,7 +631,8 @@
// Now even if we post a quick few frame we should stay infrequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -618,7 +643,8 @@
}
// More quick frames will get us to frequent again
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += HI_FPS_PERIOD;
EXPECT_EQ(1, layerCount());
@@ -645,9 +671,10 @@
nsecs_t time = systemTime();
// Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
+ time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
+ time, time, LayerHistory::LayerUpdateType::Buffer);
EXPECT_EQ(2, layerCount());
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -673,7 +700,8 @@
// layer is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -684,7 +712,8 @@
EXPECT_EQ(0, animatingLayerCount(time));
// another update with the same cadence keep in infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -694,7 +723,8 @@
EXPECT_EQ(0, animatingLayerCount(time));
// an update as animation will immediately vote for Max
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::AnimationTX);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -719,7 +749,8 @@
// Fill up the window with frequent updates
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += (60_Hz).getPeriodNsecs();
EXPECT_EQ(1, layerCount());
@@ -731,7 +762,8 @@
// posting a buffer after long inactivity should retain the layer as active
time += std::chrono::nanoseconds(3s).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
@@ -741,9 +773,11 @@
// posting more infrequent buffer should make the layer infrequent
time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -751,7 +785,8 @@
EXPECT_EQ(0, animatingLayerCount(time));
// posting another buffer should keep the layer infrequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -759,8 +794,10 @@
EXPECT_EQ(0, animatingLayerCount(time));
// posting more buffers would mean starting of an animation, so making the layer frequent
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -769,7 +806,8 @@
// posting a buffer after long inactivity should retain the layer as active
time += std::chrono::nanoseconds(3s).count();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -778,7 +816,8 @@
// posting another buffer should keep the layer frequent
time += (60_Hz).getPeriodNsecs();
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
@@ -801,7 +840,8 @@
// layer is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -869,10 +909,10 @@
const nsecs_t startTime = systemTime();
const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(infrequentLayer.get(), startTime, startTime,
- LayerHistory::LayerUpdateType::Buffer);
+ history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+ startTime, LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+ startTime, LayerHistory::LayerUpdateType::Buffer);
nsecs_t time = startTime;
nsecs_t lastInfrequentUpdate = startTime;
@@ -880,14 +920,15 @@
int infrequentLayerUpdates = 0;
while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
time += heuristicUpdateDelta.count();
- history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
totalInfrequentLayerUpdates);
lastInfrequentUpdate = time;
- history().record(infrequentLayer.get(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
+ history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time,
+ time, LayerHistory::LayerUpdateType::Buffer);
infrequentLayerUpdates++;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 89440a6..99c1d23 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -372,7 +372,7 @@
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.bgColor.a = 0.5;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
transactions.back().states.front().state.surface = handle;
@@ -383,9 +383,10 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- listener->expectLayersAdded({1, 2});
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ listener->expectLayersAdded({1, bgLayerId});
listener->expectLayersDestroyed({});
- EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf);
+ EXPECT_EQ(getRequestedLayerState(lifecycleManager, bgLayerId)->color.a, 0.5_hf);
}
TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) {
@@ -400,13 +401,13 @@
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.bgColor.a = 0.5;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
transactions.back().states.front().state.surface = handle;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0;
+ transactions.back().states.front().state.bgColor.a = 0;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
transactions.back().states.front().state.surface = handle;
@@ -417,8 +418,9 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- listener->expectLayersAdded({1, 2});
- listener->expectLayersDestroyed({2});
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ listener->expectLayersAdded({1, bgLayerId});
+ listener->expectLayersDestroyed({bgLayerId});
}
TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) {
@@ -433,7 +435,7 @@
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.bgColor.a = 0.5;
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
transactions.back().states.front().state.surface = handle;
@@ -446,8 +448,9 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- listener->expectLayersAdded({1, 2});
- listener->expectLayersDestroyed({1, 2});
+ auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ listener->expectLayersAdded({1, bgLayerId});
+ listener->expectLayersDestroyed({1, bgLayerId});
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index aa6a14e..db0b907 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -75,14 +75,14 @@
mHierarchyBuilder.update(mLifecycleManager.getLayers(),
mLifecycleManager.getDestroyedLayers());
}
- LayerSnapshotBuilder::Args args{
- .root = mHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLifecycleManager,
- .includeMetadata = false,
- .displays = mFrontEndDisplayInfos,
- .displayChanges = hasDisplayChanges,
- .globalShadowSettings = globalShadowSettings,
- };
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = hasDisplayChanges,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
actualBuilder.update(args);
// rebuild layer snapshots from scratch and verify that it matches the updated state.
@@ -100,6 +100,9 @@
}
LayerSnapshot* getSnapshot(uint32_t layerId) { return mSnapshotBuilder.getSnapshot(layerId); }
+ LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) {
+ return mSnapshotBuilder.getSnapshot(path);
+ }
LayerHierarchyBuilder mHierarchyBuilder{{}};
LayerSnapshotBuilder mSnapshotBuilder;
@@ -111,23 +114,25 @@
122, 1221, 13, 2};
TEST_F(LayerSnapshotTest, buildSnapshot) {
- LayerSnapshotBuilder::Args args{
- .root = mHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLifecycleManager,
- .includeMetadata = false,
- .displays = mFrontEndDisplayInfos,
- .globalShadowSettings = globalShadowSettings,
- };
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
LayerSnapshotBuilder builder(args);
}
TEST_F(LayerSnapshotTest, updateSnapshot) {
- LayerSnapshotBuilder::Args args{
- .root = mHierarchyBuilder.getHierarchy(),
- .layerLifecycleManager = mLifecycleManager,
- .includeMetadata = false,
- .displays = mFrontEndDisplayInfos,
- .globalShadowSettings = globalShadowSettings,
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}
+
};
LayerSnapshotBuilder builder;
@@ -320,7 +325,7 @@
// └── 2
// ROOT (DISPLAY 1)
// └── 3 (mirrors display 0)
-TEST_F(LayerSnapshotTest, displayMirrorRespects) {
+TEST_F(LayerSnapshotTest, displayMirrorRespectsLayerSkipScreenshotFlag) {
setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
setLayerStack(3, 1);
@@ -329,4 +334,96 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
}
+// ROOT (DISPLAY 0)
+// ├── 1
+// │ ├── 11
+// │ │ └── 111
+// │ └── 13
+// └── 2
+// ROOT (DISPLAY 3)
+// └── 3 (mirrors display 0)
+TEST_F(LayerSnapshotTest, mirrorLayerGetsCorrectLayerStack) {
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 3);
+ createDisplayMirrorLayer(4, ui::LayerStack::fromValue(0));
+ setLayerStack(4, 4);
+
+ std::vector<uint32_t> expected = {4, 1, 11, 111, 13, 2, 3, 1, 11,
+ 111, 13, 2, 1, 11, 111, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 3})->outputFilter.layerStack.id, 3u);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 4})->outputFilter.layerStack.id, 4u);
+}
+
+// ROOT (DISPLAY 0)
+// ├── 1 (crop 50x50)
+// │ ├── 11
+// │ │ └── 111
+// │ └── 13
+// └── 2
+// ROOT (DISPLAY 3)
+// └── 3 (mirrors display 0) (crop 100x100)
+TEST_F(LayerSnapshotTest, mirrorLayerTouchIsCroppedByMirrorRoot) {
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 3);
+ setCrop(1, Rect{50, 50});
+ setCrop(3, Rect{100, 100});
+ setCrop(111, Rect{200, 200});
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(111, touch);
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ EXPECT_TRUE(getSnapshot({.id = 111})->inputInfo.touchableRegion.hasSameRects(touch));
+ Region touchCroppedByMirrorRoot{Rect{0, 0, 50, 50}};
+ EXPECT_TRUE(getSnapshot({.id = 111, .mirrorRootId = 3})
+ ->inputInfo.touchableRegion.hasSameRects(touchCroppedByMirrorRoot));
+}
+
+TEST_F(LayerSnapshotTest, canRemoveDisplayMirror) {
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ destroyLayerHandle(3);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+}
+
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterMirroring) {
+ size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ destroyLayerHandle(3);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size());
+}
+
+// Rel z doesn't create duplicate snapshots but this is for completeness
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterRelZ) {
+ size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+ reparentRelativeLayer(13, 11);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 13, 111, 12, 121, 122, 1221, 2});
+ setZ(13, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size());
+}
+
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterLayerDestruction) {
+ size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+ destroyLayerHandle(2);
+ destroyLayerHandle(122);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 26281d2..dc76b4c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -161,7 +161,8 @@
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -185,7 +186,8 @@
kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
@@ -234,7 +236,8 @@
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
- mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ LayerHistory::LayerUpdateType::Buffer);
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn));
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6adcd52..44ab569 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -380,8 +380,10 @@
commitTransaction();
auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
- history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
+ LayerHistory::LayerUpdateType::Buffer);
+ history.record(child->getSequence(), child->getLayerProps(), 0, 0,
+ LayerHistory::LayerUpdateType::Buffer);
const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
const auto summary = history.summarize(*selectorPtr, 0);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
new file mode 100644
index 0000000..a2c54ac
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+#include <gui/AidlStatusUtil.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
+using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
+using gui::aidl_utils::statusTFromBinderStatus;
+
+TEST(HdrOutputControlTest, testGetHdrOutputConversionSupport) {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+ bool hdrOutputConversionSupport;
+ binder::Status status = sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+}
+
+TEST(HdrOutputControlTest, testGetHdrConversionCapabilities) {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+ bool hdrOutputConversionSupport;
+ binder::Status getSupportStatus =
+ sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+ std::vector<gui::HdrConversionCapability> capabilities;
+ binder::Status status = sf->getHdrConversionCapabilities(&capabilities);
+
+ if (hdrOutputConversionSupport) {
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ } else {
+ ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+ }
+}
+
+TEST(HdrOutputControlTest, testSetHdrConversionStrategy) {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+ bool hdrOutputConversionSupport;
+ binder::Status getSupportStatus =
+ sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+ std::vector<HdrConversionStrategy> strategies =
+ {HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+ GuiHdrConversionStrategyTag::passthrough)>),
+ HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+ GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>),
+ HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+ GuiHdrConversionStrategyTag::forceHdrConversion)>)};
+ int32_t outPreferredHdrOutputType = 0;
+
+ for (HdrConversionStrategy strategy : strategies) {
+ binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+
+ if (hdrOutputConversionSupport) {
+ ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+ } else {
+ ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index f28b8d8..5dc3490 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -175,6 +175,7 @@
Error(aidl::android::hardware::graphics::composer3::OverlayProperties*));
MOCK_METHOD1(onHotplugConnect, void(Display));
MOCK_METHOD1(onHotplugDisconnect, void(Display));
+ MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
};
} // namespace Hwc2::mock