Merge "Use external texture for buffer release." 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/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 6a3120c..bf2c0d1 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -308,7 +308,7 @@
// This is different from the normal installd. We only do the base
// directory, the rest will be created on demand when each app is compiled.
if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
- LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+ PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
return false;
}
@@ -460,7 +460,7 @@
// this tool will wipe the OTA artifact cache and try again (for robustness after
// a failed OTA with remaining cache artifacts).
if (access(apk_path, F_OK) != 0) {
- LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
+ PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path;
return true;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index c62734a..1b7acab 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -45,6 +45,10 @@
namespace android {
namespace installd {
+// We don't know the filesystem types of the partitions in the update package,
+// so just try the possibilities one by one.
+static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"};
+
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -82,6 +86,27 @@
}
}
+static bool TryMountWithFstypes(const char* block_device, const char* target) {
+ for (int i = 0; i < kTryMountFsTypes.size(); ++i) {
+ const char* fstype = kTryMountFsTypes[i];
+ int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr);
+ if (mount_result == 0) {
+ return true;
+ }
+ if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) {
+ // Only try the next fstype if mounting failed due to the current one
+ // being invalid.
+ LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype << " - trying " << kTryMountFsTypes[i + 1];
+ } else {
+ PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with "
+ << fstype;
+ return false;
+ }
+ }
+ __builtin_unreachable();
+}
+
static void TryExtraMount(const char* name, const char* slot, const char* target) {
std::string partition_name = StringPrintf("%s%s", name, slot);
@@ -91,12 +116,7 @@
if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
std::string path;
if (dm.GetDmDevicePathByName(partition_name, &path)) {
- int mount_result = mount(path.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- if (mount_result == 0) {
+ if (TryMountWithFstypes(path.c_str(), target)) {
return;
}
}
@@ -105,12 +125,7 @@
// Fall back and attempt a direct mount.
std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
- int mount_result = mount(block_device.c_str(),
- target,
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(mount_result);
+ (void)TryMountWithFstypes(block_device.c_str(), target);
}
// Entry for otapreopt_chroot. Expected parameters are:
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index f950276..db5c34e 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -60,6 +60,11 @@
i=0
while ((i<MAXIMUM_PACKAGES)) ; do
+ DONE=$(cmd otadexopt done)
+ if [ "$DONE" = "OTA complete." ] ; then
+ break
+ fi
+
DEXOPT_PARAMS=$(cmd otadexopt next)
/system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
@@ -67,13 +72,8 @@
PROGRESS=$(cmd otadexopt progress)
print -u${STATUS_FD} "global_progress $PROGRESS"
- DONE=$(cmd otadexopt done)
- if [ "$DONE" = "OTA incomplete." ] ; then
- sleep 1
- i=$((i+1))
- continue
- fi
- break
+ sleep 1
+ i=$((i+1))
done
DONE=$(cmd otadexopt done)
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/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 07809e2..4da0cd6 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -228,8 +228,13 @@
#endif // !VENDORSERVICEMANAGER
ServiceManager::Service::~Service() {
- if (!hasClients) {
- // only expected to happen on process death
+ if (hasClients) {
+ // only expected to happen on process death, we don't store the service
+ // name this late (it's in the map that holds this service), but if it
+ // is happening, we might want to change 'unlinkToDeath' to explicitly
+ // clear this bit so that we can abort in other cases, where it would
+ // mean inconsistent logic in servicemanager (unexpected and tested, but
+ // the original lazy service impl here had that bug).
LOG(WARNING) << "a service was removed when there are clients";
}
}
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..b520f4f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -237,9 +237,13 @@
std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion);
if (singleThreaded) {
ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
}
if (noKernel) {
ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
}
return ret;
}
@@ -350,7 +354,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) {
@@ -435,8 +439,7 @@
for (auto& t : ts) t.join();
}
-static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls,
- size_t sleepMs = 500) {
+static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) {
size_t epochMsBefore = epochMillis();
std::vector<std::thread> ts;
@@ -462,7 +465,7 @@
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = kNumThreads + 3;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -475,7 +478,7 @@
constexpr size_t kNumCalls = kNumOutgoingConnections + 3;
auto proc = createRpcTestSocketServerProcess(
{.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/);
}
TEST_P(BinderRpc, ThreadingStressTest) {
@@ -483,9 +486,9 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumClientThreads = 10;
- constexpr size_t kNumServerThreads = 10;
- constexpr size_t kNumCalls = 100;
+ constexpr size_t kNumClientThreads = 5;
+ constexpr size_t kNumServerThreads = 5;
+ constexpr size_t kNumCalls = 50;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads});
@@ -544,6 +547,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 +557,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 +578,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/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
index b3bb5eb..63b56a3 100644
--- a/libs/binder/tests/binderRpcTestTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -45,9 +45,13 @@
std::to_string(serverVersion);
if (singleThreaded) {
ret += "_single_threaded";
+ } else {
+ ret += "_multi_threaded";
}
if (noKernel) {
ret += "_no_kernel";
+ } else {
+ ret += "_with_kernel";
}
return ret;
}
@@ -71,7 +75,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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0f138ca..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;
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 29fb989..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 {
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/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/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/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 c22020a..79b1ae3 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -351,14 +351,14 @@
status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
jr_exif_ptr exif,
- bool request_sdr) {
+ 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)) {
@@ -404,7 +404,7 @@
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;
}
@@ -639,6 +639,7 @@
status_t JpegR::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) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_recovery_map == 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.
+ }
}
}
}
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/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp
index df212e1..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,7 +170,7 @@
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) {
@@ -228,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) {
@@ -236,7 +236,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_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());
@@ -289,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) {
@@ -297,7 +297,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_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());
@@ -354,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) {
@@ -362,7 +362,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_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());
@@ -435,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) {
@@ -443,7 +443,7 @@
}
if (SAVE_DECODING_RESULT) {
// Output image data to file
- std::string filePath = "/sdcard/Documents/decoded_from_p010_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/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/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 43cac05..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);
@@ -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/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index cd18cd3..089f45a 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -68,11 +68,11 @@
.free_fn = freeProperty,
};
-bool PropertyProvider::hasProperty(const std::string name) const {
+bool PropertyProvider::hasProperty(const std::string& name) const {
return mProperties.find(name) != mProperties.end();
}
-GesturesProp& PropertyProvider::getProperty(const std::string name) {
+GesturesProp& PropertyProvider::getProperty(const std::string& name) {
return mProperties.at(name);
}
@@ -84,7 +84,7 @@
return dump;
}
-GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, int* loc,
+GesturesProp* PropertyProvider::createIntArrayProperty(const std::string& name, int* loc,
size_t count, const int* init) {
const auto [it, inserted] =
mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
@@ -92,7 +92,7 @@
return &it->second;
}
-GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name,
+GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string& name,
GesturesPropBool* loc, size_t count,
const GesturesPropBool* init) {
const auto [it, inserted] =
@@ -101,7 +101,7 @@
return &it->second;
}
-GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, double* loc,
+GesturesProp* PropertyProvider::createRealArrayProperty(const std::string& name, double* loc,
size_t count, const double* init) {
const auto [it, inserted] =
mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)});
@@ -109,7 +109,7 @@
return &it->second;
}
-GesturesProp* PropertyProvider::createStringProperty(const std::string name, const char** loc,
+GesturesProp* PropertyProvider::createStringProperty(const std::string& name, const char** loc,
const char* const init) {
const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, init)});
LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str());
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
index c21260f..50451a3 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h
@@ -31,18 +31,18 @@
// Implementation of a gestures library property provider, which provides configuration parameters.
class PropertyProvider {
public:
- bool hasProperty(const std::string name) const;
- GesturesProp& getProperty(const std::string name);
+ bool hasProperty(const std::string& name) const;
+ GesturesProp& getProperty(const std::string& name);
std::string dump() const;
// Methods to be called by the gestures library:
- GesturesProp* createIntArrayProperty(const std::string name, int* loc, size_t count,
+ GesturesProp* createIntArrayProperty(const std::string& name, int* loc, size_t count,
const int* init);
- GesturesProp* createBoolArrayProperty(const std::string name, GesturesPropBool* loc,
+ GesturesProp* createBoolArrayProperty(const std::string& name, GesturesPropBool* loc,
size_t count, const GesturesPropBool* init);
- GesturesProp* createRealArrayProperty(const std::string name, double* loc, size_t count,
+ GesturesProp* createRealArrayProperty(const std::string& name, double* loc, size_t count,
const double* init);
- GesturesProp* createStringProperty(const std::string name, const char** loc,
+ GesturesProp* createStringProperty(const std::string& name, const char** loc,
const char* const init);
void freeProperty(GesturesProp* prop);
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/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 5f73fbc..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;
}
}
@@ -510,21 +521,21 @@
mDesiredActiveModeChanged = false;
}
-void DisplayDevice::adjustRefreshRate(Fps leaderDisplayRefreshRate) {
+void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
using fps_approx_ops::operator==;
if (mRequestedRefreshRate == 0_Hz) {
return;
}
using fps_approx_ops::operator>;
- if (mRequestedRefreshRate > leaderDisplayRefreshRate) {
- mAdjustedRefreshRate = leaderDisplayRefreshRate;
+ if (mRequestedRefreshRate > pacesetterDisplayRefreshRate) {
+ mAdjustedRefreshRate = pacesetterDisplayRefreshRate;
return;
}
unsigned divisor = static_cast<unsigned>(
- std::round(leaderDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
- mAdjustedRefreshRate = leaderDisplayRefreshRate / divisor;
+ std::round(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+ mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor;
}
std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b86d9be..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();
@@ -247,9 +248,9 @@
Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
- // Round the requested refresh rate to match a divisor of the leader
+ // Round the requested refresh rate to match a divisor of the pacesetter
// display's refresh rate. Only supported for virtual displays.
- void adjustRefreshRate(Fps leaderDisplayRefreshRate);
+ void adjustRefreshRate(Fps pacesetterDisplayRefreshRate);
// release HWC resources (if any) for removable displays
void disconnect();
@@ -290,7 +291,7 @@
// for virtual displays to match this requested refresh rate.
const Fps mRequestedRefreshRate;
- // Adjusted refresh rate, rounded to match a divisor of the leader
+ // Adjusted refresh rate, rounded to match a divisor of the pacesetter
// display's refresh rate. Only supported for virtual displays.
Fps mAdjustedRefreshRate = 0_Hz;
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/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 14d67c6..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") + " ";
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 0f700a9..3dd89ba 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -144,6 +144,8 @@
}
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 360a0a9..33cc429 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -187,6 +187,10 @@
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;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 85b00d7..8a45093 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -27,12 +27,16 @@
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;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 07aa122..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,7 @@
scheduler::LayerInfo::FrameRate frameRate;
ui::Transform::RotationFlags fixedTransformHint;
bool handleSkipScreenshotFlag = false;
+ int32_t frameRateSelectionPriority;
LayerHierarchy::TraversalPath mirrorRootPath;
bool unreachable = true;
ChildState childState;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index db6e716..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);
}
@@ -773,6 +773,7 @@
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)) {
@@ -954,12 +955,13 @@
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.name = requested.name;
- snapshot.inputInfo.id = static_cast<int32_t>(requested.id);
- snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
- snapshot.inputInfo.ownerPid = requested.ownerPid;
+ 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;
@@ -983,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;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 8b475a3..6f5485d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -53,6 +53,7 @@
VisibleRegion = 1u << 14,
Buffer = 1u << 15,
SidebandStream = 1u << 16,
+ Animation = 1u << 17,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5a4193d..0f2af2f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -196,7 +196,8 @@
mDrawingState.color.b = -1.0_hf;
}
- mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod().ns());
+ mFrameTracker.setDisplayRefreshPeriod(
+ args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
mOwnerUid = args.ownerUid;
mOwnerPid = args.ownerPid;
@@ -680,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;
@@ -1253,7 +1257,7 @@
return parentFrameRate;
}();
- *transactionNeeded |= setFrameRateForLayerTree(frameRate);
+ *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
// The frame rate is propagated to the children
bool childrenHaveFrameRate = false;
@@ -1267,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
@@ -1419,7 +1423,7 @@
return surfaceFrame;
}
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
@@ -1432,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;
}
@@ -2096,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) {
@@ -2114,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();
@@ -3033,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;
@@ -3052,7 +3076,7 @@
} else {
mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
}
-
+ mDrawingState.latchedVsyncId = info.vsyncId;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -3062,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);
@@ -3090,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;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 597f8ad..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);
@@ -608,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
@@ -850,7 +853,20 @@
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
- bool setFrameRateForLayerTree(FrameRate);
+ 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
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 064f853..f18dfdc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -81,7 +81,7 @@
mTouchTimer.reset();
// Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
- demoteLeaderDisplay();
+ demotePacesetterDisplay();
}
void Scheduler::startTimers() {
@@ -106,11 +106,11 @@
}
}
-void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
- demoteLeaderDisplay();
+void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+ demotePacesetterDisplay();
std::scoped_lock lock(mDisplayLock);
- promoteLeaderDisplay(leaderIdOpt);
+ promotePacesetterDisplay(pacesetterIdOpt);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
@@ -121,17 +121,17 @@
void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
RefreshRateSelectorPtr selectorPtr,
std::shared_ptr<VsyncSchedule> vsyncSchedule) {
- demoteLeaderDisplay();
+ demotePacesetterDisplay();
std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
- promoteLeaderDisplay();
+ promotePacesetterDisplay();
}
void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
- demoteLeaderDisplay();
+ demotePacesetterDisplay();
std::scoped_lock lock(mDisplayLock);
mRefreshRateSelectors.erase(displayId);
@@ -142,7 +142,7 @@
// headless virtual display.)
LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
- promoteLeaderDisplay();
+ promotePacesetterDisplay();
}
void Scheduler::run() {
@@ -165,7 +165,7 @@
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
+ pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -192,7 +192,7 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps;
+ const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().fps;
const nsecs_t currentPeriod =
getVsyncSchedule()->period().ns() ?: refreshRate.getPeriodNsecs();
@@ -285,7 +285,7 @@
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
+ pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -326,7 +326,7 @@
// If the mode is not the current mode, this means that a
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
- if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
+ if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
return;
}
@@ -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 (leaderSelectorPtr()->canSwitch()) {
- mLayerHistory.record(layer, presentTime, systemTime(), updateType);
+ if (pacesetterSelectorPtr()->canSwitch()) {
+ mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
}
}
@@ -504,7 +504,7 @@
}
void Scheduler::chooseRefreshRateForContent() {
- const auto selectorPtr = leaderSelectorPtr();
+ const auto selectorPtr = pacesetterSelectorPtr();
if (!selectorPtr->canSwitch()) return;
ATRACE_CALL();
@@ -514,22 +514,22 @@
}
void Scheduler::resetIdleTimer() {
- leaderSelectorPtr()->resetIdleTimer();
+ pacesetterSelectorPtr()->resetIdleTimer();
}
void Scheduler::onTouchHint() {
if (mTouchTimer) {
mTouchTimer->reset();
- leaderSelectorPtr()->resetKernelIdleTimer();
+ pacesetterSelectorPtr()->resetKernelIdleTimer();
}
}
void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) {
- const bool isLeader = [this, id]() REQUIRES(kMainThreadContext) {
+ const bool isPacesetter = [this, id]() REQUIRES(kMainThreadContext) {
ftl::FakeGuard guard(mDisplayLock);
- return id == mLeaderDisplayId;
+ return id == mPacesetterDisplayId;
}();
- if (isLeader) {
+ if (isPacesetter) {
// TODO (b/255657128): This needs to be handled per display.
std::lock_guard<std::mutex> lock(mPolicyLock);
mPolicy.displayPowerMode = powerMode;
@@ -539,7 +539,7 @@
auto vsyncSchedule = getVsyncScheduleLocked(id);
vsyncSchedule->getController().setDisplayPowerMode(powerMode);
}
- if (!isLeader) return;
+ if (!isPacesetter) return;
if (mDisplayPowerTimer) {
mDisplayPowerTimer->reset();
@@ -560,8 +560,8 @@
std::optional<PhysicalDisplayId> idOpt) const {
ftl::FakeGuard guard(kMainThreadContext);
if (!idOpt) {
- LOG_ALWAYS_FATAL_IF(!mLeaderDisplayId, "Missing a leader!");
- idOpt = mLeaderDisplayId;
+ LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!");
+ idOpt = mPacesetterDisplayId;
}
auto scheduleOpt = mVsyncSchedules.get(*idOpt);
LOG_ALWAYS_FATAL_IF(!scheduleOpt);
@@ -573,7 +573,7 @@
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
+ const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getFps();
constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
using namespace fps_approx_ops;
@@ -637,7 +637,7 @@
{
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+ dumper.dump("pacesetterDisplayId"sv, mPacesetterDisplayId);
}
dumper.dump("layerHistory"sv, mLayerHistory.dump());
dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
@@ -651,13 +651,13 @@
void Scheduler::dumpVsync(std::string& out) const {
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- if (mLeaderDisplayId) {
- base::StringAppendF(&out, "VsyncSchedule for leader %s:\n",
- to_string(*mLeaderDisplayId).c_str());
+ if (mPacesetterDisplayId) {
+ base::StringAppendF(&out, "VsyncSchedule for pacesetter %s:\n",
+ to_string(*mPacesetterDisplayId).c_str());
getVsyncScheduleLocked()->dump(out);
}
for (auto& [id, vsyncSchedule] : mVsyncSchedules) {
- if (id == mLeaderDisplayId) {
+ if (id == mPacesetterDisplayId) {
continue;
}
base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str());
@@ -669,31 +669,31 @@
if (consideredSignals.idle) return false;
const auto frameRateOverrides =
- leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
- displayRefreshRate, consideredSignals);
+ pacesetterSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+ displayRefreshRate, consideredSignals);
// Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
// the FrameRateOverrideMappings rather than here.
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
-void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
- // TODO(b/241286431): Choose the leader display.
- mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
- ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+ // TODO(b/241286431): Choose the pacesetter display.
+ mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+ ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
- auto vsyncSchedule = getVsyncScheduleLocked(*mLeaderDisplayId);
- if (const auto leaderPtr = leaderSelectorPtrLocked()) {
- leaderPtr->setIdleTimerCallbacks(
+ auto vsyncSchedule = getVsyncScheduleLocked(*mPacesetterDisplayId);
+ if (const auto pacesetterPtr = pacesetterSelectorPtrLocked()) {
+ pacesetterPtr->setIdleTimerCallbacks(
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
.kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
.onExpired =
[this] { kernelIdleTimerCallback(TimerState::Expired); }}});
- leaderPtr->startIdleTimer();
+ pacesetterPtr->startIdleTimer();
- const Fps refreshRate = leaderPtr->getActiveMode().modePtr->getFps();
+ const Fps refreshRate = pacesetterPtr->getActiveMode().modePtr->getFps();
vsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
true /* force */);
}
@@ -707,14 +707,14 @@
}
}
-void Scheduler::demoteLeaderDisplay() {
+void Scheduler::demotePacesetterDisplay() {
// No need to lock for reads on kMainThreadContext.
- if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
- leaderPtr->stopIdleTimer();
- leaderPtr->clearIdleTimerCallbacks();
+ if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
+ pacesetterPtr->stopIdleTimer();
+ pacesetterPtr->clearIdleTimerCallbacks();
}
- // Clear state that depends on the leader's RefreshRateSelector.
+ // Clear state that depends on the pacesetter's RefreshRateSelector.
std::scoped_lock lock(mPolicyLock);
mPolicy = {};
}
@@ -743,10 +743,11 @@
modeChoices = chooseDisplayModes();
- // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
- // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+ // TODO(b/240743786): The pacesetter display's mode must change for any
+ // DisplayModeRequest to go through. Fix this by tracking per-display Scheduler::Policy
+ // and timers.
std::tie(modeOpt, consideredSignals) =
- modeChoices.get(*mLeaderDisplayId)
+ modeChoices.get(*mPacesetterDisplayId)
.transform([](const DisplayModeChoice& choice) {
return std::make_pair(choice.mode, choice.consideredSignals);
})
@@ -879,7 +880,7 @@
FrameRateMode Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
const auto frameRateMode =
- leaderSelectorPtr()
+ pacesetterSelectorPtr()
->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
.ranking.front()
.frameRateMode;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 7374054..62a5fb2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -102,8 +102,8 @@
void startTimers();
- // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
- void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+ // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
+ void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
EXCLUDES(mDisplayLock);
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -167,9 +167,9 @@
const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; }
- // In some cases, we should only modulate for the leader display. In those
+ // In some cases, we should only modulate for the pacesetter display. In those
// cases, the caller should pass in the relevant display, and the method
- // will no-op if it's not the leader. Other cases are not specific to a
+ // will no-op if it's not the pacesetter. Other cases are not specific to a
// display.
template <typename... Args,
typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)>
@@ -177,13 +177,13 @@
if (id) {
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- if (id != mLeaderDisplayId) {
+ if (id != mPacesetterDisplayId) {
return;
}
}
if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- setVsyncConfig(*config, getLeaderVsyncPeriod());
+ setVsyncConfig(*config, getPacesetterVsyncPeriod());
}
}
@@ -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*);
@@ -254,7 +254,7 @@
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
- // Returns the preferred refresh rate and frame rate for the leader display.
+ // Returns the preferred refresh rate and frame rate for the pacesetter display.
FrameRateMode getPreferredDisplayMode();
// Notifies the scheduler about a refresh rate timeline change.
@@ -277,12 +277,12 @@
// Retrieves the overridden refresh rate for a given uid.
std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
- Period getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
- return leaderSelectorPtr()->getActiveMode().fps.getPeriod();
+ Period getPacesetterVsyncPeriod() const EXCLUDES(mDisplayLock) {
+ return pacesetterSelectorPtr()->getActiveMode().fps.getPeriod();
}
- Fps getLeaderRefreshRate() const EXCLUDES(mDisplayLock) {
- return leaderSelectorPtr()->getActiveMode().fps;
+ Fps getPacesetterRefreshRate() const EXCLUDES(mDisplayLock) {
+ return pacesetterSelectorPtr()->getActiveMode().fps;
}
// Returns the framerate of the layer with the given sequence ID
@@ -318,14 +318,14 @@
void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
- // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
- // `mLeaderDisplayId` is never `std::nullopt`.
- void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+ // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
+ // The new `mPacesetterDisplayId` is never `std::nullopt`.
+ void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
REQUIRES(kMainThreadContext, mDisplayLock);
- // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
- // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
- void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+ // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
+ // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+ void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr,
std::shared_ptr<VsyncSchedule>) REQUIRES(kMainThreadContext)
@@ -415,23 +415,23 @@
display::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<VsyncSchedule>> mVsyncSchedules
GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
- ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+ ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
GUARDED_BY(kMainThreadContext);
- RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+ RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
std::scoped_lock lock(mDisplayLock);
- return leaderSelectorPtrLocked();
+ return pacesetterSelectorPtrLocked();
}
- RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+ RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
ftl::FakeGuard guard(kMainThreadContext);
- const RefreshRateSelectorPtr noLeader;
- return mLeaderDisplayId
- .and_then([this](PhysicalDisplayId leaderId)
+ const RefreshRateSelectorPtr noPacesetter;
+ return mPacesetterDisplayId
+ .and_then([this](PhysicalDisplayId pacesetterId)
REQUIRES(mDisplayLock, kMainThreadContext) {
- return mRefreshRateSelectors.get(leaderId);
+ return mRefreshRateSelectors.get(pacesetterId);
})
- .value_or(std::cref(noLeader));
+ .value_or(std::cref(noPacesetter));
}
std::shared_ptr<const VsyncSchedule> getVsyncScheduleLocked(
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 62e37db..84671ae 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -176,10 +176,16 @@
void VsyncSchedule::disableHardwareVsync(ISchedulerCallback& callback, bool disallow) {
std::lock_guard<std::mutex> lock(mHwVsyncLock);
- if (mHwVsyncState == HwVsyncState::Enabled) {
- callback.setVsyncEnabled(mId, false);
+ switch (mHwVsyncState) {
+ case HwVsyncState::Enabled:
+ callback.setVsyncEnabled(mId, false);
+ [[fallthrough]];
+ case HwVsyncState::Disabled:
+ mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
+ break;
+ case HwVsyncState::Disallowed:
+ break;
}
- mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
}
bool VsyncSchedule::isHardwareVsyncAllowed(bool makeAllowed) {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e45b438..50d4dce 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,9 +474,9 @@
{.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);
+ base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -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;
@@ -2219,7 +2268,7 @@
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
- Changes::Hierarchy)) {
+ Changes::Hierarchy | Changes::Visibility)) {
mUpdateInputInfo = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
@@ -2257,6 +2306,7 @@
}
for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ updateLayerHistory(*snapshot);
if (!snapshot->hasReadyFrame) continue;
newDataLatched = true;
if (!snapshot->isVisible) break;
@@ -2275,12 +2325,13 @@
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;
@@ -3365,7 +3416,7 @@
}
if (display->isVirtual()) {
- display->adjustRefreshRate(mScheduler->getLeaderRefreshRate());
+ display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
}
mDisplays.try_emplace(displayToken, std::move(display));
@@ -3953,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;
@@ -4440,10 +4497,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);
}
}
}
@@ -4904,6 +4965,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)) {
@@ -5012,6 +5077,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)) {
@@ -5025,16 +5094,6 @@
if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
flags |= eTraversalNeeded;
- for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
- if (snapshot->path.isClone() ||
- !snapshot->changes.test(frontend::RequestedLayerState::Changes::FrameRate))
- continue;
- auto it = mLegacyLayers.find(snapshot->sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot->getDebugString().c_str());
- it->second->setFrameRateForLayerTree(snapshot->frameRate);
- }
-
return flags;
}
@@ -5233,8 +5292,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 |
@@ -5246,15 +5315,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);
}
@@ -5693,14 +5764,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;
}
@@ -6782,7 +6866,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);
@@ -6824,7 +6909,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);
@@ -7093,6 +7179,8 @@
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
@@ -7501,10 +7589,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);
}
@@ -7632,15 +7730,15 @@
updateActiveDisplayVsyncLocked(activeDisplay);
mScheduler->setModeChangePending(false);
- mScheduler->setLeaderDisplay(mActiveDisplayId);
+ mScheduler->setPacesetterDisplay(mActiveDisplayId);
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay.getTransformHint();
- // The policy of the new active/leader display may have changed while it was inactive. In that
- // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
- // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
- // and the kernel idle timer of the newly active display must be toggled.
+ // The policy of the new active/pacesetter display may have changed while it was inactive. In
+ // that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
+ // either case, the Scheduler's cachedModeChangedParams must be initialized to the newly active
+ // mode, and the kernel idle timer of the newly active display must be toggled.
constexpr bool kForce = true;
applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(), kForce);
}
@@ -7853,12 +7951,18 @@
}
std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
-SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
- uint32_t 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;
+ bool stopTraversal = false;
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ if (stopTraversal) {
+ return;
+ }
if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
return;
}
@@ -7868,6 +7972,9 @@
if (!snapshot->hasSomethingToDraw()) {
return;
}
+ if (snapshotFilterFn && !snapshotFilterFn(*snapshot, stopTraversal)) {
+ return;
+ }
auto it = mLegacyLayers.find(snapshot->sequence);
LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
@@ -7906,7 +8013,8 @@
.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 094b2cc..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();
@@ -1396,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/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index cfb2032..f27f53b 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -247,7 +247,7 @@
auto &mutableLayerHistory() { return mLayerHistory; }
- auto refreshRateSelector() { return leaderSelectorPtr(); }
+ auto refreshRateSelector() { return pacesetterSelectorPtr(); }
void replaceTouchTimer(int64_t millis) {
if (mTouchTimer) {
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/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 eac9edc..012a4ad 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -103,9 +103,11 @@
"SurfaceFlinger_DestroyDisplayTest.cpp",
"SurfaceFlinger_DisplayModeSwitching.cpp",
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
- "SurfaceFlinger_MultiDisplayLeaderTest.cpp",
+ "SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
"SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_OnInitializeDisplaysTest.cpp",
"SurfaceFlinger_PowerHintTest.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/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/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/SurfaceFlinger_MultiDisplayLeaderTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
similarity index 63%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
index 9c58943..e38f56e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayLeaderTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
@@ -25,12 +25,12 @@
namespace android {
namespace {
-struct MultiDisplayLeaderTest : DisplayTransactionTest {
+struct MultiDisplayPacesetterTest : DisplayTransactionTest {
static constexpr bool kWithMockScheduler = false;
- MultiDisplayLeaderTest() : DisplayTransactionTest(kWithMockScheduler) {}
+ MultiDisplayPacesetterTest() : DisplayTransactionTest(kWithMockScheduler) {}
};
-TEST_F(MultiDisplayLeaderTest, foldable) {
+TEST_F(MultiDisplayPacesetterTest, foldable) {
injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get());
// Inject inner and outer displays with uninitialized power modes.
@@ -50,31 +50,31 @@
outerDisplay = injector.inject();
}
- // When the device boots, the inner display should be the leader.
- ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+ // When the device boots, the inner display should be the pacesetter.
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
// ...and should still be after powering on.
mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
- ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
- // The outer display should become the leader after folding.
+ // The outer display should become the pacesetter after folding.
mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
- ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), outerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
- // The inner display should become the leader after unfolding.
+ // The inner display should become the pacesetter after unfolding.
mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
- ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
- // The inner display should stay the leader if both are powered on.
- // TODO(b/256196556): The leader should depend on the displays' VSYNC phases.
+ // The inner display should stay the pacesetter if both are powered on.
+ // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
- ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), innerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
- // The outer display should become the leader if designated.
- mFlinger.scheduler()->setLeaderDisplay(outerDisplay->getPhysicalId());
- ASSERT_EQ(mFlinger.scheduler()->leaderDisplayId(), outerDisplay->getPhysicalId());
+ // The outer display should become the pacesetter if designated.
+ mFlinger.scheduler()->setPacesetterDisplay(outerDisplay->getPhysicalId());
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ac04720..d4b4434 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -63,7 +63,7 @@
return Scheduler::createConnection(std::move(eventThread));
}
- auto refreshRateSelector() { return leaderSelectorPtr(); }
+ auto refreshRateSelector() { return pacesetterSelectorPtr(); }
const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
return mRefreshRateSelectors;
@@ -92,13 +92,13 @@
Scheduler::unregisterDisplay(displayId);
}
- std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
- return mLeaderDisplayId;
+ std::optional<PhysicalDisplayId> pacesetterDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+ return mPacesetterDisplayId;
}
- void setLeaderDisplay(PhysicalDisplayId displayId) {
+ void setPacesetterDisplay(PhysicalDisplayId displayId) {
ftl::FakeGuard guard(kMainThreadContext);
- Scheduler::setLeaderDisplay(displayId);
+ Scheduler::setPacesetterDisplay(displayId);
}
auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
index adf0804..4010fa6 100644
--- a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -86,6 +86,12 @@
mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
}
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed2) {
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
TEST_F(VsyncScheduleTest, MakeAllowed) {
ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
}
@@ -97,6 +103,13 @@
mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
}
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled2) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) {
ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
@@ -129,6 +142,16 @@
mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
}
+TEST_F(VsyncScheduleTest, EnableDisable2) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+ mVsyncSchedule->enableHardwareVsync(mCallback);
+
+ EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
TEST_F(VsyncScheduleTest, StartPeriodTransition) {
// Note: startPeriodTransition is only called when hardware vsyncs are
// allowed.
@@ -225,5 +248,23 @@
ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
}
+TEST_F(VsyncScheduleTest, DisableDoesNotMakeAllowed) {
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisallowMakesNotAllowed) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+ ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, StillAllowedAfterDisable) {
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+ mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+ ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
} // namespace
} // 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