Merge "nativewindow: Misc. improvements for AHardwareBuffer Rust wrapper" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 376d57a..33f6d38 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -187,6 +187,7 @@
#define CGROUPFS_DIR "/sys/fs/cgroup"
#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
#define DROPBOX_DIR "/data/system/dropbox"
+#define PRINT_FLAGS "/system/bin/printflags"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -1763,14 +1764,8 @@
DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json");
DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json");
- DumpFile("SYSTEM BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/system/etc/aconfig_flags.textproto");
- DumpFile("SYSTEM_EXT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime"
- " values)", "/system_ext/etc/aconfig_flags.textproto");
- DumpFile("PRODUCT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/product/etc/aconfig_flags.textproto");
- DumpFile("VENDOR BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/vendor/etc/aconfig_flags.textproto");
+ RunCommand("ACONFIG FLAGS", {PRINT_FLAGS},
+ CommandOptions::WithTimeout(10).Always().DropRoot().Build());
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4081514..a401838 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
@@ -117,10 +118,26 @@
});
if (!found) {
+ std::set<std::string> instances;
+ forEachManifest([&](const ManifestWithDescription& mwd) {
+ std::set<std::string> res = mwd.manifest->getAidlInstances(aname.package, aname.iface);
+ instances.insert(res.begin(), res.end());
+ return true;
+ });
+
+ std::string available;
+ if (instances.empty()) {
+ available = "No alternative instances declared in VINTF";
+ } else {
+ // for logging only. We can't return this information to the client
+ // because they may not have permissions to find or list those
+ // instances
+ available = "VINTF declared instances: " + base::Join(instances, ", ");
+ }
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- ALOGI("Could not find %s.%s/%s in the VINTF manifest.", aname.package.c_str(),
- aname.iface.c_str(), aname.instance.c_str());
+ ALOGI("Could not find %s.%s/%s in the VINTF manifest. %s.", aname.package.c_str(),
+ aname.iface.c_str(), aname.instance.c_str(), available.c_str());
}
return found;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 39bbac3..1418e1f 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -83,6 +83,12 @@
}
prebuilt_etc {
+ name: "android.hardware.consumerir.prebuilt.xml",
+ src: "android.hardware.consumerir.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.ethernet.prebuilt.xml",
src: "android.hardware.ethernet.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 645fa8a..1d513a6 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -53,27 +53,27 @@
/**
* Create a shared memory region.
*
- * Create shared memory region and returns a file descriptor. The resulting file descriptor can be
- * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory
- * region can be restricted with {@link ASharedMemory_setProt}.
+ * Create a shared memory region and returns a file descriptor. The resulting file descriptor can be
+ * mapped into the process' memory using mmap(2) with `PROT_READ | PROT_WRITE | PROT_EXEC`.
+ * Access to shared memory regions can be restricted with {@link ASharedMemory_setProt}.
*
- * Use close() to release the shared memory region.
+ * Use close(2) to release the shared memory region.
*
* Use <a href="/reference/android/os/ParcelFileDescriptor">android.os.ParcelFileDescriptor</a>
* to pass the file descriptor to another process. File descriptors may also be sent to other
- * processes over a Unix domain socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and
+ * processes over a Unix domain socket with sendmsg(2) and `SCM_RIGHTS`. See sendmsg(3) and
* cmsg(3) man pages for more information.
*
* If you intend to share this file descriptor with a child process after
- * calling exec(3), note that you will need to use fcntl(2) with F_SETFD
- * to clear the FD_CLOEXEC flag for this to work on all versions of Android.
+ * calling exec(3), note that you will need to use fcntl(2) with `F_SETFD`
+ * to clear the `FD_CLOEXEC` flag for this to work on all versions of Android.
*
* Available since API level 26.
*
* \param name an optional name.
* \param size size of the shared memory region
* \return file descriptor that denotes the shared memory;
- * -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
+ * -1 and sets `errno` on failure, or `-EINVAL` if the error is that size was 0.
*/
int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
@@ -83,7 +83,7 @@
* Available since API level 26.
*
* \param fd file descriptor of the shared memory region
- * \return size in bytes; 0 if fd is not a valid shared memory file descriptor.
+ * \return size in bytes; 0 if `fd` is not a valid shared memory file descriptor.
*/
size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26);
@@ -115,9 +115,9 @@
* Available since API level 26.
*
* \param fd file descriptor of the shared memory region.
- * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
+ * \param prot any bitwise-or'ed combination of `PROT_READ`, `PROT_WRITE`, `PROT_EXEC` denoting
* updated access. Note access can only be removed, but not added back.
- * \return 0 for success, -1 and sets errno on failure.
+ * \return 0 for success, -1 and sets `errno` on failure.
*/
int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26);
diff --git a/include/input/Input.h b/include/input/Input.h
index 64ee473..567361d 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -285,6 +285,16 @@
// This policy flag prevents key events from changing touch mode state.
POLICY_FLAG_GESTURE = 0x00000008,
+ // Indicates that key usage mapping represents a fallback mapping.
+ // Fallback mappings cannot be used to definitively determine whether a device
+ // supports a key code. For example, a HID device can report a key press
+ // as a HID usage code if it is not mapped to any linux key code in the kernel.
+ // However, we cannot know which HID usage codes that device supports from
+ // userspace through the evdev. We can use fallback mappings to convert HID
+ // usage codes to Android key codes without needing to know if a device can
+ // actually report the usage code.
+ POLICY_FLAG_FALLBACK_USAGE_MAPPING = 0x00000010,
+
POLICY_FLAG_RAW_MASK = 0x0000ffff,
#ifdef __linux__
diff --git a/libs/binder/RpcTlsUtils.cpp b/libs/binder/RpcTlsUtils.cpp
index f3ca02a..d5c86d7 100644
--- a/libs/binder/RpcTlsUtils.cpp
+++ b/libs/binder/RpcTlsUtils.cpp
@@ -21,6 +21,8 @@
#include "Utils.h"
+#include <limits>
+
namespace android {
namespace {
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index f3575cc..ddbcb57 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -79,7 +79,7 @@
altPoll);
}
- virtual bool isWaiting() { return mSocket.isInPollingState(); }
+ bool isWaiting() override { return mSocket.isInPollingState(); }
private:
android::RpcTransportFd mSocket;
@@ -88,7 +88,8 @@
// RpcTransportCtx with TLS disabled.
class RpcTransportCtxRaw : public RpcTransportCtx {
public:
- std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, FdTrigger*) const {
+ std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket,
+ FdTrigger*) const override {
return std::make_unique<RpcTransportRaw>(std::move(socket));
}
std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 785f6ce..efb09e9 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -292,7 +292,7 @@
const std::optional<android::base::function_ref<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
- bool isWaiting() { return mSocket.isInPollingState(); };
+ bool isWaiting() override { return mSocket.isInPollingState(); };
private:
android::RpcTransportFd mSocket;
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 2d05fb2..c432b3a 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -75,7 +75,7 @@
Stability::Level Stability::getLocalLevel() {
#ifdef __ANDROID_APEX__
-#error APEX can't use libbinder (must use libbinder_ndk)
+#error "APEX can't use libbinder (must use libbinder_ndk)"
#endif
#ifdef __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index a5c6094..28fb9f1 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -21,6 +21,7 @@
#include <utils/Mutex.h>
#include <map>
+#include <optional>
#include <unordered_map>
#include <variant>
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 4e231ed..45e5ace 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -17,7 +17,9 @@
#pragma once
#include <array>
+#include <limits>
#include <map> // for legacy reasons
+#include <optional>
#include <string>
#include <type_traits>
#include <variant>
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index b804f7b..2153f16 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -23,6 +23,7 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <bitset>
#include <mutex>
#include <thread>
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index 8abf04e..b80d116 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -19,6 +19,7 @@
#include <android-base/threads.h>
+#include <condition_variable>
#include <functional>
#include <memory>
#include <thread>
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index ed53891..18769b1 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -31,6 +31,7 @@
#include <android/binder_parcel.h>
#include <android/binder_status.h>
#include <assert.h>
+#include <string.h>
#include <unistd.h>
#include <cstddef>
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 692cc95..8e95d69 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -59,12 +59,13 @@
auto basename = Basename(program);
auto format = R"(dispatch calls to RPC service.
Usage:
- %s [-g] <service_name>
+ %s [-g] [-i <ip_address>] <service_name>
<service_name>: the service to connect to.
%s [-g] manager
Runs an RPC-friendly service that redirects calls to servicemanager.
-g: use getService() instead of checkService().
+ -i: use ip_address when setting up the server instead of '127.0.0.1'
If successful, writes port number and a new line character to stdout, and
blocks until killed.
@@ -74,7 +75,8 @@
return EX_USAGE;
}
-int Dispatch(const char* name, const ServiceRetriever& serviceRetriever) {
+int Dispatch(const char* name, const ServiceRetriever& serviceRetriever,
+ const char* ip_address = kLocalInetAddress) {
auto sm = defaultServiceManager();
if (nullptr == sm) {
LOG(ERROR) << "No servicemanager";
@@ -91,7 +93,7 @@
return EX_SOFTWARE;
}
unsigned int port;
- if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
+ if (status_t status = rpcServer->setupInetServer(ip_address, 0, &port); status != OK) {
LOG(ERROR) << "setupInetServer failed: " << statusToString(status);
return EX_SOFTWARE;
}
@@ -188,7 +190,8 @@
// Workaround for b/191059588.
// TODO(b/191059588): Once we can run RpcServer on single-threaded services,
// `servicedispatcher manager` should call Dispatch("manager") directly.
-int wrapServiceManager(const ServiceRetriever& serviceRetriever) {
+int wrapServiceManager(const ServiceRetriever& serviceRetriever,
+ const char* ip_address = kLocalInetAddress) {
auto sm = defaultServiceManager();
if (nullptr == sm) {
LOG(ERROR) << "No servicemanager";
@@ -212,7 +215,7 @@
auto rpcServer = RpcServer::make();
rpcServer->setRootObject(service);
unsigned int port;
- if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
+ if (status_t status = rpcServer->setupInetServer(ip_address, 0, &port); status != OK) {
LOG(ERROR) << "Unable to set up inet server: " << statusToString(status);
return EX_SOFTWARE;
}
@@ -272,11 +275,15 @@
int opt;
ServiceRetriever serviceRetriever = &android::IServiceManager::checkService;
- while (-1 != (opt = getopt(argc, argv, "g"))) {
+ char* ip_address = nullptr;
+ while (-1 != (opt = getopt(argc, argv, "gi:"))) {
switch (opt) {
case 'g': {
serviceRetriever = &android::IServiceManager::getService;
} break;
+ case 'i': {
+ ip_address = optarg;
+ } break;
default: {
return Usage(argv[0]);
}
@@ -291,7 +298,15 @@
auto name = argv[optind];
if (name == "manager"sv) {
- return wrapServiceManager(serviceRetriever);
+ if (ip_address) {
+ return wrapServiceManager(serviceRetriever, ip_address);
+ } else {
+ return wrapServiceManager(serviceRetriever);
+ }
}
- return Dispatch(name, serviceRetriever);
+ if (ip_address) {
+ return Dispatch(name, serviceRetriever, ip_address);
+ } else {
+ return Dispatch(name, serviceRetriever);
+ }
}
diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
index e4dbb2d..7390d49 100644
--- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp
@@ -29,40 +29,65 @@
const char* fdType;
std::vector<unique_fd> fds = provider->PickValueInArray<
- std::function<std::vector<unique_fd>()>>({
- [&]() {
- fdType = "ashmem";
- std::vector<unique_fd> ret;
- ret.push_back(unique_fd(
- ashmem_create_region("binder test region",
- provider->ConsumeIntegralInRange<size_t>(0, 4096))));
- return ret;
- },
- [&]() {
- fdType = "/dev/null";
- std::vector<unique_fd> ret;
- ret.push_back(unique_fd(open("/dev/null", O_RDWR)));
- return ret;
- },
- [&]() {
- fdType = "pipefd";
+ std::function<std::vector<unique_fd>()>>(
+ {[&]() {
+ fdType = "ashmem";
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(
+ ashmem_create_region("binder test region",
+ provider->ConsumeIntegralInRange<size_t>(0, 4096))));
+ return ret;
+ },
+ [&]() {
+ fdType = "/dev/null";
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(open("/dev/null", O_RDWR)));
+ return ret;
+ },
+ [&]() {
+ fdType = "pipefd";
- int pipefds[2];
+ int pipefds[2];
- int flags = O_CLOEXEC;
- if (provider->ConsumeBool()) flags |= O_DIRECT;
- if (provider->ConsumeBool()) flags |= O_NONBLOCK;
+ int flags = O_CLOEXEC;
+ if (provider->ConsumeBool()) flags |= O_DIRECT;
+ if (provider->ConsumeBool()) flags |= O_NONBLOCK;
- CHECK_EQ(0, pipe2(pipefds, flags)) << flags;
+ CHECK_EQ(0, pipe2(pipefds, flags)) << flags;
- if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]);
+ if (provider->ConsumeBool()) std::swap(pipefds[0], pipefds[1]);
- std::vector<unique_fd> ret;
- ret.push_back(unique_fd(pipefds[0]));
- ret.push_back(unique_fd(pipefds[1]));
- return ret;
- },
- })();
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(pipefds[0]));
+ ret.push_back(unique_fd(pipefds[1]));
+ return ret;
+ },
+ [&]() {
+ fdType = "tempfd";
+ char name[PATH_MAX];
+#if defined(__ANDROID__)
+ snprintf(name, sizeof(name), "/data/local/tmp/android-tempfd-test-%d-XXXXXX",
+ getpid());
+#else
+ snprintf(name, sizeof(name), "/tmp/android-tempfd-test-%d-XXXXXX", getpid());
+#endif
+ int fd = mkstemp(name);
+ CHECK_NE(fd, -1) << "Failed to create file " << name << ", errno: " << errno;
+ unlink(name);
+ if (provider->ConsumeBool()) {
+ CHECK_NE(TEMP_FAILURE_RETRY(
+ ftruncate(fd,
+ provider->ConsumeIntegralInRange<size_t>(0, 4096))),
+ -1)
+ << "Failed to truncate file, errno: " << errno;
+ }
+
+ std::vector<unique_fd> ret;
+ ret.push_back(unique_fd(fd));
+ return ret;
+ }
+
+ })();
for (const auto& fd : fds) CHECK(fd.ok()) << fd.get() << " " << fdType;
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 6d1dfe8..aab1276 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -58,7 +58,7 @@
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V4-ndk",
+ "android.hardware.graphics.common-V5-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 61e6657..ce35906 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1307,6 +1307,10 @@
return "SitedInterstitial";
case ChromaSiting::COSITED_HORIZONTAL:
return "CositedHorizontal";
+ case ChromaSiting::COSITED_VERTICAL:
+ return "CositedVertical";
+ case ChromaSiting::COSITED_BOTH:
+ return "CositedBoth";
}
}
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 1c0439e..5dc195c 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -64,9 +64,17 @@
void setTargetStatsArray(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t* values,
const uint32_t valueCount) override {
- for (uint32_t i = 0; i < valueCount; i++) {
- setTargetStats(appPackageName, driverVersionCode, stats, values[i]);
- }
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ data.writeUtf8AsUtf16(appPackageName);
+ data.writeUint64(driverVersionCode);
+ data.writeInt32(static_cast<int32_t>(stats));
+ data.writeUint32(valueCount);
+ data.write(values, valueCount * sizeof(uint64_t));
+
+ remote()->transact(BnGpuService::SET_TARGET_STATS_ARRAY, data, &reply,
+ IBinder::FLAG_ONEWAY);
}
void setUpdatableDriverPath(const std::string& driverPath) override {
@@ -164,6 +172,31 @@
return OK;
}
+ case SET_TARGET_STATS_ARRAY: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string appPackageName;
+ if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status;
+
+ uint64_t driverVersionCode;
+ if ((status = data.readUint64(&driverVersionCode)) != OK) return status;
+
+ int32_t stats;
+ if ((status = data.readInt32(&stats)) != OK) return status;
+
+ uint32_t valueCount;
+ if ((status = data.readUint32(&valueCount)) != OK) return status;
+
+ std::vector<uint64_t> values(valueCount);
+ if ((status = data.read(values.data(), valueCount * sizeof(uint64_t))) != OK) {
+ return status;
+ }
+
+ setTargetStatsArray(appPackageName, driverVersionCode,
+ static_cast<GpuStatsInfo::Stats>(stats), values.data(), valueCount);
+
+ return OK;
+ }
case SET_UPDATABLE_DRIVER_PATH: {
CHECK_INTERFACE(IGpuService, data, reply);
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index e3857d2..45f05d6 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -63,6 +63,7 @@
SET_UPDATABLE_DRIVER_PATH,
GET_UPDATABLE_DRIVER_PATH,
TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
+ SET_TARGET_STATS_ARRAY,
// Always append new enum to the end.
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9a27d23..f17a654 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -200,6 +200,7 @@
cc_aconfig_library {
name: "libguiflags",
+ host_supported: true,
vendor_available: true,
aconfig_declarations: "libgui_flags",
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index b526a6c..ff6b558 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -25,6 +25,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
+#include <gui/SchedulingPolicy.h>
#include <private/gui/ParcelUtils.h>
#include <stdint.h>
#include <sys/types.h>
@@ -201,6 +202,18 @@
isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
listenerCallbacks, transactionId, mergedTransactions);
}
+ case GET_SCHEDULING_POLICY: {
+ gui::SchedulingPolicy policy;
+ const auto status = gui::getSchedulingPolicy(&policy);
+ if (!status.isOk()) {
+ return status.exceptionCode();
+ }
+
+ SAFE_PARCEL(reply->writeInt32, policy.policy);
+ SAFE_PARCEL(reply->writeInt32, policy.priority);
+ return NO_ERROR;
+ }
+
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 038764b..8a57f92 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1227,7 +1227,7 @@
flags |= ISurfaceComposer::eEarlyWakeupEnd;
}
- sp<IBinder> applyToken = mApplyToken ? mApplyToken : sApplyToken;
+ sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
@@ -1249,11 +1249,15 @@
sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder();
+std::mutex SurfaceComposerClient::Transaction::sApplyTokenMutex;
+
sp<IBinder> SurfaceComposerClient::Transaction::getDefaultApplyToken() {
+ std::scoped_lock lock{sApplyTokenMutex};
return sApplyToken;
}
void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) {
+ std::scoped_lock lock{sApplyTokenMutex};
sApplyToken = applyToken;
}
diff --git a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
index 9781ca9..008ef19 100644
--- a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -18,6 +18,7 @@
import android.gui.BitTube;
import android.gui.ParcelableVsyncEventData;
+import android.gui.SchedulingPolicy;
/** @hide */
interface IDisplayEventConnection {
@@ -44,4 +45,9 @@
* getLatestVsyncEventData() gets the latest vsync event data.
*/
ParcelableVsyncEventData getLatestVsyncEventData();
+
+ /*
+ * getSchedulingPolicy() used in tests to validate the binder thread pririty
+ */
+ SchedulingPolicy getSchedulingPolicy();
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 1c604a1..507e086 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -46,6 +46,7 @@
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
+import android.gui.SchedulingPolicy;
import android.gui.StalledTransactionInfo;
import android.gui.StaticDisplayInfo;
import android.gui.WindowInfosListenerInfo;
@@ -545,4 +546,6 @@
* applied in SurfaceFlinger due to an unsignaled fence. Otherwise, null is returned.
*/
@nullable StalledTransactionInfo getStalledTransactionInfo(int pid);
+
+ SchedulingPolicy getSchedulingPolicy();
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
index 68781ce..920257c 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
@@ -19,6 +19,7 @@
import android.gui.CreateSurfaceResult;
import android.gui.FrameStats;
import android.gui.LayerMetadata;
+import android.gui.SchedulingPolicy;
/** @hide */
interface ISurfaceComposerClient {
@@ -60,4 +61,6 @@
CreateSurfaceResult mirrorSurface(IBinder mirrorFromHandle);
CreateSurfaceResult mirrorDisplay(long displayId);
+
+ SchedulingPolicy getSchedulingPolicy();
}
diff --git a/libs/gui/aidl/android/gui/SchedulingPolicy.aidl b/libs/gui/aidl/android/gui/SchedulingPolicy.aidl
new file mode 100644
index 0000000..4f7cf0a
--- /dev/null
+++ b/libs/gui/aidl/android/gui/SchedulingPolicy.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+/** @hide */
+parcelable SchedulingPolicy {
+ int policy;
+ int priority;
+}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index bdf8856..3142103 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -166,6 +166,7 @@
MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
MOCK_METHOD(binder::Status, getStalledTransactionInfo,
(int32_t, std::optional<gui::StalledTransactionInfo>*), (override));
+ MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override));
};
class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
@@ -186,6 +187,8 @@
MOCK_METHOD(binder::Status, mirrorDisplay,
(int64_t displayId, gui::CreateSurfaceResult* outResult), (override));
+
+ MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override));
};
class FakeDisplayEventDispatcher : public DisplayEventDispatcher {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3ff6735..a836f46 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -199,6 +199,7 @@
SET_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now.
CLEAR_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now.
SET_OVERRIDE_FRAME_RATE, // Deprecated. Autogenerated by .aidl now.
+ GET_SCHEDULING_POLICY,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SchedulingPolicy.h b/libs/gui/include/gui/SchedulingPolicy.h
new file mode 100644
index 0000000..48c9f0c
--- /dev/null
+++ b/libs/gui/include/gui/SchedulingPolicy.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sched.h>
+
+#include <android/gui/SchedulingPolicy.h>
+#include <binder/Status.h>
+
+namespace android::gui {
+
+static binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ outPolicy->policy = sched_getscheduler(0);
+ if (outPolicy->policy < 0) {
+ return binder::Status::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ struct sched_param param;
+ if (sched_getparam(0, ¶m) < 0) {
+ return binder::Status::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ outPolicy->priority = param.sched_priority;
+ return binder::Status::ok();
+}
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 42e3c16..26b1fbd 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -422,6 +422,7 @@
class Transaction : public Parcelable {
private:
static sp<IBinder> sApplyToken;
+ static std::mutex sApplyTokenMutex;
void releaseBufferIfOverwriting(const layer_state_t& state);
static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9618502..9893c71 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -31,10 +31,13 @@
#include <gui/test/CallbackUtils.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
+#include <tests/utils/ScreenshotUtils.h>
#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
#include <ui/Transform.h>
#include <gtest/gtest.h>
@@ -197,18 +200,23 @@
ALOGD("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight,
displayState.orientation);
+ mRootSurfaceControl = mClient->createSurface(String8("RootTestSurface"), mDisplayWidth,
+ mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+
+ t.setLayerStack(mRootSurfaceControl, ui::DEFAULT_LAYER_STACK)
+ .setLayer(mRootSurfaceControl, std::numeric_limits<int32_t>::max())
+ .show(mRootSurfaceControl)
+ .apply();
+
mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
- /*parent*/ nullptr);
- t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
- .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .show(mSurfaceControl)
- .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
- .apply();
+ /*parent*/ mRootSurfaceControl->getHandle());
- mCaptureArgs.displayToken = mDisplayToken;
- mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ mCaptureArgs.sourceCrop = Rect(ui::Size(mDisplayWidth, mDisplayHeight));
+ mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer,
@@ -295,21 +303,6 @@
captureBuf->unlock();
}
- static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
- const auto sf = ComposerServiceAIDL::getComposerService();
- SurfaceComposerClient::Transaction().apply(true);
-
- const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- binder::Status status = sf->captureDisplay(captureArgs, captureListener);
- status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
- if (err != NO_ERROR) {
- return err;
- }
- captureResults = captureListener->waitForResults();
- return fenceStatus(captureResults.fenceResult);
- }
-
void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
nsecs_t presentTimeDelay) {
int slot;
@@ -342,11 +335,12 @@
sp<IBinder> mDisplayToken;
sp<SurfaceControl> mSurfaceControl;
+ sp<SurfaceControl> mRootSurfaceControl;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
- DisplayCaptureArgs mCaptureArgs;
+ LayerCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
sp<CountProducerListener> mProducerListener;
};
@@ -364,7 +358,9 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<SurfaceControl> updateSurface =
mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2,
- PIXEL_FORMAT_RGBA_8888);
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ mRootSurfaceControl->getHandle());
adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
sp<IGraphicBufferProducer> igbProducer;
@@ -451,7 +447,7 @@
Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -536,7 +532,7 @@
Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -552,16 +548,6 @@
(mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
int32_t finalCropSideLength = bufferSideLength / 2;
- auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect);
- ASSERT_NE(nullptr, bg.get());
- Transaction t;
- t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
- .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setColor(bg, half3{0, 0, 0})
- .setLayer(bg, 0)
- .apply();
-
BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
@@ -597,7 +583,7 @@
Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
{10, 10, (int32_t)bufferSideLength - 10,
(int32_t)bufferSideLength - 10}));
@@ -608,17 +594,6 @@
}
TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
- // add black background
- auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect);
- ASSERT_NE(nullptr, bg.get());
- Transaction t;
- t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
- .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setColor(bg, half3{0, 0, 0})
- .setLayer(bg, 0)
- .apply();
-
Rect windowSize(1000, 1000);
Rect bufferSize(windowSize);
Rect bufferCrop(200, 200, 700, 700);
@@ -661,7 +636,7 @@
// ensure the buffer queue transaction has been committed
Transaction().apply(true /* synchronous */);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
@@ -676,17 +651,6 @@
}
TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
- // add black background
- auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect);
- ASSERT_NE(nullptr, bg.get());
- Transaction t;
- t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
- .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setColor(bg, half3{0, 0, 0})
- .setLayer(bg, 0)
- .apply();
-
Rect windowSize(1000, 1000);
Rect bufferSize(500, 500);
Rect bufferCrop(100, 100, 350, 350);
@@ -729,7 +693,7 @@
// ensure the buffer queue transaction has been committed
Transaction().apply(true /* synchronous */);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
@@ -779,7 +743,7 @@
Transaction().apply(true /* synchronous */);
}
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -815,7 +779,7 @@
Transaction().apply(true /* synchronous */);
}
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
// verify we still scale the buffer to the new size (half the screen height)
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -850,12 +814,12 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is green
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
mProducerListener->waitOnNumberReleased(1);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -895,7 +859,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -943,7 +907,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -993,7 +957,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -1051,7 +1015,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -1084,13 +1048,13 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is blue
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
mProducerListener->waitOnNumberReleased(2);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -1186,7 +1150,7 @@
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
sync.apply();
@@ -1205,7 +1169,7 @@
mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
- /*parent*/ nullptr);
+ /*parent*/ mRootSurfaceControl->getHandle());
Transaction()
.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
@@ -1230,7 +1194,7 @@
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(255, 0, 0,
{0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -1248,7 +1212,7 @@
mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
- /*parent*/ nullptr);
+ /*parent*/ mRootSurfaceControl->getHandle());
Transaction()
.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
@@ -1273,7 +1237,7 @@
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(255, 0, 0,
{0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -1406,7 +1370,7 @@
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
Transaction().apply(true /* synchronous */);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
switch (tr) {
case ui::Transform::ROT_0:
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index daed764..9eee699 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1036,6 +1036,10 @@
return binder::Status::ok();
}
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy*) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index c218e1e..0e627e5 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -430,7 +430,8 @@
DEFINE_FLAG(VIRTUAL), \
DEFINE_FLAG(FUNCTION), \
DEFINE_FLAG(GESTURE), \
- DEFINE_FLAG(WAKE)
+ DEFINE_FLAG(WAKE), \
+ DEFINE_FLAG(FALLBACK_USAGE_MAPPING)
// clang-format on
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index b5a823d..30cedb0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1276,13 +1276,13 @@
PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
const PointerCoords& currentCoords = current->getPointerById(id);
resampledCoords = currentCoords;
+ resampledCoords.isResampled = true;
if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
const PointerCoords& otherCoords = other->getPointerById(id);
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
lerp(currentCoords.getX(), otherCoords.getX(), alpha));
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
lerp(currentCoords.getY(), otherCoords.getY(), alpha));
- resampledCoords.isResampled = true;
ALOGD_IF(debugResampling(),
"[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
"other (%0.3f, %0.3f), alpha %0.3f",
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index ddc9ea4..3c1ae3e 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -250,7 +250,7 @@
std::vector<int32_t> KeyLayoutMap::findUsageCodesForKey(int32_t keyCode) const {
std::vector<int32_t> usageCodes;
for (const auto& [usageCode, key] : mKeysByUsageCode) {
- if (keyCode == key.keyCode) {
+ if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)) {
usageCodes.push_back(usageCode);
}
}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index a0563f9..6302ff5 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -27,3 +27,17 @@
description: "Set to true to enable timer support for the touchpad Gestures library"
bug: "297192727"
}
+
+flag {
+ name: "enable_multi_device_input"
+ namespace: "input"
+ description: "Set to true to enable multi-device input: touch and stylus can be active at the same time, but in different windows"
+ bug: "211379801"
+}
+
+flag {
+ name: "a11y_crash_on_inconsistent_event_stream"
+ namespace: "accessibility"
+ description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
+ bug: "299977100"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 9725b00..804f96d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -35,7 +35,20 @@
bitflags! {
/// Source of the input device or input events.
+ #[derive(Debug, PartialEq)]
pub struct Source: u32 {
+ // Constants from SourceClass, added here for compatibility reasons
+ /// SourceClass::Button
+ const SourceClassButton = SourceClass::Button as u32;
+ /// SourceClass::Pointer
+ const SourceClassPointer = SourceClass::Pointer as u32;
+ /// SourceClass::Navigation
+ const SourceClassNavigation = SourceClass::Navigation as u32;
+ /// SourceClass::Position
+ const SourceClassPosition = SourceClass::Position as u32;
+ /// SourceClass::Joystick
+ const SourceClassJoystick = SourceClass::Joystick as u32;
+
/// SOURCE_UNKNOWN
const Unknown = input_bindgen::AINPUT_SOURCE_UNKNOWN;
/// SOURCE_KEYBOARD
@@ -190,3 +203,14 @@
self.bits() & class_bits == class_bits
}
}
+
+#[cfg(test)]
+mod tests {
+ use crate::input::SourceClass;
+ use crate::Source;
+ #[test]
+ fn convert_source_class_pointer() {
+ let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap();
+ assert!(source.is_from_class(SourceClass::Pointer));
+ }
+}
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index a0ec6ad..fe5490c 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -167,6 +167,41 @@
ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
}
+TEST(InputDeviceKeyLayoutTest, HidUsageCodesFallbackMapping) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/hid_fallback_mapping.kl";
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_TRUE(ret.ok()) << "Unable to load KeyLayout at " << klPath;
+ const std::shared_ptr<KeyLayoutMap>& keyLayoutMap = *ret;
+
+ static constexpr std::array<int32_t, 5> hidUsageCodesWithoutFallback = {0x0c0067, 0x0c0070,
+ 0x0c006F, 0x0c0079,
+ 0x0c007A};
+ for (int32_t hidUsageCode : hidUsageCodesWithoutFallback) {
+ int32_t outKeyCode;
+ uint32_t outFlags;
+ keyLayoutMap->mapKey(0, hidUsageCode, &outKeyCode, &outFlags);
+ ASSERT_FALSE(outFlags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)
+ << "HID usage code should not be marked as fallback";
+ std::vector<int32_t> usageCodes = keyLayoutMap->findUsageCodesForKey(outKeyCode);
+ ASSERT_NE(std::find(usageCodes.begin(), usageCodes.end(), hidUsageCode), usageCodes.end())
+ << "Fallback usage code should be mapped to key";
+ }
+
+ static constexpr std::array<int32_t, 6> hidUsageCodesWithFallback = {0x0c007C, 0x0c0173,
+ 0x0c019C, 0x0c01A2,
+ 0x0d0044, 0x0d005a};
+ for (int32_t hidUsageCode : hidUsageCodesWithFallback) {
+ int32_t outKeyCode;
+ uint32_t outFlags;
+ keyLayoutMap->mapKey(0, hidUsageCode, &outKeyCode, &outFlags);
+ ASSERT_TRUE(outFlags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)
+ << "HID usage code should be marked as fallback";
+ std::vector<int32_t> usageCodes = keyLayoutMap->findUsageCodesForKey(outKeyCode);
+ ASSERT_EQ(std::find(usageCodes.begin(), usageCodes.end(), hidUsageCode), usageCodes.end())
+ << "Fallback usage code should not be mapped to key";
+ }
+}
+
TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
#if !defined(__ANDROID__)
GTEST_SKIP() << "Can't check kernel configs on host";
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 3ecf8ee..06b841b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -25,7 +25,22 @@
namespace android {
-constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION;
+namespace {
+
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+static constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_2_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+struct Pointer {
+ int32_t id;
+ float x;
+ float y;
+ bool isResampled = false;
+};
+
+} // namespace
class InputPublisherAndConsumerTest : public testing::Test {
protected:
@@ -46,12 +61,28 @@
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
- void PublishAndConsumeKeyEvent();
- void PublishAndConsumeMotionEvent();
- void PublishAndConsumeFocusEvent();
- void PublishAndConsumeCaptureEvent();
- void PublishAndConsumeDragEvent();
- void PublishAndConsumeTouchModeEvent();
+ void publishAndConsumeKeyEvent();
+ void publishAndConsumeMotionStream();
+ void publishAndConsumeFocusEvent();
+ void publishAndConsumeCaptureEvent();
+ void publishAndConsumeDragEvent();
+ void publishAndConsumeTouchModeEvent();
+ void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime,
+ const std::vector<Pointer>& pointers);
+
+private:
+ // The sequence number to use when publishing the next event
+ uint32_t mSeq = 1;
+
+ void publishAndConsumeMotionEvent(
+ int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags,
+ int32_t metaState, int32_t buttonState, MotionClassification classification,
+ float xScale, float yScale, float xOffset, float yOffset, float xPrecision,
+ float yPrecision, float xCursorPosition, float yCursorPosition, float rawXScale,
+ float rawYScale, float rawXOffset, float rawYOffset, nsecs_t downTime,
+ nsecs_t eventTime, const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords);
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -63,10 +94,10 @@
mConsumer->getChannel()->getConnectionToken());
}
-void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeKeyEvent() {
status_t status;
- constexpr uint32_t seq = 15;
+ const uint32_t seq = mSeq++;
int32_t eventId = InputEvent::nextId();
constexpr int32_t deviceId = 1;
constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
@@ -132,20 +163,43 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
- status_t status;
+void InputPublisherAndConsumerTest::publishAndConsumeMotionStream() {
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
- constexpr uint32_t seq = 15;
- int32_t eventId = InputEvent::nextId();
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+
+ publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300}});
+
+ publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 300, .y = 400}});
+
+ // Provide a consistent input stream - cancel the gesture that was started above
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 300, .y = 400}});
+}
+
+void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent(
+ int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) {
constexpr int32_t deviceId = 1;
constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
- constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
constexpr int32_t actionButton = 0;
- constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ const size_t pointerCount = pointers.size();
constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
@@ -162,20 +216,21 @@
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
- constexpr nsecs_t downTime = 3;
- constexpr size_t pointerCount = 3;
- constexpr nsecs_t eventTime = 4;
- const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
+
+ const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties.push_back({});
pointerProperties[i].clear();
- pointerProperties[i].id = (i + 2) % pointerCount;
+ pointerProperties[i].id = pointers[i].id;
pointerProperties[i].toolType = ToolType::FINGER;
+ pointerCoords.push_back({});
pointerCoords[i].clear();
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i);
- pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i);
+ pointerCoords[i].isResampled = pointers[i].isResampled;
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
@@ -185,18 +240,40 @@
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
}
+ publishAndConsumeMotionEvent(deviceId, source, displayId, hmac, action, actionButton, flags,
+ edgeFlags, metaState, buttonState, classification, xScale, yScale,
+ xOffset, yOffset, xPrecision, yPrecision, xCursorPosition,
+ yCursorPosition, rawXScale, rawYScale, rawXOffset, rawYOffset,
+ downTime, eventTime, pointerProperties, pointerCoords);
+}
+
+void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent(
+ int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, float xScale, float yScale,
+ float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+ float yCursorPosition, float rawXScale, float rawYScale, float rawXOffset, float rawYOffset,
+ nsecs_t downTime, nsecs_t eventTime,
+ const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords) {
+ const uint32_t seq = mSeq++;
+ const int32_t eventId = InputEvent::nextId();
ui::Transform transform;
transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
ui::Transform rawTransform;
rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
+
+ status_t status;
+ ASSERT_EQ(pointerProperties.size(), pointerCoords.size());
+ const size_t pointerCount = pointerProperties.size();
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
xCursorPosition, yCursorPosition, rawTransform,
- downTime, eventTime, pointerCount, pointerProperties,
- pointerCoords);
- ASSERT_EQ(OK, status)
- << "publisher publishMotionEvent should return OK";
+ downTime, eventTime, pointerCount,
+ pointerProperties.data(), pointerCoords.data());
+ ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK";
uint32_t consumeSeq;
InputEvent* event;
@@ -280,7 +357,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeFocusEvent() {
status_t status;
constexpr uint32_t seq = 15;
@@ -321,7 +398,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeCaptureEvent() {
status_t status;
constexpr uint32_t seq = 42;
@@ -361,7 +438,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeDragEvent() {
status_t status;
constexpr uint32_t seq = 15;
@@ -405,7 +482,7 @@
<< "finished signal's consume time should be greater than publish time";
}
-void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+void InputPublisherAndConsumerTest::publishAndConsumeTouchModeEvent() {
status_t status;
constexpr uint32_t seq = 15;
@@ -462,27 +539,27 @@
}
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream());
}
TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishCaptureEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
@@ -546,17 +623,29 @@
}
TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeCaptureEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
- ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent());
+ publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent());
+ // Provide a consistent input stream - cancel the gesture that was started above
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
}
} // namespace android
diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp
index e24fa6e..e2eb080 100644
--- a/libs/input/tests/InputVerifier_test.cpp
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -20,10 +20,35 @@
namespace android {
+using android::base::Result;
+
TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) {
constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)};
const std::string name(bytes, sizeof(bytes));
InputVerifier verifier(name);
}
+TEST(InputVerifierTest, ProcessSourceClassPointer) {
+ InputVerifier verifier("Verify testOnTouchEventScroll");
+
+ std::vector<PointerProperties> properties;
+ properties.push_back({});
+ properties.back().clear();
+ properties.back().id = 0;
+ properties.back().toolType = ToolType::UNKNOWN;
+
+ std::vector<PointerCoords> coords;
+ coords.push_back({});
+ coords.back().clear();
+ coords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 75);
+ coords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 300);
+
+ const Result<void> result =
+ verifier.processMovement(/*deviceId=*/0, AINPUT_SOURCE_CLASS_POINTER,
+ AMOTION_EVENT_ACTION_DOWN,
+ /*pointerCount=*/properties.size(), properties.data(),
+ coords.data(), /*flags=*/0);
+ ASSERT_TRUE(result.ok());
+}
+
} // namespace android
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index b5ed9e4..c3ac0b7 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -130,19 +130,19 @@
std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
ASSERT_GT(model->inputLength(), 0u);
- const int inputLength = model->inputLength();
- ASSERT_EQ(inputLength, model->inputR().size());
- ASSERT_EQ(inputLength, model->inputPhi().size());
- ASSERT_EQ(inputLength, model->inputPressure().size());
- ASSERT_EQ(inputLength, model->inputOrientation().size());
- ASSERT_EQ(inputLength, model->inputTilt().size());
+ const size_t inputLength = model->inputLength();
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputR().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputPhi().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputPressure().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputOrientation().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputTilt().size()));
ASSERT_TRUE(model->invoke());
- const int outputLength = model->outputLength();
- ASSERT_EQ(outputLength, model->outputR().size());
- ASSERT_EQ(outputLength, model->outputPhi().size());
- ASSERT_EQ(outputLength, model->outputPressure().size());
+ const size_t outputLength = model->outputLength();
+ ASSERT_EQ(outputLength, static_cast<size_t>(model->outputR().size()));
+ ASSERT_EQ(outputLength, static_cast<size_t>(model->outputPhi().size()));
+ ASSERT_EQ(outputLength, static_cast<size_t>(model->outputPressure().size()));
}
TEST(TfLiteMotionPredictorTest, ModelOutput) {
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 655de80..1cb7f7b 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -27,10 +27,13 @@
namespace android {
+namespace {
+
struct Pointer {
int32_t id;
float x;
float y;
+ ToolType toolType = ToolType::FINGER;
bool isResampled = false;
};
@@ -40,6 +43,8 @@
int32_t action;
};
+} // namespace
+
class TouchResamplingTest : public testing::Test {
protected:
std::unique_ptr<InputPublisher> mPublisher;
@@ -99,7 +104,7 @@
properties.push_back({});
properties.back().clear();
properties.back().id = pointer.id;
- properties.back().toolType = ToolType::FINGER;
+ properties.back().toolType = pointer.toolType;
coords.push_back({});
coords.back().clear();
@@ -292,6 +297,48 @@
}
/**
+ * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with
+ * a resampled timestamp and should be marked as such.
+ */
+TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) {
+ std::chrono::nanoseconds frameTime;
+ std::vector<InputEventEntry> entries, expectedEntries;
+
+ // Initial ACTION_DOWN should be separate, because the first consume event will only return
+ // InputEvent with a single action.
+ entries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 5ms;
+ expectedEntries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ entries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 35ms;
+ expectedEntries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
+ // A resampled event is generated, but the stylus coordinates are not resampled.
+ {25ms,
+ {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
* Event should not be resampled when sample time is equal to event time.
*/
TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
@@ -544,13 +591,13 @@
// First pointer id=0 leaves the screen
entries = {
// id x y
- {80ms, {{1, 600, 600}}, actionPointer0Up},
+ {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
};
publishInputEventEntries(entries);
frameTime = 90ms;
expectedEntries = {
// id x y
- {80ms, {{1, 600, 600}}, actionPointer0Up},
+ {80ms, {{0, 120, 120}, {1, 600, 600}}, actionPointer0Up},
// no resampled event for ACTION_POINTER_UP
};
consumeInputEventEntries(expectedEntries, frameTime);
diff --git a/libs/input/tests/data/hid_fallback_mapping.kl b/libs/input/tests/data/hid_fallback_mapping.kl
new file mode 100644
index 0000000..b4ca9ef
--- /dev/null
+++ b/libs/input/tests/data/hid_fallback_mapping.kl
@@ -0,0 +1,32 @@
+# 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.
+
+#
+# test key layout file for InputDeviceKeyMapTest#HidUsageCodesUseFallbackMapping
+#
+
+# Keys defined by HID usages without fallback mapping flag
+key usage 0x0c0067 WINDOW
+key usage 0x0c006F BRIGHTNESS_UP
+key usage 0x0c0070 BRIGHTNESS_DOWN
+key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP
+key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN
+
+# Keys defined by HID usages with fallback mapping flag
+key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING
+key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING
+key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING
\ No newline at end of file
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 8d8a2bc..342f5de 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -73,6 +73,8 @@
"libGLESv2",
],
+ static_libs: ["libguiflags"],
+
export_header_lib_headers: ["jni_headers"],
header_libs: [
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 0f119f3..32fb350 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -19,6 +19,7 @@
#include <android/hardware_buffer.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
+#include <gui/Flags.h>
#include <gui/IGraphicBufferProducer.h>
#include <sys/cdefs.h>
#include <system/graphics.h>
@@ -290,6 +291,20 @@
*/
void releaseConsumerOwnership();
+ /**
+ * Interface for SurfaceTexture callback(s).
+ */
+ struct SurfaceTextureListener : public RefBase {
+ virtual void onFrameAvailable(const BufferItem& item) = 0;
+ virtual void onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) = 0;
+ };
+
+ /**
+ * setSurfaceTextureListener registers a SurfaceTextureListener.
+ */
+ void setSurfaceTextureListener(const sp<SurfaceTextureListener>&);
+
protected:
/**
* abandonLocked overrides the ConsumerBase method to clear
@@ -335,6 +350,14 @@
void computeCurrentTransformMatrixLocked();
/**
+ * onSetFrameRate Notifies the consumer of a setFrameRate call from the producer side.
+ */
+#if FLAG_BQ_SET_FRAME_RATE
+ void onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override;
+#endif
+
+ /**
* The default consumer usage flags that SurfaceTexture always sets on its
* BufferQueue instance; these will be OR:d with any additional flags passed
* from the SurfaceTexture user. In particular, SurfaceTexture will always
@@ -465,8 +488,30 @@
*/
ImageConsumer mImageConsumer;
+ /**
+ * mSurfaceTextureListener holds the registered SurfaceTextureListener.
+ * Note that SurfaceTexture holds the lister with an sp<>, which means that the listener
+ * must only hold a wp<> to SurfaceTexture and not an sp<>.
+ */
+ sp<SurfaceTextureListener> mSurfaceTextureListener;
+
friend class ImageConsumer;
friend class EGLConsumer;
+
+private:
+ // Proxy listener to avoid having SurfaceTexture directly implement FrameAvailableListener as it
+ // is extending ConsumerBase which also implements FrameAvailableListener.
+ class FrameAvailableListenerProxy : public ConsumerBase::FrameAvailableListener {
+ public:
+ FrameAvailableListenerProxy(const wp<SurfaceTextureListener>& listener)
+ : mSurfaceTextureListener(listener) {}
+
+ private:
+ void onFrameAvailable(const BufferItem& item) override;
+
+ const wp<SurfaceTextureListener> mSurfaceTextureListener;
+ };
+ sp<FrameAvailableListenerProxy> mFrameAvailableListenerProxy;
};
// ----------------------------------------------------------------------------
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 9f610e1..c2535e0 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -23,6 +23,8 @@
#include <system/window.h>
#include <utils/Trace.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
// Macros for including the SurfaceTexture name in log messages
@@ -491,4 +493,42 @@
return buffer;
}
+void SurfaceTexture::setSurfaceTextureListener(
+ const sp<android::SurfaceTexture::SurfaceTextureListener>& listener) {
+ SFT_LOGV("setSurfaceTextureListener");
+
+ Mutex::Autolock _l(mMutex);
+ mSurfaceTextureListener = listener;
+ if (mSurfaceTextureListener != nullptr) {
+ mFrameAvailableListenerProxy =
+ sp<FrameAvailableListenerProxy>::make(mSurfaceTextureListener);
+ setFrameAvailableListener(mFrameAvailableListenerProxy);
+ } else {
+ mFrameAvailableListenerProxy.clear();
+ }
+}
+
+void SurfaceTexture::FrameAvailableListenerProxy::onFrameAvailable(const BufferItem& item) {
+ const auto listener = mSurfaceTextureListener.promote();
+ if (listener) {
+ listener->onFrameAvailable(item);
+ }
+}
+
+#if FLAG_BQ_SET_FRAME_RATE
+void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
+ SFT_LOGV("onSetFrameRate: %.2f", frameRate);
+
+ auto listener = [&] {
+ Mutex::Autolock _l(mMutex);
+ return mSurfaceTextureListener;
+ }();
+
+ if (listener) {
+ listener->onSetFrameRate(frameRate, compatibility, changeFrameRateStrategy);
+ }
+}
+#endif
+
} // namespace android
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 9fa5569..eab21fb 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -81,11 +81,12 @@
STANDARD_UNSPECIFIED = 0 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.300 0.600
* blue 0.150 0.060
* red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
* for RGB conversion.
@@ -93,11 +94,12 @@
STANDARD_BT709 = 1 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.290 0.600
* blue 0.150 0.060
* red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
* for RGB conversion from the one purely determined by the primaries
@@ -107,11 +109,12 @@
STANDARD_BT601_625 = 2 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.290 0.600
* blue 0.150 0.060
* red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
* for RGB conversion.
@@ -119,11 +122,12 @@
STANDARD_BT601_625_UNADJUSTED = 3 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.310 0.595
* blue 0.155 0.070
* red 0.630 0.340
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
* for RGB conversion from the one purely determined by the primaries
@@ -133,11 +137,12 @@
STANDARD_BT601_525 = 4 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.310 0.595
* blue 0.155 0.070
* red 0.630 0.340
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
* for RGB conversion (as in SMPTE 240M).
@@ -145,11 +150,12 @@
STANDARD_BT601_525_UNADJUSTED = 5 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.170 0.797
* blue 0.131 0.046
* red 0.708 0.292
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion.
@@ -157,11 +163,12 @@
STANDARD_BT2020 = 6 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.170 0.797
* blue 0.131 0.046
* red 0.708 0.292
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion using the linear domain.
@@ -169,11 +176,12 @@
STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.21 0.71
* blue 0.14 0.08
* red 0.67 0.33
- * white (C) 0.310 0.316
+ * white (C) 0.310 0.316</pre>
*
* Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
* for RGB conversion.
@@ -181,11 +189,12 @@
STANDARD_BT470M = 8 << 16,
/**
+ * <pre>
* Primaries: x y
* green 0.243 0.692
* blue 0.145 0.049
* red 0.681 0.319
- * white (C) 0.310 0.316
+ * white (C) 0.310 0.316</pre>
*
* Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
* for RGB conversion.
@@ -194,21 +203,23 @@
/**
* SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
+ * <pre>
* Primaries: x y
* green 0.265 0.690
* blue 0.150 0.060
* red 0.680 0.320
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*/
STANDARD_DCI_P3 = 10 << 16,
/**
* Adobe RGB
+ * <pre>
* Primaries: x y
* green 0.210 0.710
* blue 0.150 0.060
* red 0.640 0.330
- * white (D65) 0.3127 0.3290
+ * white (D65) 0.3127 0.3290</pre>
*/
STANDARD_ADOBE_RGB = 11 << 16,
@@ -242,83 +253,86 @@
TRANSFER_UNSPECIFIED = 0 << 22,
/**
+ * Linear transfer.
+ * <pre>
* Transfer characteristic curve:
- * E = L
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
+ * E = L
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_LINEAR = 1 << 22,
/**
+ * sRGB transfer.
+ * <pre>
* Transfer characteristic curve:
- *
* E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
* = 12.92 * L for 0 <= L < 0.0031308
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_SRGB = 2 << 22,
/**
- * BT.601 525, BT.601 625, BT.709, BT.2020
- *
+ * SMPTE 170M transfer.
+ * <pre>
* Transfer characteristic curve:
- * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
- * = 4.500 * L for 0 <= L < 0.018
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
+ * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
+ * = 4.500 * L for 0 <= L < 0.018
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_SMPTE_170M = 3 << 22,
/**
- * Assumed display gamma 2.2.
- *
+ * Display gamma 2.2.
+ * <pre>
* Transfer characteristic curve:
- * E = L ^ (1/2.2)
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
+ * E = L ^ (1/2.2)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_GAMMA2_2 = 4 << 22,
/**
- * display gamma 2.6.
- *
+ * Display gamma 2.6.
+ * <pre>
* Transfer characteristic curve:
- * E = L ^ (1/2.6)
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
+ * E = L ^ (1/2.6)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_GAMMA2_6 = 5 << 22,
/**
- * display gamma 2.8.
- *
+ * Display gamma 2.8.
+ * <pre>
* Transfer characteristic curve:
- * E = L ^ (1/2.8)
- * L - luminance of image 0 <= L <= 1 for conventional colorimetry
- * E - corresponding electrical signal
+ * E = L ^ (1/2.8)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_GAMMA2_8 = 6 << 22,
/**
- * SMPTE ST 2084 (Dolby Perceptual Quantizer)
- *
+ * SMPTE ST 2084 (Dolby Perceptual Quantizer).
+ * <pre>
* Transfer characteristic curve:
- * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
- * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
- * c2 = 32 * 2413 / 4096 = 18.8515625
- * c3 = 32 * 2392 / 4096 = 18.6875
- * m = 128 * 2523 / 4096 = 78.84375
- * n = 0.25 * 2610 / 4096 = 0.1593017578125
- * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
- * L = 1 corresponds to 10000 cd/m2
- * E - corresponding electrical signal
+ * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+ * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+ * c2 = 32 * 2413 / 4096 = 18.8515625
+ * c3 = 32 * 2392 / 4096 = 18.6875
+ * m = 128 * 2523 / 4096 = 78.84375
+ * n = 0.25 * 2610 / 4096 = 0.1593017578125
+ * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+ * L = 1 corresponds to 10000 cd/m2
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_ST2084 = 7 << 22,
/**
- * ARIB STD-B67 Hybrid Log Gamma
- *
+ * ARIB STD-B67 Hybrid Log Gamma.
+ * <pre>
* Transfer characteristic curve:
* E = r * L^0.5 for 0 <= L <= 1
* = a * ln(L - b) + c for 1 < L
@@ -328,7 +342,7 @@
* r = 0.5
* L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
* to reference white level of 100 cd/m2
- * E - corresponding electrical signal
+ * E - corresponding electrical signal</pre>
*/
TRANSFER_HLG = 8 << 22,
@@ -384,21 +398,22 @@
RANGE_EXTENDED = 3 << 27,
/**
- * scRGB linear encoding:
+ * scRGB linear encoding
*
* The red, green, and blue components are stored in extended sRGB space,
* but are linear, not gamma-encoded.
- * The RGB primaries and the white point are the same as BT.709.
*
* The values are floating point.
* A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits.
* Values beyond the range [0.0 - 1.0] would correspond to other colors
* spaces and/or HDR content.
+ *
+ * Uses extended range, linear transfer and BT.709 standard.
*/
ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
/**
- * sRGB gamma encoding:
+ * sRGB gamma encoding
*
* The red, green and blue components are stored in sRGB space, and
* converted to linear space when read, using the SRGB transfer function
@@ -408,29 +423,29 @@
* The alpha component, if present, is always stored in linear space and
* is left unmodified when read or written.
*
- * Use full range and BT.709 standard.
+ * Uses full range, sRGB transfer BT.709 standard.
*/
ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
/**
- * scRGB:
+ * scRGB
*
* The red, green, and blue components are stored in extended sRGB space,
* and gamma-encoded using the SRGB transfer function.
- * The RGB primaries and the white point are the same as BT.709.
*
* The values are floating point.
* A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits.
* Values beyond the range [0.0 - 1.0] would correspond to other colors
* spaces and/or HDR content.
+ *
+ * Uses extended range, sRGB transfer and BT.709 standard.
*/
ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED
/**
* Display P3
*
- * Use same primaries and white-point as DCI-P3
- * but sRGB transfer function.
+ * Uses full range, sRGB transfer and D65 DCI-P3 standard.
*/
ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
@@ -439,7 +454,7 @@
*
* Ultra High-definition television
*
- * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ * Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
@@ -448,14 +463,15 @@
*
* Ultra High-definition television
*
- * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ * Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
/**
* Adobe RGB
*
- * Use full range, gamma 2.2 transfer and Adobe RGB primaries
+ * Uses full range, gamma 2.2 transfer and Adobe RGB standard.
+ *
* Note: Application is responsible for gamma encoding the data as
* a 2.2 gamma encoding is not supported in HW.
*/
@@ -464,9 +480,9 @@
/**
* JPEG File Interchange Format (JFIF)
*
- * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
+ * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255.
*
- * Use full range, SMPTE 170M transfer and BT.601_625 standard.
+ * Uses full range, SMPTE 170M transfer and BT.601_625 standard.
*/
ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
@@ -475,7 +491,7 @@
*
* Standard-definition television, 625 Lines (PAL)
*
- * Use limited range, SMPTE 170M transfer and BT.601_625 standard.
+ * Uses limited range, SMPTE 170M transfer and BT.601_625 standard.
*/
ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
@@ -484,7 +500,7 @@
*
* Standard-definition television, 525 Lines (NTSC)
*
- * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
+ * Uses limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
@@ -493,7 +509,7 @@
*
* Ultra High-definition television
*
- * Use full range, BT.709 transfer and BT2020 standard
+ * Uses full range, SMPTE 170M transfer and BT2020 standard.
*/
ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
@@ -502,23 +518,24 @@
*
* High-definition television
*
- * Use limited range, BT.709 transfer and BT.709 standard.
+ * Uses limited range, SMPTE 170M transfer and BT.709 standard.
*/
ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
/**
- * SMPTE EG 432-1 and SMPTE RP 431-2.
+ * SMPTE EG 432-1 and SMPTE RP 431-2
*
* Digital Cinema DCI-P3
*
- * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+ * Uses full range, gamma 2.6 transfer and D65 DCI-P3 standard.
+ *
* Note: Application is responsible for gamma encoding the data as
* a 2.6 gamma encoding is not supported in HW.
*/
ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
/**
- * sRGB linear encoding:
+ * sRGB linear encoding
*
* The red, green, and blue components are stored in sRGB space, but
* are linear, not gamma-encoded.
@@ -526,32 +543,34 @@
*
* The values are encoded using the full range ([0,255] for 8-bit) for all
* components.
+ *
+ * Uses full range, linear transfer and BT.709 standard.
*/
ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
/**
- * Hybrid Log Gamma encoding:
+ * Hybrid Log Gamma encoding
*
- * Use full range, hybrid log gamma transfer and BT2020 standard.
+ * Uses full range, hybrid log gamma transfer and BT2020 standard.
*/
ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
/**
- * ITU Hybrid Log Gamma encoding:
+ * ITU Hybrid Log Gamma encoding
*
- * Use limited range, hybrid log gamma transfer and BT2020 standard.
+ * Uses limited range, hybrid log gamma transfer and BT2020 standard.
*/
ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
/**
- * Depth:
+ * Depth
*
* This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB.
*/
ADATASPACE_DEPTH = 4096,
/**
- * ISO 16684-1:2011(E) Dynamic Depth:
+ * ISO 16684-1:2011(E) Dynamic Depth
*
* Embedded depth metadata following the dynamic depth specification.
*/
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 28aa4dd..8ac0af4 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -28,6 +28,7 @@
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/ShadowSettings.h>
#include <ui/StretchEffect.h>
#include <ui/Transform.h>
@@ -97,36 +98,6 @@
half3 solidColor = half3(0.0f, 0.0f, 0.0f);
};
-/*
- * Contains the configuration for the shadows drawn by single layer. Shadow follows
- * material design guidelines.
- */
-struct ShadowSettings {
- // Boundaries of the shadow.
- FloatRect boundaries = FloatRect();
-
- // Color to the ambient shadow. The alpha is premultiplied.
- vec4 ambientColor = vec4();
-
- // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
- // depends on the light position.
- vec4 spotColor = vec4();
-
- // Position of the light source used to cast the spot shadow.
- vec3 lightPos = vec3();
-
- // Radius of the spot light source. Smaller radius will have sharper edges,
- // larger radius will have softer shadows
- float lightRadius = 0.f;
-
- // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
- float length = 0.f;
-
- // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
- // Otherwise the shadow will only be drawn around the edges of the casting layer.
- bool casterIsTranslucent = false;
-};
-
// The settings that RenderEngine requires for correctly rendering a Layer.
struct LayerSettings {
// Geometry information
@@ -194,17 +165,6 @@
return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
}
-static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
- return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
- lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
- lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
- lhs.casterIsTranslucent == rhs.casterIsTranslucent;
-}
-
-static inline bool operator!=(const ShadowSettings& lhs, const ShadowSettings& rhs) {
- return !(operator==(lhs, rhs));
-}
-
static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
return false;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index cc1d12b..2053c6a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -27,6 +27,7 @@
#include <GrTypes.h>
#include <android-base/stringprintf.h>
#include <gl/GrGLInterface.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <gui/TraceUtils.h>
#include <sync/sync.h>
#include <ui/DebugUtils.h>
@@ -299,10 +300,10 @@
LOG_ALWAYS_FATAL_IF(!glInterface.get(), "GrGLMakeNativeInterface() failed");
SkiaRenderEngine::Contexts contexts;
- contexts.first = GrDirectContext::MakeGL(glInterface, options);
+ contexts.first = GrDirectContexts::MakeGL(glInterface, options);
if (supportsProtectedContentImpl()) {
useProtectedContextImpl(GrProtected::kYes);
- contexts.second = GrDirectContext::MakeGL(glInterface, options);
+ contexts.second = GrDirectContexts::MakeGL(glInterface, options);
useProtectedContextImpl(GrProtected::kNo);
}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7dde716..6023808 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -403,7 +403,7 @@
}
void expectShadowColor(const renderengine::LayerSettings& castingLayer,
- const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ShadowSettings& shadow, const ubyte4& casterColor,
const ubyte4& backgroundColor) {
const Rect casterRect(castingLayer.geometry.boundaries);
Region casterRegion = Region(casterRect);
@@ -443,8 +443,7 @@
backgroundColor.a);
}
- void expectShadowColorWithoutCaster(const FloatRect& casterBounds,
- const renderengine::ShadowSettings& shadow,
+ void expectShadowColorWithoutCaster(const FloatRect& casterBounds, const ShadowSettings& shadow,
const ubyte4& backgroundColor) {
const float shadowInset = shadow.length * -1.0f;
const Rect casterRect(casterBounds);
@@ -463,9 +462,9 @@
backgroundColor.a);
}
- static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
- bool casterIsTranslucent) {
- renderengine::ShadowSettings shadow;
+ static ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+ bool casterIsTranslucent) {
+ ShadowSettings shadow;
shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
@@ -602,12 +601,10 @@
void fillGreenColorBufferThenClearRegion();
template <typename SourceVariant>
- void drawShadow(const renderengine::LayerSettings& castingLayer,
- const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
- const ubyte4& backgroundColor);
+ void drawShadow(const renderengine::LayerSettings& castingLayer, const ShadowSettings& shadow,
+ const ubyte4& casterColor, const ubyte4& backgroundColor);
- void drawShadowWithoutCaster(const FloatRect& castingBounds,
- const renderengine::ShadowSettings& shadow,
+ void drawShadowWithoutCaster(const FloatRect& castingBounds, const ShadowSettings& shadow,
const ubyte4& backgroundColor);
// Tonemaps grey values from sourceDataspace -> Display P3 and checks that GPU and CPU
@@ -1337,8 +1334,8 @@
template <typename SourceVariant>
void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
- const renderengine::ShadowSettings& shadow,
- const ubyte4& casterColor, const ubyte4& backgroundColor) {
+ const ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor) {
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
@@ -1374,7 +1371,7 @@
}
void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
- const renderengine::ShadowSettings& shadow,
+ const ShadowSettings& shadow,
const ubyte4& backgroundColor) {
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -2103,9 +2100,8 @@
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
@@ -2127,9 +2123,8 @@
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
@@ -2152,9 +2147,8 @@
castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
@@ -2177,9 +2171,8 @@
castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
backgroundColor);
@@ -2204,9 +2197,8 @@
castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
backgroundColor);
@@ -2227,9 +2219,8 @@
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 0.5f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- true /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, true /* casterIsTranslucent */);
drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
backgroundColor);
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 475dc15..7289fe7 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -98,7 +98,6 @@
}
TEST_F(RenderEngineThreadedTest, PostRenderCleanup_skipped) {
- EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(true));
EXPECT_CALL(*mRenderEngine, cleanupPostRender()).Times(0);
mThreadedRE->cleanupPostRender();
@@ -107,8 +106,25 @@
}
TEST_F(RenderEngineThreadedTest, PostRenderCleanup_notSkipped) {
- EXPECT_CALL(*mRenderEngine, canSkipPostRenderCleanup()).WillOnce(Return(false));
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(), *mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+ base::unique_fd bufferFence;
+
+ EXPECT_CALL(*mRenderEngine, useProtectedContext(false));
+ EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+ .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return());
+ ftl::Future<FenceResult> future =
+ mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence));
mThreadedRE->cleanupPostRender();
// call ANY synchronous function to ensure that cleanupPostRender has completed.
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 2cb66cb..786a6fe 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -231,13 +231,13 @@
ATRACE_NAME("REThreaded::cleanupPostRender");
instance.cleanupPostRender();
});
+ mNeedsPostRenderCleanup = false;
}
mCondition.notify_one();
}
bool RenderEngineThreaded::canSkipPostRenderCleanup() const {
- waitUntilInitialized();
- return mRenderEngine->canSkipPostRenderCleanup();
+ return !mNeedsPostRenderCleanup;
}
void RenderEngineThreaded::drawLayersInternal(
@@ -257,6 +257,7 @@
int fd = bufferFence.release();
{
std::lock_guard lock(mThreadMutex);
+ mNeedsPostRenderCleanup = true;
mFunctionCalls.push(
[resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::drawLayers");
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 43ec011..1093f5f 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -89,6 +89,7 @@
mutable std::mutex mThreadMutex;
std::thread mThread GUARDED_BY(mThreadMutex);
std::atomic<bool> mRunning = true;
+ std::atomic<bool> mNeedsPostRenderCleanup = false;
using Work = std::function<void(renderengine::RenderEngine&)>;
mutable std::queue<Work> mFunctionCalls GUARDED_BY(mThreadMutex);
diff --git a/libs/ui/include/ui/ShadowSettings.h b/libs/ui/include/ui/ShadowSettings.h
new file mode 100644
index 0000000..c0b83b8
--- /dev/null
+++ b/libs/ui/include/ui/ShadowSettings.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec4.h>
+#include "FloatRect.h"
+
+namespace android {
+
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+ // Boundaries of the shadow.
+ FloatRect boundaries = FloatRect();
+
+ // Color to the ambient shadow. The alpha is premultiplied.
+ vec4 ambientColor = vec4();
+
+ // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ // depends on the light position.
+ vec4 spotColor = vec4();
+
+ // Position of the light source used to cast the spot shadow.
+ vec3 lightPos = vec3();
+
+ // Radius of the spot light source. Smaller radius will have sharper edges,
+ // larger radius will have softer shadows
+ float lightRadius = 0.f;
+
+ // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+ float length = 0.f;
+
+ // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+ // Otherwise the shadow will only be drawn around the edges of the casting layer.
+ bool casterIsTranslucent = false;
+};
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
+ lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
+ lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+ lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator!=(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return !(operator==(lhs, rhs));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp
index 7ffdac7..ed3c616 100644
--- a/opengl/libs/EGL/MultifileBlobCache.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache.cpp
@@ -48,9 +48,8 @@
void freeHotCacheEntry(android::MultifileHotCache& entry) {
if (entry.entryFd != -1) {
// If we have an fd, then this entry was added to hot cache via INIT or GET
- // We need to unmap and close the entry
+ // We need to unmap the entry
munmap(entry.entryBuffer, entry.entrySize);
- close(entry.entryFd);
} else {
// Otherwise, this was added to hot cache during SET, so it was never mapped
// and fd was only on the deferred thread.
@@ -143,6 +142,7 @@
if (result != sizeof(MultifileHeader)) {
ALOGE("Error reading MultifileHeader from cache entry (%s): %s",
fullPath.c_str(), std::strerror(errno));
+ close(fd);
return;
}
@@ -152,6 +152,7 @@
if (remove(fullPath.c_str()) != 0) {
ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno));
}
+ close(fd);
continue;
}
@@ -161,6 +162,10 @@
// Memory map the file
uint8_t* mappedEntry = reinterpret_cast<uint8_t*>(
mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
+
+ // We can close the file now and the mmap will remain
+ close(fd);
+
if (mappedEntry == MAP_FAILED) {
ALOGE("Failed to mmap cacheEntry, error: %s", std::strerror(errno));
return;
@@ -206,13 +211,11 @@
if (!addToHotCache(entryHash, fd, mappedEntry, fileSize)) {
ALOGE("INIT Failed to add %u to hot cache", entryHash);
munmap(mappedEntry, fileSize);
- close(fd);
return;
}
} else {
// If we're not keeping it in hot cache, unmap it now
munmap(mappedEntry, fileSize);
- close(fd);
}
}
closedir(dir);
@@ -401,9 +404,12 @@
// Memory map the file
cacheEntry =
reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0));
+
+ // We can close the file now and the mmap will remain
+ close(fd);
+
if (cacheEntry == MAP_FAILED) {
ALOGE("Failed to mmap cacheEntry, error: %s", std::strerror(errno));
- close(fd);
return 0;
}
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index dbee13b..1639be6 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -42,6 +42,8 @@
virtual void TearDown() { mMBC.reset(); }
+ int getFileDescriptorCount();
+
std::unique_ptr<TemporaryFile> mTempFile;
std::unique_ptr<MultifileBlobCache> mMBC;
};
@@ -216,4 +218,56 @@
ASSERT_EQ('y', buf[0]);
}
+int MultifileBlobCacheTest::getFileDescriptorCount() {
+ DIR* directory = opendir("/proc/self/fd");
+
+ int fileCount = 0;
+ struct dirent* entry;
+ while ((entry = readdir(directory)) != NULL) {
+ fileCount++;
+ // printf("File: %s\n", entry->d_name);
+ }
+
+ closedir(directory);
+ return fileCount;
+}
+
+TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
+ // Populate the cache with a bunch of entries
+ size_t kLargeNumberOfEntries = 1024;
+ for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ // printf("Caching: %i", i);
+
+ // Use the index as the key and value
+ mMBC->set(&i, sizeof(i), &i, sizeof(i));
+
+ int result = 0;
+ ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
+ ASSERT_EQ(i, result);
+ }
+
+ // Ensure we don't have a bunch of open fds
+ ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+
+ // Close the cache so everything writes out
+ mMBC->finish();
+ mMBC.reset();
+
+ // Now open it again and ensure we still don't have a bunch of open fds
+ mMBC.reset(
+ new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
+
+ // Check after initialization
+ ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+
+ for (int i = 0; i < kLargeNumberOfEntries; i++) {
+ int result = 0;
+ ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
+ ASSERT_EQ(i, result);
+ }
+
+ // And again after we've actually used it
+ ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+}
+
} // namespace android
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index dd3cc3b..141fe02 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -77,7 +77,7 @@
mInitialized.store(true);
}
-void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+void GpuMem::setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map) {
mGpuMemTotalMap = std::move(map);
}
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index 7588b54..9aa74d6 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -44,12 +44,12 @@
friend class TestableGpuMem;
// set gpu memory total map
- void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+ void setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map);
// indicate whether ebpf has been initialized
std::atomic<bool> mInitialized = false;
// bpf map for GPU memory total data
- android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+ android::bpf::BpfMapRO<uint64_t, uint64_t> mGpuMemTotalMap;
// gpu memory tracepoint event category
static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp
index 8dabe4f..1f5b288 100644
--- a/services/gpuservice/tests/unittests/GpuMemTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp
@@ -66,9 +66,7 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
mTestableGpuMem.setInitialized();
errno = 0;
- mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
- TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC));
+ mTestMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
EXPECT_EQ(0, errno);
EXPECT_TRUE(mTestMap.isValid());
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
index 5c04210..6550df9 100644
--- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -65,9 +65,7 @@
mTestableGpuMem = TestableGpuMem(mGpuMem.get());
errno = 0;
- mTestMap = std::move(bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH,
- TEST_MAP_SIZE,
- BPF_F_NO_PREALLOC));
+ mTestMap.resetMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
EXPECT_EQ(0, errno);
EXPECT_TRUE(mTestMap.isValid());
diff --git a/services/gpuservice/tests/unittests/TestableGpuMem.h b/services/gpuservice/tests/unittests/TestableGpuMem.h
index 6c8becb..f21843f 100644
--- a/services/gpuservice/tests/unittests/TestableGpuMem.h
+++ b/services/gpuservice/tests/unittests/TestableGpuMem.h
@@ -28,7 +28,7 @@
void setInitialized() { mGpuMem->mInitialized.store(true); }
- void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+ void setGpuMemTotalMap(bpf::BpfMapRO<uint64_t, uint64_t>& map) {
mGpuMem->setGpuMemTotalMap(map);
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 5d1d4af..76729ef 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -182,6 +182,7 @@
filegroup {
name: "libinputflinger_base_sources",
srcs: [
+ "InputDeviceMetricsSource.cpp",
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
@@ -199,6 +200,7 @@
"libcutils",
"libinput",
"liblog",
+ "libstatslog",
"libutils",
],
header_libs: [
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index 5693848..f848c82 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -17,6 +17,7 @@
#pragma once
#include <condition_variable>
+#include <functional>
#include <list>
#include <mutex>
#include <optional>
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 56a3fb4..cefb140 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -17,11 +17,10 @@
#define LOG_TAG "InputDeviceMetricsCollector"
#include "InputDeviceMetricsCollector.h"
-#include "KeyCodeClassifications.h"
+#include "InputDeviceMetricsSource.h"
#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
-#include <linux/input.h>
namespace android {
@@ -64,14 +63,13 @@
class : public InputDeviceMetricsLogger {
nanoseconds getCurrentTime() override { return nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); }
- void logInputDeviceUsageReported(const InputDeviceInfo& info,
+ void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
const DeviceUsageReport& report) override {
const int32_t durationMillis =
std::chrono::duration_cast<std::chrono::milliseconds>(report.usageDuration).count();
const static std::vector<int32_t> empty;
- const auto& identifier = info.getIdentifier();
- ALOGD_IF(DEBUG, "Usage session reported for device: %s", identifier.name.c_str());
+ ALOGD_IF(DEBUG, "Usage session reported for device id: %d", info.deviceId);
ALOGD_IF(DEBUG, " Total duration: %dms", durationMillis);
ALOGD_IF(DEBUG, " Source breakdown:");
@@ -96,11 +94,9 @@
ALOGD_IF(DEBUG, " - uid: %s\t duration: %dms", uid.toString().c_str(),
durMillis);
}
- util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
- identifier.version,
- linuxBusToInputDeviceBusEnum(identifier.bus,
- info.getUsiVersion().has_value()),
- durationMillis, sources, durationsPerSource, uids, durationsPerUid);
+ util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, info.vendor, info.product, info.version,
+ linuxBusToInputDeviceBusEnum(info.bus, info.isUsiStylus), durationMillis,
+ sources, durationsPerSource, uids, durationsPerUid);
}
} sStatsdLogger;
@@ -116,96 +112,6 @@
} // namespace
-InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo& info,
- const NotifyKeyArgs& keyArgs) {
- if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
- return InputDeviceUsageSource::UNKNOWN;
- }
-
- if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
- DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
- return InputDeviceUsageSource::DPAD;
- }
-
- if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
- GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
- return InputDeviceUsageSource::GAMEPAD;
- }
-
- if (info.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
- return InputDeviceUsageSource::KEYBOARD;
- }
-
- return InputDeviceUsageSource::BUTTONS;
-}
-
-std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
- LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
- std::set<InputDeviceUsageSource> sources;
-
- for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
- const auto toolType = motionArgs.pointerProperties[i].toolType;
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
- if (toolType == ToolType::MOUSE) {
- sources.emplace(InputDeviceUsageSource::MOUSE);
- continue;
- }
- if (toolType == ToolType::FINGER) {
- sources.emplace(InputDeviceUsageSource::TOUCHPAD);
- continue;
- }
- if (isStylusToolType(toolType)) {
- sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
- continue;
- }
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
- toolType == ToolType::MOUSE) {
- sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
- toolType == ToolType::FINGER) {
- sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
- isStylusToolType(toolType)) {
- sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
- sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
- sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
- sources.emplace(InputDeviceUsageSource::JOYSTICK);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
- sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
- sources.emplace(InputDeviceUsageSource::TRACKBALL);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
- sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
- continue;
- }
- sources.emplace(InputDeviceUsageSource::UNKNOWN);
- }
-
- return sources;
-}
-
-// --- InputDeviceMetricsCollector ---
-
InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
: InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
@@ -232,8 +138,8 @@
void InputDeviceMetricsCollector::notifyKey(const NotifyKeyArgs& args) {
reportCompletedSessions();
- const SourceProvider getSources = [&args](const InputDeviceInfo& info) {
- return std::set{getUsageSourceForKeyArgs(info, args)};
+ const SourceProvider getSources = [&args](const MetricsDeviceInfo& info) {
+ return std::set{getUsageSourceForKeyArgs(info.keyboardType, args)};
};
onInputDeviceUsage(DeviceId{args.deviceId}, nanoseconds(args.eventTime), getSources);
@@ -291,13 +197,23 @@
}
void InputDeviceMetricsCollector::onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) {
- std::map<DeviceId, InputDeviceInfo> newDeviceInfos;
+ std::map<DeviceId, MetricsDeviceInfo> newDeviceInfos;
for (const InputDeviceInfo& info : infos) {
if (isIgnoredInputDeviceId(info.getId())) {
continue;
}
- newDeviceInfos.emplace(info.getId(), info);
+ const auto& i = info.getIdentifier();
+ newDeviceInfos.emplace(info.getId(),
+ MetricsDeviceInfo{
+ .deviceId = info.getId(),
+ .vendor = i.vendor,
+ .product = i.product,
+ .version = i.version,
+ .bus = i.bus,
+ .isUsiStylus = info.getUsiVersion().has_value(),
+ .keyboardType = info.getKeyboardType(),
+ });
}
for (auto [deviceId, info] : mLoggedDeviceInfos) {
@@ -311,7 +227,7 @@
}
void InputDeviceMetricsCollector::onInputDeviceRemoved(DeviceId deviceId,
- const InputDeviceInfo& info) {
+ const MetricsDeviceInfo& info) {
auto it = mActiveUsageSessions.find(deviceId);
if (it == mActiveUsageSessions.end()) {
return;
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 1f7c5d9..9633664 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -16,6 +16,7 @@
#pragma once
+#include "InputDeviceMetricsSource.h"
#include "InputListener.h"
#include "NotifyArgs.h"
#include "SyncQueue.h"
@@ -23,7 +24,6 @@
#include <ftl/mixins.h>
#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
-#include <statslog.h>
#include <chrono>
#include <functional>
#include <map>
@@ -52,38 +52,6 @@
virtual void dump(std::string& dump) = 0;
};
-/**
- * Enum representation of the InputDeviceUsageSource.
- */
-enum class InputDeviceUsageSource : int32_t {
- UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
- BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
- KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
- DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
- GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
- JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
- MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
- MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
- TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
- TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
- ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
- STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
- STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
- STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
- TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
- TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
- TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
-
- ftl_first = UNKNOWN,
- ftl_last = TRACKBALL,
-};
-
-/** Returns the InputDeviceUsageSource that corresponds to the key event. */
-InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo&, const NotifyKeyArgs&);
-
-/** Returns the InputDeviceUsageSources that correspond to the motion event. */
-std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
-
/** The logging interface for the metrics collector, injected for testing. */
class InputDeviceMetricsLogger {
public:
@@ -110,7 +78,19 @@
UidUsageBreakdown uidBreakdown;
};
- virtual void logInputDeviceUsageReported(const InputDeviceInfo&, const DeviceUsageReport&) = 0;
+ // A subset of information from the InputDeviceInfo class that is used for metrics collection,
+ // used to avoid copying and storing all of the fields and strings in InputDeviceInfo.
+ struct MetricsDeviceInfo {
+ int32_t deviceId;
+ int32_t vendor;
+ int32_t product;
+ int32_t version;
+ int32_t bus;
+ bool isUsiStylus;
+ int32_t keyboardType;
+ };
+ virtual void logInputDeviceUsageReported(const MetricsDeviceInfo&,
+ const DeviceUsageReport&) = 0;
virtual ~InputDeviceMetricsLogger() = default;
};
@@ -153,8 +133,9 @@
}
using Uid = gui::Uid;
+ using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo;
- std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos;
+ std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos;
using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
SyncQueue<Interaction> mInteractionsQueue;
@@ -188,8 +169,9 @@
std::map<DeviceId, ActiveSession> mActiveUsageSessions;
void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos);
- void onInputDeviceRemoved(DeviceId deviceId, const InputDeviceInfo& info);
- using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>;
+ void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info);
+ using SourceProvider =
+ std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>;
void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
const SourceProvider& getSources);
void onInputDeviceInteraction(const Interaction&);
diff --git a/services/inputflinger/InputDeviceMetricsSource.cpp b/services/inputflinger/InputDeviceMetricsSource.cpp
new file mode 100644
index 0000000..dee4cb8
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsSource.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputDeviceMetricsSource.h"
+
+#include "KeyCodeClassifications.h"
+
+#include <android/input.h>
+#include <input/Input.h>
+#include <linux/input.h>
+#include <log/log_main.h>
+
+#include <set>
+
+namespace android {
+
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
+ const NotifyKeyArgs& keyArgs) {
+ if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
+ return InputDeviceUsageSource::UNKNOWN;
+ }
+
+ if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
+ DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
+ return InputDeviceUsageSource::DPAD;
+ }
+
+ if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
+ GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
+ return InputDeviceUsageSource::GAMEPAD;
+ }
+
+ if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ return InputDeviceUsageSource::KEYBOARD;
+ }
+
+ return InputDeviceUsageSource::BUTTONS;
+}
+
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
+ LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
+ std::set<InputDeviceUsageSource> sources;
+
+ for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
+ const auto toolType = motionArgs.pointerProperties[i].toolType;
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
+ if (toolType == ToolType::MOUSE) {
+ sources.emplace(InputDeviceUsageSource::MOUSE);
+ continue;
+ }
+ if (toolType == ToolType::FINGER) {
+ sources.emplace(InputDeviceUsageSource::TOUCHPAD);
+ continue;
+ }
+ if (isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
+ continue;
+ }
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
+ toolType == ToolType::MOUSE) {
+ sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
+ toolType == ToolType::FINGER) {
+ sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
+ isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
+ sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
+ sources.emplace(InputDeviceUsageSource::JOYSTICK);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
+ sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
+ sources.emplace(InputDeviceUsageSource::TRACKBALL);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+ sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
+ continue;
+ }
+ sources.emplace(InputDeviceUsageSource::UNKNOWN);
+ }
+
+ return sources;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputDeviceMetricsSource.h b/services/inputflinger/InputDeviceMetricsSource.h
new file mode 100644
index 0000000..a6be8f4
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsSource.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "NotifyArgs.h"
+
+#include <linux/input.h>
+#include <statslog.h>
+
+namespace android {
+
+/**
+ * Enum representation of the InputDeviceUsageSource.
+ */
+enum class InputDeviceUsageSource : int32_t {
+ UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
+ BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
+ KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
+ DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
+ GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
+ JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
+ MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
+ MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
+ TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
+ TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
+ ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
+ STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
+ STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
+ STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
+ TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
+ TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
+ TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
+
+ ftl_first = UNKNOWN,
+ ftl_last = TRACKBALL,
+ // Used by latency fuzzer
+ kMaxValue = ftl_last
+};
+
+/** Returns the InputDeviceUsageSource that corresponds to the key event. */
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
+
+/** Returns the InputDeviceUsageSources that correspond to the motion event. */
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
+
+} // namespace android
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index f889de5..0f62324 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -18,6 +18,7 @@
#include "UnwantedInteractionBlocker.h"
#include <android-base/stringprintf.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
#include <inttypes.h>
@@ -28,6 +29,8 @@
#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h"
+namespace input_flags = com::android::input::flags;
+
using android::base::StringPrintf;
/**
@@ -344,10 +347,14 @@
ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
{ // acquire lock
std::scoped_lock lock(mLock);
- const std::vector<NotifyMotionArgs> processedArgs =
- mPreferStylusOverTouchBlocker.processMotion(args);
- for (const NotifyMotionArgs& loopArgs : processedArgs) {
- notifyMotionLocked(loopArgs);
+ if (input_flags::enable_multi_device_input()) {
+ notifyMotionLocked(args);
+ } else {
+ const std::vector<NotifyMotionArgs> processedArgs =
+ mPreferStylusOverTouchBlocker.processMotion(args);
+ for (const NotifyMotionArgs& loopArgs : processedArgs) {
+ notifyMotionLocked(loopArgs);
+ }
}
} // release lock
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index ed95de7..f304712 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -38,13 +38,4 @@
return "?";
}
-std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
- for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
- if ((*it)->seq == seq) {
- return it;
- }
- }
- return waitQueue.end();
-}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index 2929d61..c17baea 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -53,11 +53,11 @@
bool responsive = true;
// Queue of events that need to be published to the connection.
- std::deque<DispatchEntry*> outboundQueue;
+ std::deque<std::unique_ptr<DispatchEntry>> outboundQueue;
// Queue of events that have been published to the connection but that have not
// yet received a "finished" response from the application.
- std::deque<DispatchEntry*> waitQueue;
+ std::deque<std::unique_ptr<DispatchEntry>> waitQueue;
Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
const IdGenerator& idGenerator);
@@ -65,8 +65,6 @@
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
const std::string getWindowName() const;
-
- std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index dd4aab8..98e2507 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -226,7 +226,7 @@
const uint32_t seq; // unique sequence number, never 0
std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
- ftl::Flags<InputTarget::Flags> targetFlags;
+ const ftl::Flags<InputTarget::Flags> targetFlags;
ui::Transform transform;
ui::Transform rawTransform;
float globalScaleFactor;
@@ -244,6 +244,8 @@
DispatchEntry(std::shared_ptr<EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform,
const ui::Transform& rawTransform, float globalScaleFactor);
+ DispatchEntry(const DispatchEntry&) = delete;
+ DispatchEntry& operator=(const DispatchEntry&) = delete;
inline bool hasForegroundTarget() const {
return targetFlags.test(InputTarget::Flags::FOREGROUND);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4657c01..276f75c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -25,6 +25,7 @@
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <log/log_event_list.h>
#if defined(__ANDROID__)
@@ -46,6 +47,8 @@
#include <queue>
#include <sstream>
+#include "../InputDeviceMetricsSource.h"
+
#include "Connection.h"
#include "DebugConfig.h"
#include "InputDispatcher.h"
@@ -67,6 +70,7 @@
using android::gui::WindowInfoHandle;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
+namespace input_flags = com::android::input::flags;
namespace android::inputdispatcher {
@@ -271,7 +275,8 @@
return dump;
}
-std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+std::string dumpQueue(const std::deque<std::unique_ptr<DispatchEntry>>& queue,
+ nsecs_t currentTime) {
constexpr size_t maxEntries = 50; // max events to print
constexpr size_t skipBegin = maxEntries / 2;
const size_t skipEnd = queue.size() - maxEntries / 2;
@@ -492,8 +497,8 @@
*/
bool isConnectionResponsive(const Connection& connection) {
const nsecs_t currentTime = now();
- for (const DispatchEntry* entry : connection.waitQueue) {
- if (entry->timeoutTime < currentTime) {
+ for (const auto& dispatchEntry : connection.waitQueue) {
+ if (dispatchEntry->timeoutTime < currentTime) {
return false;
}
}
@@ -669,11 +674,11 @@
// This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) {
android::base::LogSeverity severity = android::base::LogSeverity::FATAL;
- if (entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
+ if (!input_flags::a11y_crash_on_inconsistent_event_stream() &&
+ entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
// The Accessibility injected touch exploration event stream
// has known inconsistencies, so log ERROR instead of
// crashing the device with FATAL.
- // TODO(b/299977100): Move a11y severity back to FATAL.
severity = android::base::LogSeverity::ERROR;
}
LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
@@ -1293,7 +1298,7 @@
}
std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
- int32_t displayId, const sp<WindowInfoHandle>& touchedWindow) const {
+ int32_t displayId, const sp<WindowInfoHandle>& touchedWindow, int32_t pointerId) const {
if (touchedWindow == nullptr) {
return {};
}
@@ -1309,9 +1314,10 @@
const WindowInfo& info = *windowHandle->getInfo();
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
- addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
- /*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
- outsideTargets);
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(pointerId);
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE, pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
}
}
return outsideTargets;
@@ -2333,6 +2339,7 @@
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
const int32_t pointerIndex = MotionEvent::getActionIndex(action);
+ const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
@@ -2340,7 +2347,7 @@
findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (isDown) {
- targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle);
+ targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointerId);
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
@@ -2398,7 +2405,6 @@
if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- const int32_t pointerId = entry.pointerProperties[0].id;
// The "windowHandle" is the target of this hovering pointer.
tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
}
@@ -2423,7 +2429,7 @@
// Update the temporary touch state.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
if (!isHoverAction) {
- pointerIds.set(entry.pointerProperties[pointerIndex].id);
+ pointerIds.set(pointerId);
}
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
@@ -2467,7 +2473,6 @@
// If a window is already pilfering some pointers, give it this new pointer as well and
// make it pilfering. This will prevent other non-spy windows from getting this pointer,
// which is a specific behaviour that we want.
- const int32_t pointerId = entry.pointerProperties[pointerIndex].id;
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.hasTouchingPointer(entry.deviceId, pointerId) &&
touchedWindow.hasPilferingPointers(entry.deviceId)) {
@@ -3415,7 +3420,7 @@
}
// Enqueue the dispatch entry.
- connection->outboundQueue.push_back(dispatchEntry.release());
+ connection->outboundQueue.emplace_back(std::move(dispatchEntry));
traceOutboundQueueLength(*connection);
}
@@ -3584,7 +3589,7 @@
}
while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
- DispatchEntry* dispatchEntry = connection->outboundQueue.front();
+ std::unique_ptr<DispatchEntry>& dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection);
dispatchEntry->timeoutTime = currentTime + timeout.count();
@@ -3699,14 +3704,12 @@
}
// Re-enqueue the event on the wait queue.
- connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
- connection->outboundQueue.end(),
- dispatchEntry));
+ const nsecs_t timeoutTime = dispatchEntry->timeoutTime;
+ connection->waitQueue.emplace_back(std::move(dispatchEntry));
+ connection->outboundQueue.erase(connection->outboundQueue.begin());
traceOutboundQueueLength(*connection);
- connection->waitQueue.push_back(dispatchEntry);
if (connection->responsive) {
- mAnrTracker.insert(dispatchEntry->timeoutTime,
- connection->inputChannel->getConnectionToken());
+ mAnrTracker.insert(timeoutTime, connection->inputChannel->getConnectionToken());
}
traceWaitQueueLength(*connection);
}
@@ -3805,19 +3808,17 @@
}
}
-void InputDispatcher::drainDispatchQueue(std::deque<DispatchEntry*>& queue) {
+void InputDispatcher::drainDispatchQueue(std::deque<std::unique_ptr<DispatchEntry>>& queue) {
while (!queue.empty()) {
- DispatchEntry* dispatchEntry = queue.front();
+ releaseDispatchEntry(std::move(queue.front()));
queue.pop_front();
- releaseDispatchEntry(dispatchEntry);
}
}
-void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
+void InputDispatcher::releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispatchEntry) {
if (dispatchEntry->hasForegroundTarget()) {
decrementPendingForegroundDispatches(*(dispatchEntry->eventEntry));
}
- delete dispatchEntry;
}
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
@@ -3924,6 +3925,16 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
+ if ((options.mode == CancelationOptions::Mode::CANCEL_POINTER_EVENTS ||
+ options.mode == CancelationOptions::Mode::CANCEL_ALL_EVENTS) &&
+ mDragState && mDragState->dragWindow->getToken() == connection->inputChannel->getToken()) {
+ LOG(INFO) << __func__
+ << ": Canceling drag and drop because the pointers for the drag window are being "
+ "canceled.";
+ sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
+ mDragState.reset();
+ }
+
if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3947,7 +3958,6 @@
android_log_event_list(LOGTAG_INPUT_CANCEL)
<< connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
- InputTarget target;
sp<WindowInfoHandle> windowHandle;
if (options.displayId) {
windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken(),
@@ -3955,27 +3965,47 @@
} else {
windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken());
}
- if (windowHandle != nullptr) {
- const WindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerTransform(windowInfo->transform);
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- }
- target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
const bool wasEmpty = connection->outboundQueue.empty();
for (size_t i = 0; i < cancelationEvents.size(); i++) {
std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
+ std::vector<InputTarget> targets{};
+ // The target to use if we don't find a window associated with the channel.
+ const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
+ .flags = InputTarget::Flags::DISPATCH_AS_IS};
+
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
- logOutboundKeyDetails("cancel - ",
- static_cast<const KeyEntry&>(*cancelationEventEntry));
+ const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
+ if (windowHandle != nullptr) {
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+ /*pointerIds=*/{}, keyEntry.downTime, targets);
+ } else {
+ targets.emplace_back(fallbackTarget);
+ }
+ logOutboundKeyDetails("cancel - ", keyEntry);
break;
}
case EventEntry::Type::MOTION: {
- logOutboundMotionDetails("cancel - ",
- static_cast<const MotionEntry&>(*cancelationEventEntry));
+ const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
+ if (windowHandle != nullptr) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+ pointerIndex++) {
+ pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
+ }
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+ pointerIds, motionEntry.downTime, targets);
+ } else {
+ targets.emplace_back(fallbackTarget);
+ const auto it = mDisplayInfos.find(motionEntry.displayId);
+ if (it != mDisplayInfos.end()) {
+ targets.back().displayTransform = it->second.transform;
+ targets.back().setDefaultPointerTransform(it->second.transform);
+ }
+ }
+ logOutboundMotionDetails("cancel - ", motionEntry);
break;
}
case EventEntry::Type::FOCUS:
@@ -3995,7 +4025,8 @@
}
}
- enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
+ if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+ enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0],
InputTarget::Flags::DISPATCH_AS_IS);
}
@@ -4024,23 +4055,33 @@
connection->getInputChannelName().c_str(), downEvents.size());
}
- InputTarget target;
sp<WindowInfoHandle> windowHandle =
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
- if (windowHandle != nullptr) {
- const WindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerTransform(windowInfo->transform);
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- }
- target.inputChannel = connection->inputChannel;
- target.flags = targetFlags;
const bool wasEmpty = connection->outboundQueue.empty();
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
+ std::vector<InputTarget> targets{};
switch (downEventEntry->type) {
case EventEntry::Type::MOTION: {
- logOutboundMotionDetails("down - ",
- static_cast<const MotionEntry&>(*downEventEntry));
+ const auto& motionEntry = static_cast<const MotionEntry&>(*downEventEntry);
+ if (windowHandle != nullptr) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+ pointerIndex++) {
+ pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
+ }
+ addWindowTargetLocked(windowHandle, targetFlags, pointerIds,
+ motionEntry.downTime, targets);
+ } else {
+ targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel,
+ .flags = targetFlags});
+ const auto it = mDisplayInfos.find(motionEntry.displayId);
+ if (it != mDisplayInfos.end()) {
+ targets.back().displayTransform = it->second.transform;
+ targets.back().setDefaultPointerTransform(it->second.transform);
+ }
+ }
+ logOutboundMotionDetails("down - ", motionEntry);
break;
}
@@ -4058,7 +4099,8 @@
}
}
- enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
+ if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+ enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0],
InputTarget::Flags::DISPATCH_AS_IS);
}
@@ -4186,6 +4228,11 @@
return splitMotionEntry;
}
+void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ std::scoped_lock _l(mLock);
+ mLatencyTracker.setInputDevices(args.inputDeviceInfos);
+}
+
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
if (debugInboundEventDetails()) {
ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime);
@@ -4398,7 +4445,9 @@
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
- mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);
+ std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
+ mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime,
+ args.deviceId, sources);
}
needWake = enqueueInboundEventLocked(std::move(newEntry));
@@ -4531,7 +4580,7 @@
// the injected event, it is responsible for setting POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY.
// For those events, we will set FLAG_IS_ACCESSIBILITY_EVENT to allow apps to distinguish them
// from events that originate from actual hardware.
- int32_t resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
+ DeviceId resolvedDeviceId = VIRTUAL_KEYBOARD_ID;
if (policyFlags & POLICY_FLAG_FILTERED) {
resolvedDeviceId = event->getDeviceId();
}
@@ -5918,14 +5967,16 @@
status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {
const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token);
if (!requestingChannel) {
- ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
+ LOG(WARNING)
+ << "Attempted to pilfer pointers from an un-registered channel or invalid token";
return BAD_VALUE;
}
auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
if (statePtr == nullptr || windowPtr == nullptr) {
- ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
- " Ignoring.");
+ LOG(WARNING)
+ << "Attempted to pilfer points from a channel without any on-going pointer streams."
+ " Ignoring.";
return BAD_VALUE;
}
std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
@@ -5934,36 +5985,38 @@
<< " in window: " << windowPtr->dump();
return BAD_VALUE;
}
- const int32_t deviceId = *deviceIds.begin();
- TouchState& state = *statePtr;
- TouchedWindow& window = *windowPtr;
- // Send cancel events to all the input channels we're stealing from.
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "input channel stole pointer stream");
- options.deviceId = deviceId;
- options.displayId = displayId;
- std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
- options.pointerIds = pointerIds;
- std::string canceledWindows;
- for (const TouchedWindow& w : state.windows) {
- const std::shared_ptr<InputChannel> channel =
- getInputChannelLocked(w.windowHandle->getToken());
- if (channel != nullptr && channel->getConnectionToken() != token) {
- synthesizeCancelationEventsForInputChannelLocked(channel, options);
- canceledWindows += canceledWindows.empty() ? "[" : ", ";
- canceledWindows += channel->getName();
+ for (const DeviceId deviceId : deviceIds) {
+ TouchState& state = *statePtr;
+ TouchedWindow& window = *windowPtr;
+ // Send cancel events to all the input channels we're stealing from.
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "input channel stole pointer stream");
+ options.deviceId = deviceId;
+ options.displayId = displayId;
+ std::bitset<MAX_POINTER_ID + 1> pointerIds = window.getTouchingPointers(deviceId);
+ options.pointerIds = pointerIds;
+ std::string canceledWindows;
+ for (const TouchedWindow& w : state.windows) {
+ const std::shared_ptr<InputChannel> channel =
+ getInputChannelLocked(w.windowHandle->getToken());
+ if (channel != nullptr && channel->getConnectionToken() != token) {
+ synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ canceledWindows += canceledWindows.empty() ? "[" : ", ";
+ canceledWindows += channel->getName();
+ }
}
+ canceledWindows += canceledWindows.empty() ? "[]" : "]";
+ LOG(INFO) << "Channel " << requestingChannel->getName()
+ << " is stealing input gesture for device " << deviceId << " from "
+ << canceledWindows;
+
+ // Prevent the gesture from being sent to any other windows.
+ // This only blocks relevant pointers to be sent to other windows
+ window.addPilferingPointers(deviceId, pointerIds);
+
+ state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
}
- canceledWindows += canceledWindows.empty() ? "[]" : "]";
- ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(),
- canceledWindows.c_str());
-
- // Prevent the gesture from being sent to any other windows.
- // This only blocks relevant pointers to be sent to other windows
- window.addPilferingPointers(deviceId, pointerIds);
-
- state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
return OK;
}
@@ -6061,43 +6114,52 @@
uint32_t seq, bool handled,
nsecs_t consumeTime) {
// Handle post-event policy actions.
- std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
- if (dispatchEntryIt == connection->waitQueue.end()) {
- return;
- }
- DispatchEntry* dispatchEntry = *dispatchEntryIt;
- const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
- if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
- ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
- ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
- }
- if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
- mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
- connection->inputChannel->getConnectionToken(),
- dispatchEntry->deliveryTime, consumeTime, finishTime);
- }
-
bool restartEvent;
- if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
- KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
- restartEvent =
- afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
- MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
- restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
- handled);
- } else {
- restartEvent = false;
- }
+
+ { // Start critical section
+ auto dispatchEntryIt =
+ std::find_if(connection->waitQueue.begin(), connection->waitQueue.end(),
+ [seq](auto& e) { return e->seq == seq; });
+ if (dispatchEntryIt == connection->waitQueue.end()) {
+ return;
+ }
+
+ DispatchEntry& dispatchEntry = **dispatchEntryIt;
+
+ const nsecs_t eventDuration = finishTime - dispatchEntry.deliveryTime;
+ if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+ ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+ ns2ms(eventDuration), dispatchEntry.eventEntry->getDescription().c_str());
+ }
+ if (shouldReportFinishedEvent(dispatchEntry, *connection)) {
+ mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id,
+ connection->inputChannel->getConnectionToken(),
+ dispatchEntry.deliveryTime, consumeTime, finishTime);
+ }
+
+ if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
+ KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry.eventEntry));
+ restartEvent =
+ afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+ } else if (dispatchEntry.eventEntry->type == EventEntry::Type::MOTION) {
+ MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry.eventEntry));
+ restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry,
+ motionEntry, handled);
+ } else {
+ restartEvent = false;
+ }
+ } // End critical section: The -LockedInterruptable methods may have released the lock.
// Dequeue the event and start the next cycle.
// Because the lock might have been released, it is possible that the
// contents of the wait queue to have been drained, so we need to double-check
// a few things.
- dispatchEntryIt = connection->findWaitQueueEntry(seq);
- if (dispatchEntryIt != connection->waitQueue.end()) {
- dispatchEntry = *dispatchEntryIt;
- connection->waitQueue.erase(dispatchEntryIt);
+ auto entryIt = std::find_if(connection->waitQueue.begin(), connection->waitQueue.end(),
+ [seq](auto& e) { return e->seq == seq; });
+ if (entryIt != connection->waitQueue.end()) {
+ std::unique_ptr<DispatchEntry> dispatchEntry = std::move(*entryIt);
+ connection->waitQueue.erase(entryIt);
+
const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
if (!connection->responsive) {
@@ -6109,10 +6171,10 @@
}
traceWaitQueueLength(*connection);
if (restartEvent && connection->status == Connection::Status::NORMAL) {
- connection->outboundQueue.push_front(dispatchEntry);
+ connection->outboundQueue.emplace_front(std::move(dispatchEntry));
traceOutboundQueueLength(*connection);
} else {
- releaseDispatchEntry(dispatchEntry);
+ releaseDispatchEntry(std::move(dispatchEntry));
}
}
@@ -6156,13 +6218,13 @@
* processes the events linearly. So providing information about the oldest entry seems to be
* most useful.
*/
- DispatchEntry* oldestEntry = *connection->waitQueue.begin();
- const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
+ DispatchEntry& oldestEntry = *connection->waitQueue.front();
+ const nsecs_t currentWait = now() - oldestEntry.deliveryTime;
std::string reason =
android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
connection->inputChannel->getName().c_str(),
ns2ms(currentWait),
- oldestEntry->eventEntry->getDescription().c_str());
+ oldestEntry.eventEntry->getDescription().c_str());
sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();
updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
@@ -6299,7 +6361,7 @@
}
bool InputDispatcher::afterKeyEventLockedInterruptable(
- const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+ const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
KeyEntry& keyEntry, bool handled) {
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
@@ -6317,7 +6379,7 @@
connection->inputState.removeFallbackKey(originalKeyCode);
}
- if (handled || !dispatchEntry->hasForegroundTarget()) {
+ if (handled || !dispatchEntry.hasForegroundTarget()) {
// If the application handles the original key for which we previously
// generated a fallback or if the window is not a foreground window,
// then cancel the associated fallback key, if any.
@@ -6484,7 +6546,7 @@
}
bool InputDispatcher::afterMotionEventLockedInterruptable(
- const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+ const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
MotionEntry& motionEntry, bool handled) {
return false;
}
@@ -6586,6 +6648,7 @@
}
}
if (changes.newFocus) {
+ resetNoFocusedWindowTimeoutLocked();
enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4d145c6..ee5a797 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -92,7 +92,7 @@
status_t start() override;
status_t stop() override;
- void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override{};
+ void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
void notifyMotion(const NotifyMotionArgs& args) override;
@@ -242,8 +242,8 @@
int32_t displayId, float x, float y, bool isStylus = false,
bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<InputTarget> findOutsideTargetsLocked(
- int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow) const
- REQUIRES(mLock);
+ int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
+ int32_t pointerId) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
@@ -598,8 +598,8 @@
void abortBrokenDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
- void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
- void releaseDispatchEntry(DispatchEntry* dispatchEntry);
+ void drainDispatchQueue(std::deque<std::unique_ptr<DispatchEntry>>& queue);
+ void releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispatchEntry);
int handleReceiveCallback(int events, sp<IBinder> connectionToken);
// The action sent should only be of type AMOTION_EVENT_*
void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
@@ -664,10 +664,10 @@
REQUIRES(mLock);
std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
+ DispatchEntry& dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
+ DispatchEntry& dispatchEntry, MotionEntry& motionEntry,
bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
index 3edb638..a7c6d16 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.cpp
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -16,6 +16,8 @@
#include "InputEventTimeline.h"
+#include "../InputDeviceMetricsSource.h"
+
namespace android::inputdispatcher {
ConnectionTimeline::ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime,
@@ -64,8 +66,15 @@
return !operator==(rhs);
}
-InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime)
- : isDown(isDown), eventTime(eventTime), readTime(readTime) {}
+InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime,
+ uint16_t vendorId, uint16_t productId,
+ std::set<InputDeviceUsageSource> sources)
+ : isDown(isDown),
+ eventTime(eventTime),
+ readTime(readTime),
+ vendorId(vendorId),
+ productId(productId),
+ sources(sources) {}
bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const {
if (connectionTimelines.size() != rhs.connectionTimelines.size()) {
@@ -80,7 +89,8 @@
return false;
}
}
- return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime;
+ return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources;
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index daf375d..e9deb2d 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -16,6 +16,8 @@
#pragma once
+#include "../InputDeviceMetricsSource.h"
+
#include <binder/IBinder.h>
#include <input/Input.h>
#include <unordered_map>
@@ -73,10 +75,14 @@
};
struct InputEventTimeline {
- InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime);
+ InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
+ uint16_t productId, std::set<InputDeviceUsageSource> sources);
const bool isDown; // True if this is an ACTION_DOWN event
const nsecs_t eventTime;
const nsecs_t readTime;
+ const uint16_t vendorId;
+ const uint16_t productId;
+ const std::set<InputDeviceUsageSource> sources;
struct IBinderHash {
std::size_t operator()(const sp<IBinder>& b) const {
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index ccffe26..b21427d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -83,6 +83,11 @@
}
}
+/**
+ * Return:
+ * true if the incoming event was correctly tracked,
+ * false if the incoming event should be dropped.
+ */
bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
switch (actionMasked) {
@@ -310,7 +315,7 @@
nsecs_t currentTime) {
std::vector<std::unique_ptr<EventEntry>> events;
for (MotionMemento& memento : mMotionMementos) {
- if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
+ if (!isFromSource(memento.source, AINPUT_SOURCE_CLASS_POINTER)) {
continue;
}
@@ -443,7 +448,7 @@
MotionMemento& memento = mMotionMementos[i];
// Since we support split pointers we need to merge touch events
// from the same source + device + screen.
- if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (isFromSource(memento.source, AINPUT_SOURCE_CLASS_POINTER)) {
bool merged = false;
for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
MotionMemento& otherMemento = other.mMotionMementos[j];
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 11f3413..343630c 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -16,7 +16,9 @@
#include "InputTarget.h"
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <input/PrintTools.h>
#include <inttypes.h>
#include <string>
@@ -34,7 +36,10 @@
}
// Ensure that the new set of pointers doesn't overlap with the current set of pointers.
- LOG_ALWAYS_FATAL_IF((pointerIds & newPointerIds).any());
+ if ((pointerIds & newPointerIds).any()) {
+ LOG(FATAL) << __func__ << " - overlap with incoming pointers "
+ << bitsetToString(newPointerIds) << " in " << *this;
+ }
pointerIds |= newPointerIds;
for (size_t i = 0; i < newPointerIds.size(); i++) {
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index b7c36a8..698bd9f 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "LatencyTracker"
#include "LatencyTracker.h"
+#include "../InputDeviceMetricsSource.h"
#include <inttypes.h>
@@ -23,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <log/log.h>
using android::base::HwTimeoutMultiplier;
@@ -66,7 +68,8 @@
}
void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime,
- nsecs_t readTime) {
+ nsecs_t readTime, DeviceId deviceId,
+ const std::set<InputDeviceUsageSource>& sources) {
reportAndPruneMatureRecords(eventTime);
const auto it = mTimelines.find(inputEventId);
if (it != mTimelines.end()) {
@@ -78,7 +81,29 @@
eraseByValue(mEventTimes, inputEventId);
return;
}
- mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
+
+ // Create an InputEventTimeline for the device ID. The vendorId and productId
+ // can be obtained from the InputDeviceIdentifier of the particular device.
+ const InputDeviceIdentifier* identifier = nullptr;
+ for (auto& inputDevice : mInputDevices) {
+ if (deviceId == inputDevice.getId()) {
+ identifier = &inputDevice.getIdentifier();
+ break;
+ }
+ }
+
+ // If no matching ids can be found for the device from among the input devices connected,
+ // the call to trackListener will be dropped.
+ // Note: there generally isn't expected to be a situation where we can't find an InputDeviceInfo
+ // but a possibility of it is handled in case of race conditions
+ if (identifier == nullptr) {
+ ALOGE("Could not find input device identifier. Dropping call to LatencyTracker.");
+ return;
+ }
+
+ mTimelines.emplace(inputEventId,
+ InputEventTimeline(isDown, eventTime, readTime, identifier->vendor,
+ identifier->product, sources));
mEventTimes.emplace(eventTime, inputEventId);
}
@@ -171,4 +196,8 @@
StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
}
+void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) {
+ mInputDevices = inputDevices;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 4212da8..890d61d 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -16,6 +16,8 @@
#pragma once
+#include "../InputDeviceMetricsSource.h"
+
#include <map>
#include <unordered_map>
@@ -23,6 +25,7 @@
#include <input/Input.h>
#include "InputEventTimeline.h"
+#include "NotifyArgs.h"
namespace android::inputdispatcher {
@@ -49,13 +52,15 @@
* duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
* must drop all duplicate data.
*/
- void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
+ void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime,
+ DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources);
void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
std::string dump(const char* prefix) const;
+ void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
private:
/**
@@ -76,6 +81,7 @@
std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes;
InputEventTimelineProcessor* mTimelineProcessor;
+ std::vector<InputDeviceInfo> mInputDevices;
void reportAndPruneMatureRecords(nsecs_t newEventTime);
};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index d87a5a7..64e8825 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -50,6 +50,7 @@
"HardwareProperties_test.cpp",
"HardwareStateConverter_test.cpp",
"InputDeviceMetricsCollector_test.cpp",
+ "InputDeviceMetricsSource_test.cpp",
"InputMapperTest.cpp",
"InputProcessor_test.cpp",
"InputProcessorConverter_test.cpp",
@@ -65,7 +66,6 @@
"SyncQueue_test.cpp",
"TimerProvider_test.cpp",
"TestInputListener.cpp",
- "TestInputListenerMatchers.cpp",
"TouchpadInputMapper_test.cpp",
"KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
@@ -93,6 +93,7 @@
},
},
static_libs: [
+ "libflagtest",
"libc++fs",
"libgmock",
],
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 99a6a1f..b738abf 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -29,8 +29,8 @@
#include "FakeInputReaderPolicy.h"
#include "InstrumentedInputReader.h"
#include "TestConstants.h"
+#include "TestEventMatchers.h"
#include "TestInputListener.h"
-#include "TestInputListenerMatchers.h"
namespace android {
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index e630915..b55c9cc 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -22,7 +22,7 @@
#include "FakePointerController.h"
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
-#include "TestInputListenerMatchers.h"
+#include "TestEventMatchers.h"
#define TAG "CursorInputMapper_test"
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 74ce359..d7dc800 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -27,8 +27,8 @@
#include "InstrumentedInputReader.h"
#include "NotifyArgs.h"
#include "TestConstants.h"
+#include "TestEventMatchers.h"
#include "TestInputListener.h"
-#include "TestInputListenerMatchers.h"
#include "include/gestures.h"
#include "ui/Rotation.h"
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index 7ccfaca..85e055d 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -36,7 +36,6 @@
constexpr auto USAGE_TIMEOUT = 8765309ns;
constexpr auto TIME = 999999ns;
-constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
constexpr int32_t DEVICE_ID = 3;
constexpr int32_t DEVICE_ID_2 = 4;
@@ -48,10 +47,6 @@
const std::string UNIQUE_ID = "Yosemite";
constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
-constexpr uint32_t KEY_SOURCES =
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
-constexpr int32_t POINTER_1_DOWN =
- AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
InputDeviceIdentifier generateTestIdentifier(int32_t id = DEVICE_ID) {
InputDeviceIdentifier identifier;
@@ -66,21 +61,14 @@
}
InputDeviceInfo generateTestDeviceInfo(int32_t id = DEVICE_ID,
- uint32_t sources = TOUCHSCREEN | STYLUS,
- bool isAlphabetic = false) {
+ uint32_t sources = TOUCHSCREEN | STYLUS) {
auto info = InputDeviceInfo();
info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, generateTestIdentifier(id),
"alias", /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
info.addSource(sources);
- info.setKeyboardType(isAlphabetic ? AINPUT_KEYBOARD_TYPE_ALPHABETIC
- : AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
return info;
}
-const InputDeviceInfo ALPHABETIC_KEYBOARD_INFO =
- generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/true);
-const InputDeviceInfo NON_ALPHABETIC_KEYBOARD_INFO =
- generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/false);
const InputDeviceInfo TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID);
const InputDeviceInfo SECOND_TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID_2);
@@ -94,258 +82,6 @@
} // namespace
-// --- InputDeviceMetricsCollectorDeviceClassificationTest ---
-
-class DeviceClassificationFixture : public ::testing::Test,
- public ::testing::WithParamInterface<InputDeviceUsageSource> {};
-
-TEST_P(DeviceClassificationFixture, ValidClassifications) {
- const InputDeviceUsageSource usageSource = GetParam();
-
- // Use a switch to ensure a test is added for all source classifications.
- switch (usageSource) {
- case InputDeviceUsageSource::UNKNOWN: {
- ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
- getUsageSourceForKeyArgs(generateTestDeviceInfo(),
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
- .build()));
-
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
- .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::BUTTONS: {
- ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
- getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::KEYBOARD: {
- ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
- getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::DPAD: {
- ASSERT_EQ(InputDeviceUsageSource::DPAD,
- getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_DPAD_CENTER)
- .build()));
-
- ASSERT_EQ(InputDeviceUsageSource::DPAD,
- getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_DPAD_CENTER)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::GAMEPAD: {
- ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
- getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_BUTTON_A)
- .build()));
-
- ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
- getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_BUTTON_A)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::JOYSTICK: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
- .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
- .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::MOUSE: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
- AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::MOUSE_CAPTURED: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
- AINPUT_SOURCE_MOUSE_RELATIVE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
- .x(100)
- .y(200)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCHPAD: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::ROTARY_ENCODER: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
- AINPUT_SOURCE_ROTARY_ENCODER)
- .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
- .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
- .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::STYLUS_DIRECT: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- STYLUS | TOUCHSCREEN)
- .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::STYLUS_INDIRECT: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::STYLUS_FUSED: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
- .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCH_NAVIGATION: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
- AINPUT_SOURCE_TOUCH_NAVIGATION)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCHSCREEN: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200))
- .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
- .x(300)
- .y(400))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TRACKBALL: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
- AINPUT_SOURCE_TRACKBALL)
- .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
- .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
- .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
- .build()));
- break;
- }
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsCollectorDeviceClassificationTest,
- DeviceClassificationFixture,
- ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
- [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
- return ftl::enum_string(testParamInfo.param);
- });
-
-TEST(InputDeviceMetricsCollectorDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
- InputDeviceUsageSource::STYLUS_DIRECT};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
- .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
- .build()));
-}
-
// --- InputDeviceMetricsCollectorTest ---
class InputDeviceMetricsCollectorTest : public testing::Test, public InputDeviceMetricsLogger {
@@ -358,7 +94,13 @@
std::optional<UidUsageBreakdown> uidBreakdown = {}) {
ASSERT_GE(mLoggedUsageSessions.size(), 1u);
const auto& [loggedInfo, report] = *mLoggedUsageSessions.begin();
- ASSERT_EQ(info.getIdentifier(), loggedInfo.getIdentifier());
+ const auto& i = info.getIdentifier();
+ ASSERT_EQ(info.getId(), loggedInfo.deviceId);
+ ASSERT_EQ(i.vendor, loggedInfo.vendor);
+ ASSERT_EQ(i.product, loggedInfo.product);
+ ASSERT_EQ(i.version, loggedInfo.version);
+ ASSERT_EQ(i.bus, loggedInfo.bus);
+ ASSERT_EQ(info.getUsiVersion().has_value(), loggedInfo.isUsiStylus);
ASSERT_EQ(duration, report.usageDuration);
if (sourceBreakdown) {
ASSERT_EQ(sourceBreakdown, report.sourceBreakdown);
@@ -389,12 +131,12 @@
}
private:
- std::vector<std::tuple<InputDeviceInfo, DeviceUsageReport>> mLoggedUsageSessions;
+ std::vector<std::tuple<MetricsDeviceInfo, DeviceUsageReport>> mLoggedUsageSessions;
nanoseconds mCurrentTime{TIME};
nanoseconds getCurrentTime() override { return mCurrentTime; }
- void logInputDeviceUsageReported(const InputDeviceInfo& info,
+ void logInputDeviceUsageReported(const MetricsDeviceInfo& info,
const DeviceUsageReport& report) override {
mLoggedUsageSessions.emplace_back(info, report);
}
diff --git a/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
new file mode 100644
index 0000000..84ef52c
--- /dev/null
+++ b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../InputDeviceMetricsSource.h"
+
+#include <NotifyArgsBuilders.h>
+
+#include <android/input.h>
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <linux/input.h>
+
+#include <set>
+
+namespace android {
+
+namespace {
+
+constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
+constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
+constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
+constexpr uint32_t KEY_SOURCES =
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
+constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+} // namespace
+
+// --- InputDeviceMetricsSourceDeviceClassificationTest ---
+
+class DeviceClassificationFixture : public ::testing::Test,
+ public ::testing::WithParamInterface<InputDeviceUsageSource> {};
+
+TEST_P(DeviceClassificationFixture, ValidClassifications) {
+ const InputDeviceUsageSource usageSource = GetParam();
+
+ // Use a switch to ensure a test is added for all source classifications.
+ switch (usageSource) {
+ case InputDeviceUsageSource::UNKNOWN: {
+ ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
+ .build()));
+
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::BUTTONS: {
+ ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::KEYBOARD: {
+ ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::DPAD: {
+ ASSERT_EQ(InputDeviceUsageSource::DPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_DPAD_CENTER)
+ .build()));
+
+ ASSERT_EQ(InputDeviceUsageSource::DPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_DPAD_CENTER)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::GAMEPAD: {
+ ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_BUTTON_A)
+ .build()));
+
+ ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_BUTTON_A)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::JOYSTICK: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::MOUSE: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::MOUSE_CAPTURED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+ .x(100)
+ .y(200)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHPAD: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::ROTARY_ENCODER: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+ AINPUT_SOURCE_ROTARY_ENCODER)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
+ .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_DIRECT: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ STYLUS | TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_INDIRECT: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_FUSED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCH_NAVIGATION: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_TOUCH_NAVIGATION)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHSCREEN: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
+ .x(300)
+ .y(400))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TRACKBALL: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+ AINPUT_SOURCE_TRACKBALL)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
+ .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
+ .build()));
+ break;
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsSourceDeviceClassificationTest,
+ DeviceClassificationFixture,
+ ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
+ [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
+ return ftl::enum_string(testParamInfo.param);
+ });
+
+TEST(InputDeviceMetricsSourceDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
+ InputDeviceUsageSource::STYLUS_DIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
+ .build()));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 38970f2..2f63b2a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,7 +16,7 @@
#include "../dispatcher/InputDispatcher.h"
#include "../BlockingQueue.h"
-#include "TestInputListenerMatchers.h"
+#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
#include <android-base/properties.h>
@@ -24,10 +24,13 @@
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
+#include <com_android_input_flags.h>
#include <fcntl.h>
+#include <flag_macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
+#include <input/PrintTools.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -50,6 +53,8 @@
using namespace ftl::flag_operators;
using testing::AllOf;
+namespace {
+
// An arbitrary time value.
static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -70,6 +75,7 @@
static constexpr int32_t ACTION_HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
static constexpr int32_t ACTION_HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
static constexpr int32_t ACTION_HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
+static constexpr int32_t ACTION_SCROLL = AMOTION_EVENT_ACTION_SCROLL;
static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
/**
@@ -130,12 +136,6 @@
using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
-struct PointF {
- float x;
- float y;
- auto operator<=>(const PointF&) const = default;
-};
-
/**
* Return a DOWN key event with KEYCODE_A.
*/
@@ -148,48 +148,6 @@
return event;
}
-static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
- ASSERT_EQ(expectedAction, receivedAction)
- << "expected " << MotionEvent::actionToString(expectedAction) << ", got "
- << MotionEvent::actionToString(receivedAction);
-}
-
-MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
- return arg.getDownTime() == downTime;
-}
-
-MATCHER_P(WithSource, source, "InputEvent with specified source") {
- *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
- << inputEventSourceToString(arg.getSource());
- return arg.getSource() == source;
-}
-
-MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
- return arg.getFlags() == flags;
-}
-
-MATCHER_P2(WithCoords, x, y, "MotionEvent with specified coordinates") {
- if (arg.getPointerCount() != 1) {
- *result_listener << "Expected 1 pointer, got " << arg.getPointerCount();
- return false;
- }
- return arg.getX(/*pointerIndex=*/0) == x && arg.getY(/*pointerIndex=*/0) == y;
-}
-
-MATCHER_P(WithPointerCount, pointerCount, "MotionEvent with specified number of pointers") {
- return arg.getPointerCount() == pointerCount;
-}
-
-MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") {
- // Build a map for the received pointers, by pointer id
- std::map<int32_t /*pointerId*/, PointF> actualPointers;
- for (size_t pointerIndex = 0; pointerIndex < arg.getPointerCount(); pointerIndex++) {
- const int32_t pointerId = arg.getPointerId(pointerIndex);
- actualPointers[pointerId] = {arg.getX(pointerIndex), arg.getY(pointerIndex)};
- }
- return pointers == actualPointers;
-}
-
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -617,6 +575,7 @@
mFilteredEvent = nullptr;
}
};
+} // namespace
// --- InputDispatcherTest ---
@@ -959,7 +918,7 @@
switch (expectedEventType) {
case InputEventType::KEY: {
const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
- EXPECT_EQ(expectedAction, keyEvent.getAction());
+ ASSERT_THAT(keyEvent, WithKeyAction(expectedAction));
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
}
@@ -967,8 +926,7 @@
}
case InputEventType::MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- assertMotionAction(expectedAction, motionEvent.getAction());
-
+ ASSERT_THAT(motionEvent, WithMotionAction(expectedAction));
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
}
@@ -1439,6 +1397,68 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
+class FakeMonitorReceiver {
+public:
+ FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId) {
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher.createInputMonitor(displayId, name, MONITOR_PID);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+ }
+
+ sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+ void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
+ }
+
+ std::optional<int32_t> receiveEvent() {
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ }
+
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
+ void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
+ }
+
+ void consumeMotionPointerDown(int32_t pointerIdx) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
+ /*expectedFlags=*/0);
+ }
+
+ void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ mInputReceiver->consumeMotionEvent(matcher);
+ }
+
+ MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
+
+ void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+ std::unique_ptr<FakeInputReceiver> mInputReceiver;
+};
+
static InputEventInjectionResult injectKey(
InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
@@ -2358,13 +2378,196 @@
}
/**
+ * Hover mouse over a window, and then send ACTION_SCROLL. Ensure both the hover and the scroll
+ * events are delivered to the window.
+ */
+TEST_F(InputDispatcherTest, HoverMoveAndScroll) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // Start hovering in the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+ // Scroll with the mouse
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_SCROLL, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_SCROLL));
+}
+
+using InputDispatcherMultiDeviceTest = InputDispatcherTest;
+
+/**
+ * One window. Stylus down on the window. Next, touch from another device goes down.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownAndTouchDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ // Touch cancels stylus
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId),
+ WithCoords(100, 110)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
+ WithCoords(140, 145)));
+
+ // Touch move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(141, 146)));
+
+ // Subsequent stylus movements are dropped
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->assertNoEvents();
+}
+
+/**
+ * One window and one spy window. Stylus down on the window. Next, touch from another device goes
+ * down.
+ * Similar test as above, but with added SPY window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyAndTouchDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+
+ // Touch move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ // Subsequent stylus movements are dropped
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
+}
+
+/**
+ * One window. Stylus hover on the window. Next, touch from another device goes down.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ // Subsequent stylus movements are ignored
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->assertNoEvents();
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
* down. Then, on the left window, also place second touch pointer down.
* This test tries to reproduce a crash.
* In the buggy implementation, second pointer down on the left window would cause a crash.
*/
-TEST_F(InputDispatcherTest, MultiDeviceSplitTouch) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -2414,8 +2617,8 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
-
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(mouseDeviceId)));
rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Second touch pointer down on left window
@@ -2437,6 +2640,215 @@
}
/**
+ * Two windows: a window on the left and a window on the right.
+ * Mouse is hovered on the left window and stylus is hovered on the right window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHover) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 3;
+ const int32_t mouseDeviceId = 6;
+
+ // Start hovering over the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Stylus hovered on right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(300).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Subsequent HOVER_MOVE events are dispatched correctly.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(310).y(110))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Three windows: a window on the left and a window on the right.
+ * And a spy window that's positioned above all of them.
+ * Stylus down on the left window and remains down. Touch goes down on the right and remains down.
+ * Check the stream that's received by the spy.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 1;
+ const int32_t touchDeviceId = 2;
+
+ // Stylus down on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Stylus movements continue, but are ignored because the touch went down more recently.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(310).y(110))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spyWindow->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Three windows: a window on the left, a window on the right, and a spy window positioned above
+ * both.
+ * Check hover in left window and touch down in the right window.
+ * At first, spy should receive hover, but the touch down should cancel hovering inside spy.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverAndTouchWithSpy) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 1;
+ const int32_t touchDeviceId = 2;
+
+ // Stylus hover on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on the right window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Stylus movements continue, but are ignored because the touch is down.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+
+ // Touch movements continue. They should be delivered to the right window and to the spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(301).y(101))
+ .build());
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spyWindow->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* On a single window, use two different devices: mouse and touch.
* Touch happens first, with two pointers going down, and then the first pointer leaving.
* Mouse is clicked next, which causes the touch stream to be aborted with ACTION_CANCEL.
@@ -2444,7 +2856,7 @@
* because the mouse is currently down, and a POINTER_DOWN event from the touchscreen does not
* represent a new gesture.
*/
-TEST_F(InputDispatcherTest, MixedTouchAndMouseWithPointerDown) {
+TEST_F(InputDispatcherMultiDeviceTest, MixedTouchAndMouseWithPointerDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2502,7 +2914,17 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
.build());
- // The pointer_down event should be ignored
+ // Since we already canceled this touch gesture, it will be ignored until a completely new
+ // gesture is started. This is easier to implement than trying to keep track of the new pointer
+ // and generating an ACTION_DOWN instead of ACTION_POINTER_DOWN.
+ // However, mouse movements should continue to work.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(330).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(mouseDeviceId)));
+
window->assertNoEvents();
}
@@ -2510,7 +2932,7 @@
* Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event cancels
* the injected event.
*/
-TEST_F(InputDispatcherTest, UnfinishedInjectedEvent) {
+TEST_F(InputDispatcherMultiDeviceTest, UnfinishedInjectedEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2556,7 +2978,7 @@
* This test reproduces a crash where there is a mismatch between the downTime and eventTime.
* In the buggy implementation, second finger down on the left window would cause a crash.
*/
-TEST_F(InputDispatcherTest, HoverTapAndSplitTouch) {
+TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -2632,7 +3054,7 @@
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
-TEST_F(InputDispatcherTest, StylusHoverAndTouchTap) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchTap) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2645,25 +3067,24 @@
// Start hovering with stylus
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- AINPUT_SOURCE_STYLUS)
+ MotionEventBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
// Finger down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Try to continue hovering with stylus. Since we are already down, injection should fail
+ // Continue hovering with stylus. Injection will fail because touch is already down.
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
@@ -2682,7 +3103,7 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
// Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2692,8 +3113,8 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
- // No more events
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithDeviceId(stylusDeviceId)));
window->assertNoEvents();
}
@@ -3351,7 +3772,9 @@
/**
* Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
*/
-TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
+TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
+ a11y_crash_on_inconsistent_event_stream))) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3418,22 +3841,17 @@
window->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
-
- // Inject a hover_move from mouse.
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE,
- ADISPLAY_ID_DEFAULT, {{50, 50}});
- motionArgs.xCursorPosition = 50;
- motionArgs.yCursorPosition = 50;
- mDispatcher->notifyMotion(motionArgs);
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
+ .build());
ASSERT_NO_FATAL_FAILURE(
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithSource(AINPUT_SOURCE_MOUSE))));
// Tap on the window
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {{10, 10}}));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
ASSERT_NO_FATAL_FAILURE(
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
WithSource(AINPUT_SOURCE_MOUSE))));
@@ -3442,8 +3860,9 @@
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{10, 10}}));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
ASSERT_NO_FATAL_FAILURE(
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
@@ -3644,12 +4063,12 @@
*/
TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinates) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect{0, 0, 100, 100});
sp<FakeWindowHandle> outsideWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Outside Window",
ADISPLAY_ID_DEFAULT);
outsideWindow->setFrame(Rect{100, 100, 200, 200});
outsideWindow->setWatchOutsideTouch(true);
@@ -4198,6 +4617,88 @@
EXPECT_EQ(80, event->getY(0));
}
+TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+ // The monitor will always receive events in the logical display's coordinate space, because
+ // it does not have a window.
+ FakeMonitorReceiver monitor{*mDispatcher, "Monitor", ADISPLAY_ID_DEFAULT};
+
+ // Send down to the first window.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+ monitor.consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+
+ // Second pointer goes down on second window.
+ mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT,
+ {PointF{50, 100}, PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80)));
+ const std::map<int32_t, PointF> expectedMonitorPointers{{0, PointF{100, 400}},
+ {1, PointF{300, 880}}};
+ monitor.consumeMotionEvent(
+ AllOf(WithMotionAction(POINTER_1_DOWN), WithPointers(expectedMonitorPointers)));
+
+ mDispatcher->cancelCurrentTouch();
+
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 80)));
+ monitor.consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedMonitorPointers)));
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send down to the first window.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+
+ // The pointer is transferred to the second window, and the second window receives it in the
+ // correct coordinate space.
+ mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400)));
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverEnterExitWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send hover move to the second window, and ensure it shows up as hover enter.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880)));
+
+ // Touch down at the same location and ensure a hover exit is synthesized.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880)));
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80), WithRawCoords(300, 880)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverCancelationWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send hover enter to second window
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880)));
+
+ mDispatcher->cancelCurrentTouch();
+
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
/** Ensure consistent behavior of InputDispatcher in all orientations. */
class InputDispatcherDisplayOrientationFixture
: public InputDispatcherDisplayProjectionTest,
@@ -4980,65 +5481,6 @@
mDispatcher->waitForIdle();
}
-class FakeMonitorReceiver {
-public:
- FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId) {
- base::Result<std::unique_ptr<InputChannel>> channel =
- dispatcher->createInputMonitor(displayId, name, MONITOR_PID);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
- }
-
- sp<IBinder> getToken() { return mInputReceiver->getToken(); }
-
- void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
- }
-
- std::optional<int32_t> receiveEvent() {
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
- }
-
- void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
-
- void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- expectedDisplayId, expectedFlags);
- }
-
- void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
- expectedDisplayId, expectedFlags);
- }
-
- void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
- expectedDisplayId, expectedFlags);
- }
-
- void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeMotionEvent(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
- WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
- }
-
- void consumeMotionPointerDown(int32_t pointerIdx) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
- /*expectedFlags=*/0);
- }
-
- MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
-
- void assertNoEvents() { mInputReceiver->assertNoEvents(); }
-
-private:
- std::unique_ptr<FakeInputReceiver> mInputReceiver;
-};
-
using InputDispatcherMonitorTest = InputDispatcherTest;
/**
@@ -5054,7 +5496,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -5096,7 +5538,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5106,7 +5548,7 @@
}
TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -5140,7 +5582,7 @@
window->setWindowOffset(20, 40);
window->setWindowTransform(0, 1, -1, 0);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5153,7 +5595,7 @@
TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5654,6 +6096,53 @@
rightDropTouchesWindow->assertNoEvents();
}
+/**
+ * A single window is on screen first. Touch is injected into that window. Next, a second window
+ * appears. Since the first window is slippery, touch will move from the first window to the second.
+ */
+TEST_F(InputDispatcherTest, InjectedTouchSlips) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> originalWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Original", ADISPLAY_ID_DEFAULT);
+ originalWindow->setFrame(Rect(0, 0, 200, 200));
+ originalWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> appearingWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Appearing", ADISPLAY_ID_DEFAULT);
+ appearingWindow->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*originalWindow->getInfo()}, {}, 0, 0});
+
+ // Touch down on the original window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
+ .build()));
+ originalWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Now, a new window appears. This could be, for example, a notification shade that appears
+ // after user starts to drag down on the launcher window.
+ mDispatcher->onWindowInfosChanged(
+ {{*appearingWindow->getInfo(), *originalWindow->getInfo()}, {}, 0, 0});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(110).y(110))
+ .build()));
+ originalWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ appearingWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+ .build()));
+ appearingWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ originalWindow->assertNoEvents();
+ appearingWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithMotions) {
using Uid = gui::Uid;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -5996,9 +6485,9 @@
// Test per-display input monitors for motion event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
- FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6041,9 +6530,9 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
// Input monitor per display.
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
- FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
@@ -6079,9 +6568,9 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
- FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6467,7 +6956,7 @@
ASSERT_NE(nullptr, motionEvent)
<< name.c_str() << ": consumer should have returned non-NULL event.";
- assertMotionAction(expectedAction, motionEvent->getAction());
+ ASSERT_THAT(*motionEvent, WithMotionAction(expectedAction));
ASSERT_EQ(points.size(), motionEvent->getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
@@ -6652,15 +7141,15 @@
mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Start hover in window 1
- mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{50, 50}}));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER,
{getPointInWindow(mWindow1->getInfo(), PointF{50, 50})});
-
// Move hover to window 2.
- mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{150, 150}}));
-
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
+ .build());
consumeMotionEvent(mWindow1, ACTION_HOVER_EXIT, {{50, 50}});
consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER,
{getPointInWindow(mWindow2->getInfo(), PointF{150, 150})});
@@ -6848,6 +7337,10 @@
// We have a focused application, but no focused window
// Make sure that we don't notify policy twice about the same ANR.
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
+ const std::chrono::duration appTimeout = 400ms;
+ mApplication->setDispatchingTimeout(appTimeout);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
mWindow->setFocusable(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
mWindow->consumeFocusEvent(false);
@@ -6855,13 +7348,18 @@
// Once a focused event arrives, we get an ANR for this application
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// injection times out (instead of failing).
+ const std::chrono::duration eventInjectionTimeout = 100ms;
+ ASSERT_LT(eventInjectionTimeout, appTimeout);
const InputEventInjectionResult result =
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 100ms, /*allowKeyRepeat=*/false);
- ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
- const std::chrono::duration appTimeout =
- mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
- mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(appTimeout, mApplication);
+ InputEventInjectionSync::WAIT_FOR_RESULT, eventInjectionTimeout,
+ /*allowKeyRepeat=*/false);
+ ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result)
+ << "result=" << ftl::enum_string(result);
+ // We already waited for 'eventInjectionTimeout`, because the countdown started when the event
+ // was first injected. So now we have (appTimeout - eventInjectionTimeout) left to wait.
+ std::chrono::duration remainingWaitTime = appTimeout - eventInjectionTimeout;
+ mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(remainingWaitTime, mApplication);
std::this_thread::sleep_for(appTimeout);
// ANR should not be raised again. It is up to policy to do that if it desires.
@@ -6996,7 +7494,7 @@
TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -7523,7 +8021,7 @@
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(200ms);
+ focusedApplication->setDispatchingTimeout(300ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
@@ -7938,6 +8436,47 @@
mWindow->assertNoEvents();
}
+/**
+ * One window. Hover mouse in the window, and then start capture. Make sure that the relative
+ * mouse movements don't affect the previous mouse hovering state.
+ * When pointer capture is enabled, the incoming events are always ACTION_MOVE (there are no
+ * HOVER_MOVE events).
+ */
+TEST_F(InputDispatcherPointerCaptureTests, MouseHoverAndPointerCapture) {
+ // Mouse hover on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER)));
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE)));
+
+ // Start pointer capture
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ // Send some relative mouse movements and receive them in the window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(10).y(11))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithCoords(10, 11),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE)));
+
+ // Stop pointer capture
+ requestAndVerifyPointerCapture(mWindow, false);
+
+ // Continue hovering on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(105).y(115))
+ .build());
+ mWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE)));
+
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
@@ -8812,6 +9351,76 @@
mSecondWindow->assertNoEvents();
}
+/**
+ * Start drag and drop with a pointer whose id is not 0, cancel the current touch, and ensure drag
+ * and drop is also canceled. Then inject a simple gesture, and ensure dispatcher does not crash.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) {
+ // Down on second window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionDown());
+
+ // Down on first window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionMove());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionPointerDown(1));
+
+ // Start drag on first window
+ ASSERT_TRUE(startDrag(/*sendDown=*/false, AINPUT_SOURCE_TOUCHSCREEN));
+
+ // Trigger cancel
+ mDispatcher->cancelCurrentTouch();
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionCancel());
+ ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionCancel());
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // The D&D finished with nullptr
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
+
+ // Remove drag window
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+
+ // Inject a simple gesture, ensure dispatcher not crashed
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ PointF{50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+
+ const MotionEvent moveEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, moveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionMove());
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp());
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
@@ -9644,6 +10253,73 @@
spy->assertNoEvents();
}
+/**
+ * A window on the left and a window on the right. Also, a spy window that's above all of the
+ * windows, and spanning both left and right windows.
+ * Send simultaneous motion streams from two different devices, one to the left window, and another
+ * to the right window.
+ * Pilfer from spy window.
+ * Check that the pilfering only affects the pointers that are actually being received by the spy.
+ */
+TEST_F(InputDispatcherPilferPointersTest, MultiDevicePilfer) {
+ sp<FakeWindowHandle> spy = createSpy();
+ spy->setFrame(Rect(0, 0, 200, 200));
+ sp<FakeWindowHandle> leftWindow = createForeground();
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> rightWindow = createForeground();
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+ constexpr int32_t stylusDeviceId = 1;
+ constexpr int32_t touchDeviceId = 2;
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ // Stylus down on left window and spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Finger down on right window and spy - but spy already has stylus
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Act: pilfer from spy. Spy is currently receiving touch events.
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+
+ // Continue movements from both stylus and touch. Touch will be delivered to spy, but not stylus
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(52))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(52))
+ .build());
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spy->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
class InputDispatcherStylusInterceptorTest : public InputDispatcherTest {
public:
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 6032e30..64ae9e8 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -31,8 +31,8 @@
#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
#include <SwitchInputMapper.h>
+#include <TestEventMatchers.h>
#include <TestInputListener.h>
-#include <TestInputListenerMatchers.h>
#include <TouchInputMapper.h>
#include <UinputDevice.h>
#include <VibratorInputMapper.h>
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index fa149db..6606de8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -15,11 +15,14 @@
*/
#include "../dispatcher/LatencyTracker.h"
+#include "../InputDeviceMetricsSource.h"
#include <android-base/properties.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
+#include <gui/constants.h>
#include <inttypes.h>
+#include <linux/input.h>
#include <log/log.h>
#define TAG "LatencyTracker_test"
@@ -30,6 +33,29 @@
namespace android::inputdispatcher {
+namespace {
+
+constexpr DeviceId DEVICE_ID = 100;
+
+static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId,
+ DeviceId deviceId) {
+ InputDeviceIdentifier identifier;
+ identifier.vendor = vendorId;
+ identifier.product = productId;
+ auto info = InputDeviceInfo();
+ info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device",
+ /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+ return info;
+}
+
+void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
+ InputDeviceInfo deviceInfo = generateTestDeviceInfo(
+ /*vendorId=*/0, /*productId=*/0, DEVICE_ID);
+ tracker.setInputDevices({deviceInfo});
+}
+
+} // namespace
+
const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
HwTimeoutMultiplier());
@@ -38,7 +64,10 @@
InputEventTimeline t(
/*isDown=*/true,
/*eventTime=*/2,
- /*readTime=*/3);
+ /*readTime=*/3,
+ /*vendorId=*/0,
+ /*productId=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8);
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
@@ -60,6 +89,7 @@
connection2 = sp<BBinder>::make();
mTracker = std::make_unique<LatencyTracker>(this);
+ setDefaultInputDeviceInfo(*mTracker);
}
void TearDown() override {}
@@ -88,7 +118,8 @@
const nsecs_t triggerEventTime =
lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/true, triggerEventTime,
- /*readTime=*/3);
+ /*readTime=*/3, DEVICE_ID,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
}
void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
@@ -138,9 +169,11 @@
*/
TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/false, /*eventTime=*/2,
- /*readTime=*/3);
+ /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
triggerEventReporting(/*eventTime=*/2);
- assertReceivedTimeline(InputEventTimeline{false, 2, 3});
+ assertReceivedTimeline(InputEventTimeline{/*isDown=*/false, /*eventTime=*/2,
+ /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN}});
}
/**
@@ -171,7 +204,8 @@
const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
- mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
+ DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
@@ -191,8 +225,10 @@
// In the following 2 calls to trackListener, the inputEventId's are the same, but event times
// are different.
- mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime);
- mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime);
+ mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN});
+ mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime, DEVICE_ID,
+ {InputDeviceUsageSource::UNKNOWN});
triggerEventReporting(/*eventTime=*/2);
// Since we sent duplicate input events, the tracker should just delete all of them, because it
@@ -205,7 +241,10 @@
InputEventTimeline timeline1(
/*isDown*/ true,
/*eventTime*/ 2,
- /*readTime*/ 3);
+ /*readTime*/ 3,
+ /*vendorId=*/0,
+ /*productId=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
timeline1.connectionTimelines.emplace(connection1,
ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
/*finishTime*/ 8));
@@ -219,7 +258,10 @@
InputEventTimeline timeline2(
/*isDown=*/false,
/*eventTime=*/20,
- /*readTime=*/30);
+ /*readTime=*/30,
+ /*vendorId=*/0,
+ /*productId=*/0,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
timeline2.connectionTimelines.emplace(connection2,
ConnectionTimeline(/*deliveryTime=*/60,
/*consumeTime=*/70,
@@ -232,10 +274,10 @@
// Start processing first event
mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime,
- timeline1.readTime);
+ timeline1.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
// Start processing second event
mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime,
- timeline2.readTime);
+ timeline2.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
@@ -261,9 +303,11 @@
for (size_t i = 1; i <= 100; i++) {
mTracker->trackListener(/*inputEventId=*/i, timeline.isDown, timeline.eventTime,
- timeline.readTime);
- expectedTimelines.push_back(
- InputEventTimeline{timeline.isDown, timeline.eventTime, timeline.readTime});
+ timeline.readTime, /*deviceId=*/DEVICE_ID,
+ /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+ expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime,
+ timeline.readTime, timeline.vendorId,
+ timeline.productId, timeline.sources});
}
// Now, complete the first event that was sent.
mTracker->trackFinishedEvent(/*inputEventId=*/1, token, expectedCT.deliveryTime,
@@ -289,10 +333,38 @@
expectedCT.consumeTime, expectedCT.finishTime);
mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
- mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+ mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
+ DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
triggerEventReporting(expected.eventTime);
- assertReceivedTimeline(
- InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
+ assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime,
+ expected.readTime, expected.vendorId,
+ expected.productId, expected.sources});
+}
+
+/**
+ * Check that LatencyTracker has the received timeline that contains the correctly
+ * resolved product ID, vendor ID and source for a particular device ID from
+ * among a list of devices.
+ */
+TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) {
+ constexpr int32_t inputEventId = 1;
+ InputEventTimeline timeline(
+ /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3,
+ /*vendorId=*/50, /*productId=*/60,
+ /*sources=*/
+ {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT});
+ InputDeviceInfo deviceInfo1 = generateTestDeviceInfo(
+ /*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1);
+ InputDeviceInfo deviceInfo2 = generateTestDeviceInfo(
+ /*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID);
+
+ mTracker->setInputDevices({deviceInfo1, deviceInfo2});
+ mTracker->trackListener(inputEventId, timeline.isDown, timeline.eventTime, timeline.readTime,
+ DEVICE_ID,
+ {InputDeviceUsageSource::TOUCHSCREEN,
+ InputDeviceUsageSource::STYLUS_DIRECT});
+ triggerEventReporting(timeline.eventTime);
+ assertReceivedTimeline(timeline);
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
new file mode 100644
index 0000000..ee6ff53
--- /dev/null
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cmath>
+#include <compare>
+
+#include <android-base/stringprintf.h>
+#include <android/input.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/PrintTools.h>
+
+#include "NotifyArgs.h"
+#include "TestConstants.h"
+
+namespace android {
+
+struct PointF {
+ float x;
+ float y;
+ auto operator<=>(const PointF&) const = default;
+};
+
+inline std::string pointFToString(const PointF& p) {
+ return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
+}
+
+/// Source
+class WithSourceMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithSourceMatcher(uint32_t source) : mSource(source) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mSource == args.source;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mSource == args.source;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mSource == event.getSource();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with source " << inputEventSourceToString(mSource);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong source"; }
+
+private:
+ const uint32_t mSource;
+};
+
+inline WithSourceMatcher WithSource(uint32_t source) {
+ return WithSourceMatcher(source);
+}
+
+/// Key action
+class WithKeyActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mAction == args.action;
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mAction == event.getAction();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with key action " << KeyEvent::actionToString(mAction);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+inline WithKeyActionMatcher WithKeyAction(int32_t action) {
+ return WithKeyActionMatcher(action);
+}
+
+/// Motion action
+class WithMotionActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ bool matches = mAction == args.action;
+ if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ bool matches = mAction == event.getAction();
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with motion action " << MotionEvent::actionToString(mAction);
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ *os << " and FLAG_CANCELED";
+ }
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+inline WithMotionActionMatcher WithMotionAction(int32_t action) {
+ return WithMotionActionMatcher(action);
+}
+
+/// Display Id
+class WithDisplayIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDisplayId == args.displayId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDisplayId == event.getDisplayId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
+
+private:
+ const int32_t mDisplayId;
+};
+
+inline WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
+ return WithDisplayIdMatcher(displayId);
+}
+
+/// Device Id
+class WithDeviceIdMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
+ return mDeviceId == args.deviceId;
+ }
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDeviceId == event.getDeviceId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+ const int32_t mDeviceId;
+};
+
+inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+ return WithDeviceIdMatcher(deviceId);
+}
+
+/// Flags
+class WithFlagsMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithFlagsMatcher(int32_t flags) : mFlags(flags) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mFlags == args.flags;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mFlags == args.flags;
+ }
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ return mFlags == event.getFlags();
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mFlags == event.getFlags();
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with flags " << base::StringPrintf("0x%x", mFlags);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong flags"; }
+
+private:
+ const int32_t mFlags;
+};
+
+inline WithFlagsMatcher WithFlags(int32_t flags) {
+ return WithFlagsMatcher(flags);
+}
+
+/// DownTime
+class WithDownTimeMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithDownTimeMatcher(nsecs_t downTime) : mDownTime(downTime) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ return mDownTime == args.downTime;
+ }
+
+ bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+ return mDownTime == args.downTime;
+ }
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ return mDownTime == event.getDownTime();
+ }
+
+ bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+ return mDownTime == event.getDownTime();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with down time " << mDownTime; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong down time"; }
+
+private:
+ const nsecs_t mDownTime;
+};
+
+inline WithDownTimeMatcher WithDownTime(nsecs_t downTime) {
+ return WithDownTimeMatcher(downTime);
+}
+
+/// Coordinate matcher
+class WithCoordsMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithCoordsMatcher(size_t pointerIndex, float x, float y)
+ : mPointerIndex(pointerIndex), mX(x), mY(y) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+ if (mPointerIndex >= event.getPointerCount()) {
+ *os << "Pointer index " << mPointerIndex << " is out of bounds";
+ return false;
+ }
+
+ bool matches = mX == event.getX(mPointerIndex) && mY == event.getY(mPointerIndex);
+ if (!matches) {
+ *os << "expected coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex
+ << ", but got (" << event.getX(mPointerIndex) << ", " << event.getY(mPointerIndex)
+ << ")";
+ }
+ return matches;
+ }
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ if (mPointerIndex >= event.pointerCoords.size()) {
+ *os << "Pointer index " << mPointerIndex << " is out of bounds";
+ return false;
+ }
+
+ bool matches = mX == event.pointerCoords[mPointerIndex].getX() &&
+ mY == event.pointerCoords[mPointerIndex].getY();
+ if (!matches) {
+ *os << "expected coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex
+ << ", but got (" << event.pointerCoords[mPointerIndex].getX() << ", "
+ << event.pointerCoords[mPointerIndex].getY() << ")";
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong coords"; }
+
+private:
+ const size_t mPointerIndex;
+ const float mX;
+ const float mY;
+};
+
+inline WithCoordsMatcher WithCoords(float x, float y) {
+ return WithCoordsMatcher(0, x, y);
+}
+
+inline WithCoordsMatcher WithPointerCoords(size_t pointerIndex, float x, float y) {
+ return WithCoordsMatcher(pointerIndex, x, y);
+}
+
+/// Raw coordinate matcher
+class WithRawCoordsMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithRawCoordsMatcher(size_t pointerIndex, float rawX, float rawY)
+ : mPointerIndex(pointerIndex), mRawX(rawX), mRawY(rawY) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+ if (mPointerIndex >= event.getPointerCount()) {
+ *os << "Pointer index " << mPointerIndex << " is out of bounds";
+ return false;
+ }
+
+ bool matches =
+ mRawX == event.getRawX(mPointerIndex) && mRawY == event.getRawY(mPointerIndex);
+ if (!matches) {
+ *os << "expected raw coords (" << mRawX << ", " << mRawY << ") at pointer index "
+ << mPointerIndex << ", but got (" << event.getRawX(mPointerIndex) << ", "
+ << event.getRawY(mPointerIndex) << ")";
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with raw coords (" << mRawX << ", " << mRawY << ") at pointer index "
+ << mPointerIndex;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong raw coords"; }
+
+private:
+ const size_t mPointerIndex;
+ const float mRawX;
+ const float mRawY;
+};
+
+inline WithRawCoordsMatcher WithRawCoords(float rawX, float rawY) {
+ return WithRawCoordsMatcher(0, rawX, rawY);
+}
+
+inline WithRawCoordsMatcher WithPointerRawCoords(size_t pointerIndex, float rawX, float rawY) {
+ return WithRawCoordsMatcher(pointerIndex, rawX, rawY);
+}
+
+/// Pointer count
+class WithPointerCountMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithPointerCountMatcher(size_t pointerCount) : mPointerCount(pointerCount) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+ if (event.getPointerCount() != mPointerCount) {
+ *os << "expected pointer count " << mPointerCount << ", but got "
+ << event.getPointerCount();
+ return false;
+ }
+ return true;
+ }
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ if (event.pointerCoords.size() != mPointerCount) {
+ *os << "expected pointer count " << mPointerCount << ", but got "
+ << event.pointerCoords.size();
+ return false;
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with pointer count " << mPointerCount; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer count"; }
+
+private:
+ const size_t mPointerCount;
+};
+
+inline WithPointerCountMatcher WithPointerCount(size_t pointerCount) {
+ return WithPointerCountMatcher(pointerCount);
+}
+
+/// Pointers matcher
+class WithPointersMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithPointersMatcher(std::map<int32_t, PointF> pointers) : mPointers(pointers) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+ std::map<int32_t, PointF> actualPointers;
+ for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) {
+ const int32_t pointerId = event.getPointerId(pointerIndex);
+ actualPointers[pointerId] = {event.getX(pointerIndex), event.getY(pointerIndex)};
+ }
+
+ if (mPointers != actualPointers) {
+ *os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
+ << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
+ return false;
+ }
+ return true;
+ }
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ std::map<int32_t, PointF> actualPointers;
+ for (size_t pointerIndex = 0; pointerIndex < event.pointerCoords.size(); pointerIndex++) {
+ const int32_t pointerId = event.pointerProperties[pointerIndex].id;
+ actualPointers[pointerId] = {event.pointerCoords[pointerIndex].getX(),
+ event.pointerCoords[pointerIndex].getY()};
+ }
+
+ if (mPointers != actualPointers) {
+ *os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
+ << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
+ return false;
+ }
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with pointers " << dumpMap(mPointers, constToString, pointFToString);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointers"; }
+
+private:
+ const std::map<int32_t, PointF> mPointers;
+};
+
+inline WithPointersMatcher WithPointers(
+ const std::map<int32_t /*id*/, PointF /*coords*/>& pointers) {
+ return WithPointersMatcher(pointers);
+}
+
+MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
+ *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
+ return arg.keyCode == keyCode;
+}
+
+MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
+ return arg.getRepeatCount() == repeatCount;
+}
+
+MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
+ const auto argPointerId = arg.pointerProperties[index].id;
+ *result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
+ return argPointerId == id;
+}
+
+MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position") {
+ const auto argX = arg.xCursorPosition;
+ const auto argY = arg.yCursorPosition;
+ *result_listener << "expected cursor position (" << x << ", " << y << "), but got (" << argX
+ << ", " << argY << ")";
+ return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
+}
+
+MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
+ const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
+ << ", " << argY << ")";
+ return argX == x && argY == y;
+}
+
+MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
+ "InputEvent with specified touchpad gesture offset") {
+ const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET);
+ const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET);
+ const double xDiff = fabs(argGestureX - dx);
+ const double yDiff = fabs(argGestureY - dy);
+ *result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon
+ << ", but got (" << argGestureX << ", " << argGestureY << ")";
+ return xDiff <= epsilon && yDiff <= epsilon;
+}
+
+MATCHER_P3(WithGestureScrollDistance, x, y, epsilon,
+ "InputEvent with specified touchpad gesture scroll distance") {
+ const auto argXDistance =
+ arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE);
+ const auto argYDistance =
+ arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE);
+ const double xDiff = fabs(argXDistance - x);
+ const double yDiff = fabs(argYDistance - y);
+ *result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon
+ << ", but got (" << argXDistance << ", " << argYDistance << ")";
+ return xDiff <= epsilon && yDiff <= epsilon;
+}
+
+MATCHER_P2(WithGesturePinchScaleFactor, factor, epsilon,
+ "InputEvent with specified touchpad pinch gesture scale factor") {
+ const auto argScaleFactor =
+ arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR);
+ *result_listener << "expected gesture scale factor " << factor << " within " << epsilon
+ << " but got " << argScaleFactor;
+ return fabs(argScaleFactor - factor) <= epsilon;
+}
+
+MATCHER_P(WithGestureSwipeFingerCount, count,
+ "InputEvent with specified touchpad swipe finger count") {
+ const auto argFingerCount =
+ arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT);
+ *result_listener << "expected gesture swipe finger count " << count << " but got "
+ << argFingerCount;
+ return fabs(argFingerCount - count) <= EPSILON;
+}
+
+MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
+ const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
+ return argPressure == pressure;
+}
+
+MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
+ const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+ const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+ *result_listener << "expected touch dimensions " << maj << " major x " << min
+ << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+ return argMajor == maj && argMinor == min;
+}
+
+MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
+ const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+ const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
+ *result_listener << "expected tool dimensions " << maj << " major x " << min
+ << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+ return argMajor == maj && argMinor == min;
+}
+
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+ const auto argToolType = arg.pointerProperties[0].toolType;
+ *result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
+ << ftl::enum_string(argToolType);
+ return argToolType == toolType;
+}
+
+MATCHER_P2(WithPointerToolType, pointer, toolType,
+ "InputEvent with specified tool type for pointer") {
+ const auto argToolType = arg.pointerProperties[pointer].toolType;
+ *result_listener << "expected pointer " << pointer << " to have tool type "
+ << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
+ return argToolType == toolType;
+}
+
+MATCHER_P(WithMotionClassification, classification,
+ "InputEvent with specified MotionClassification") {
+ *result_listener << "expected classification " << motionClassificationToString(classification)
+ << ", but got " << motionClassificationToString(arg.classification);
+ return arg.classification == classification;
+}
+
+MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
+ *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
+ return arg.buttonState == buttons;
+}
+
+MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
+ *result_listener << "expected action button " << actionButton << ", but got "
+ << arg.actionButton;
+ return arg.actionButton == actionButton;
+}
+
+MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
+ *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
+ return arg.eventTime == eventTime;
+}
+
+MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+ *result_listener << "expected down time " << downTime << ", but got " << arg.downTime;
+ 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/inputflinger/tests/TestInputListenerMatchers.cpp b/services/inputflinger/tests/TestInputListenerMatchers.cpp
deleted file mode 100644
index 1464e60..0000000
--- a/services/inputflinger/tests/TestInputListenerMatchers.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TestInputListenerMatchers.h"
-
-namespace android {
-
-WithKeyActionMatcher WithKeyAction(int32_t action) {
- return WithKeyActionMatcher(action);
-}
-
-WithMotionActionMatcher WithMotionAction(int32_t action) {
- return WithMotionActionMatcher(action);
-}
-
-WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
- return WithDisplayIdMatcher(displayId);
-}
-
-WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
- return WithDeviceIdMatcher(deviceId);
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
deleted file mode 100644
index 31ad569..0000000
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cmath>
-
-#include <android/input.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <input/Input.h>
-
-#include "NotifyArgs.h"
-#include "TestConstants.h"
-
-namespace android {
-
-MATCHER_P(WithSource, source, "InputEvent with specified source") {
- *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
- << inputEventSourceToString(arg.source);
- return arg.source == source;
-}
-
-/// Key action
-class WithKeyActionMatcher {
-public:
- using is_gtest_matcher = void;
- explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
-
- bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
- return mAction == args.action;
- }
-
- bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
- return mAction == event.getAction();
- }
-
- void DescribeTo(std::ostream* os) const {
- *os << "with key action " << KeyEvent::actionToString(mAction);
- }
-
- void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
-
-private:
- const int32_t mAction;
-};
-
-WithKeyActionMatcher WithKeyAction(int32_t action);
-
-/// Motion action
-class WithMotionActionMatcher {
-public:
- using is_gtest_matcher = void;
- explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
-
- bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
- bool matches = mAction == args.action;
- if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
- matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
- }
-
- bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
- bool matches = mAction == event.getAction();
- if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
- matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
- }
- return matches;
- }
-
- void DescribeTo(std::ostream* os) const {
- *os << "with motion action " << MotionEvent::actionToString(mAction);
- if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
- *os << " and FLAG_CANCELED";
- }
- }
-
- void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
-
-private:
- const int32_t mAction;
-};
-
-WithMotionActionMatcher WithMotionAction(int32_t action);
-
-/// Display Id
-class WithDisplayIdMatcher {
-public:
- using is_gtest_matcher = void;
- explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
-
- bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
- return mDisplayId == args.displayId;
- }
-
- bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
- return mDisplayId == args.displayId;
- }
-
- bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
- return mDisplayId == event.getDisplayId();
- }
-
- void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
-
- void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
-
-private:
- const int32_t mDisplayId;
-};
-
-WithDisplayIdMatcher WithDisplayId(int32_t displayId);
-
-/// Device Id
-class WithDeviceIdMatcher {
-public:
- using is_gtest_matcher = void;
- explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
-
- bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
- return mDeviceId == args.deviceId;
- }
-
- bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
- return mDeviceId == args.deviceId;
- }
-
- bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
- return mDeviceId == args.deviceId;
- }
-
- bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
- return mDeviceId == event.getDeviceId();
- }
-
- void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
-
- void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
-
-private:
- const int32_t mDeviceId;
-};
-
-WithDeviceIdMatcher WithDeviceId(int32_t deviceId);
-
-MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
- *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
- return arg.keyCode == keyCode;
-}
-
-MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
- return arg.getRepeatCount() == repeatCount;
-}
-
-MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
- *result_listener << "expected " << count << " pointer(s), but got " << arg.getPointerCount();
- return arg.getPointerCount() == count;
-}
-
-MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
- const auto argPointerId = arg.pointerProperties[index].id;
- *result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
- return argPointerId == id;
-}
-
-MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
- const auto argX = arg.pointerCoords[0].getX();
- const auto argY = arg.pointerCoords[0].getY();
- *result_listener << "expected coords (" << x << ", " << y << "), but got (" << argX << ", "
- << argY << ")";
- return argX == x && argY == y;
-}
-
-MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position") {
- const auto argX = arg.xCursorPosition;
- const auto argY = arg.yCursorPosition;
- *result_listener << "expected cursor position (" << x << ", " << y << "), but got (" << argX
- << ", " << argY << ")";
- return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
-}
-
-MATCHER_P3(WithPointerCoords, pointer, x, y, "InputEvent with specified coords for pointer") {
- const auto argX = arg.pointerCoords[pointer].getX();
- const auto argY = arg.pointerCoords[pointer].getY();
- *result_listener << "expected pointer " << pointer << " to have coords (" << x << ", " << y
- << "), but got (" << argX << ", " << argY << ")";
- return argX == x && argY == y;
-}
-
-MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
- const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
- << ", " << argY << ")";
- return argX == x && argY == y;
-}
-
-MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
- "InputEvent with specified touchpad gesture offset") {
- const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET);
- const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET);
- const double xDiff = fabs(argGestureX - dx);
- const double yDiff = fabs(argGestureY - dy);
- *result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon
- << ", but got (" << argGestureX << ", " << argGestureY << ")";
- return xDiff <= epsilon && yDiff <= epsilon;
-}
-
-MATCHER_P3(WithGestureScrollDistance, x, y, epsilon,
- "InputEvent with specified touchpad gesture scroll distance") {
- const auto argXDistance =
- arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE);
- const auto argYDistance =
- arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE);
- const double xDiff = fabs(argXDistance - x);
- const double yDiff = fabs(argYDistance - y);
- *result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon
- << ", but got (" << argXDistance << ", " << argYDistance << ")";
- return xDiff <= epsilon && yDiff <= epsilon;
-}
-
-MATCHER_P2(WithGesturePinchScaleFactor, factor, epsilon,
- "InputEvent with specified touchpad pinch gesture scale factor") {
- const auto argScaleFactor =
- arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR);
- *result_listener << "expected gesture scale factor " << factor << " within " << epsilon
- << " but got " << argScaleFactor;
- return fabs(argScaleFactor - factor) <= epsilon;
-}
-
-MATCHER_P(WithGestureSwipeFingerCount, count,
- "InputEvent with specified touchpad swipe finger count") {
- const auto argFingerCount =
- arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT);
- *result_listener << "expected gesture swipe finger count " << count << " but got "
- << argFingerCount;
- return fabs(argFingerCount - count) <= EPSILON;
-}
-
-MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
- const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
- *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
- return argPressure == pressure;
-}
-
-MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
- const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
- const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
- *result_listener << "expected touch dimensions " << maj << " major x " << min
- << " minor, but got " << argMajor << " major x " << argMinor << " minor";
- return argMajor == maj && argMinor == min;
-}
-
-MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
- const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
- const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
- *result_listener << "expected tool dimensions " << maj << " major x " << min
- << " minor, but got " << argMajor << " major x " << argMinor << " minor";
- return argMajor == maj && argMinor == min;
-}
-
-MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
- const auto argToolType = arg.pointerProperties[0].toolType;
- *result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
- << ftl::enum_string(argToolType);
- return argToolType == toolType;
-}
-
-MATCHER_P2(WithPointerToolType, pointer, toolType,
- "InputEvent with specified tool type for pointer") {
- const auto argToolType = arg.pointerProperties[pointer].toolType;
- *result_listener << "expected pointer " << pointer << " to have tool type "
- << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
- return argToolType == toolType;
-}
-
-MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
- *result_listener << "expected flags " << flags << ", but got " << arg.flags;
- return arg.flags == static_cast<int32_t>(flags);
-}
-
-MATCHER_P(WithMotionClassification, classification,
- "InputEvent with specified MotionClassification") {
- *result_listener << "expected classification " << motionClassificationToString(classification)
- << ", but got " << motionClassificationToString(arg.classification);
- return arg.classification == classification;
-}
-
-MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
- *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
- return arg.buttonState == buttons;
-}
-
-MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
- *result_listener << "expected action button " << actionButton << ", but got "
- << arg.actionButton;
- return arg.actionButton == actionButton;
-}
-
-MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
- *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
- return arg.eventTime == eventTime;
-}
-
-MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
- *result_listener << "expected down time " << downTime << ", but got " << arg.downTime;
- 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/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 02abf9f..6203a1d 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -23,7 +23,7 @@
#include "FakePointerController.h"
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
-#include "TestInputListenerMatchers.h"
+#include "TestEventMatchers.h"
#define TAG "TouchpadInputMapper_test"
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 7cfcf71..78f7291 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -23,8 +23,8 @@
#include <thread>
#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
+#include "TestEventMatchers.h"
#include "TestInputListener.h"
-#include "TestInputListenerMatchers.h"
using ::testing::AllOf;
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 72780fb..6daeaaf 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -15,6 +15,9 @@
*/
#include <fuzzer/FuzzedDataProvider.h>
+#include <linux/input.h>
+
+#include "../../InputDeviceMetricsSource.h"
#include "dispatcher/LatencyTracker.h"
namespace android {
@@ -65,7 +68,11 @@
int32_t isDown = fdp.ConsumeBool();
nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
- tracker.trackListener(inputEventId, isDown, eventTime, readTime);
+ const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
+ std::set<InputDeviceUsageSource> sources = {
+ fdp.ConsumeEnum<InputDeviceUsageSource>()};
+ tracker.trackListener(inputEventId, isDown, eventTime, readTime, deviceId,
+ sources);
},
[&]() -> void {
int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 71c75f9..9d0f285 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -31,9 +31,6 @@
"-Wconversion",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
- static_libs: [
- "libsurfaceflingerflags",
- ],
}
cc_defaults {
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index bdbc79b..6b4215e 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -22,6 +22,7 @@
#include <private/android_filesystem_config.h>
#include <gui/AidlStatusUtil.h>
+#include <gui/SchedulingPolicy.h>
#include "Client.h"
#include "FrontEnd/LayerCreationArgs.h"
@@ -117,5 +118,9 @@
return binderStatusFromStatusT(status);
}
+binder::Status Client::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ return gui::getSchedulingPolicy(outPolicy);
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index af410ea..77916e0 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -55,6 +55,8 @@
binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override;
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
+
// constant
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 2dc7332..370e4b6 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -63,6 +63,7 @@
cc_library {
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
+ static_libs: ["libsurfaceflingerflags"],
srcs: [
"src/planner/CachedSet.cpp",
"src/planner/Flattener.cpp",
@@ -107,6 +108,7 @@
"libgtest",
"libgmock",
"libcompositionengine",
+ "libsurfaceflingerflags_test",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -141,6 +143,7 @@
"librenderengine_mocks",
"libgmock",
"libgtest",
+ "libsurfaceflingerflags_test",
],
// For some reason, libvulkan isn't picked up from librenderengine
// Probably ASAN related?
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 35ca3a5..11759b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -26,6 +26,7 @@
#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/ShadowSettings.h>
#include <ui/Transform.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -132,8 +133,7 @@
// The bounds of the layer in layer local coordinates
FloatRect geomLayerBounds;
- // length of the shadow in screen space
- float shadowRadius{0.f};
+ ShadowSettings shadowSettings;
// List of regions that require blur
std::vector<BlurRegion> blurRegions;
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 97725ea..f339d41 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -392,6 +392,10 @@
dumpVal(out, "dv", hasDolbyVisionSupport());
dumpVal(out, "metadata", getSupportedPerFrameMetadata());
+ out.append("\n Hdr Luminance Info:");
+ dumpVal(out, "desiredMinLuminance", mHdrCapabilities.getDesiredMinLuminance());
+ dumpVal(out, "desiredMaxLuminance", mHdrCapabilities.getDesiredMaxLuminance());
+ dumpVal(out, "desiredMaxAverageLuminance", mHdrCapabilities.getDesiredMaxAverageLuminance());
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 426cc57..2d10866 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -68,7 +68,7 @@
dumpVal(out, "geomLayerBounds", geomLayerBounds);
out.append(" ");
- dumpVal(out, "shadowRadius", shadowRadius);
+ dumpVal(out, "shadowLength", shadowSettings.length);
out.append("\n ");
dumpVal(out, "blend", toString(blendMode), blendMode);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 78c23da..fa3733b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -588,10 +588,10 @@
const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
visibleRegion.set(visibleRect);
- if (layerFEState->shadowRadius > 0.0f) {
+ if (layerFEState->shadowSettings.length > 0.0f) {
// if the layer casts a shadow, offset the layers visible region and
// calculate the shadow region.
- const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
+ const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowSettings.length) * -1.0f);
Rect visibleRectWithShadows(visibleRect);
visibleRectWithShadows.inset(inset, inset, inset, inset);
visibleRegion.set(visibleRectWithShadows);
@@ -967,7 +967,7 @@
case ui::Dataspace::BT2020_ITU_HLG:
bestDataSpace = ui::Dataspace::DISPLAY_P3;
// When there's mixed PQ content and HLG content, we set the HDR
- // data space to be BT2020_PQ and convert HLG to PQ.
+ // data space to be BT2020_HLG and convert PQ to HLG.
if (*outHdrDataSpace == ui::Dataspace::UNKNOWN) {
*outHdrDataSpace = ui::Dataspace::BT2020_HLG;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index fe56969..7fe3369 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -222,8 +222,8 @@
// Some HWCs may clip client composited input to its displayFrame. Make sure
// that this does not cut off the shadow.
- if (layerState.forceClientComposition && layerState.shadowRadius > 0.0f) {
- const auto outset = layerState.shadowRadius;
+ if (layerState.forceClientComposition && layerState.shadowSettings.length > 0.0f) {
+ const auto outset = layerState.shadowSettings.length;
geomLayerBounds.left -= outset;
geomLayerBounds.top -= outset;
geomLayerBounds.right += outset;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 630906a..1c54469 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -334,7 +334,7 @@
TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame) {
const int kShadowRadius = 5;
- mLayerFEState.shadowRadius = kShadowRadius;
+ mLayerFEState.shadowSettings.length = kShadowRadius;
mLayerFEState.forceClientComposition = true;
mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
@@ -345,7 +345,7 @@
TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame_onlyIfForcingClientComposition) {
const int kShadowRadius = 5;
- mLayerFEState.shadowRadius = kShadowRadius;
+ mLayerFEState.shadowSettings.length = kShadowRadius;
mLayerFEState.forceClientComposition = false;
mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ee6998a..cdcb68d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1830,7 +1830,7 @@
ui::Transform translate;
translate.set(50, 50);
mLayer.layerFEState.geomLayerTransform = translate;
- mLayer.layerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.shadowSettings.length = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// half of the layer including the casting shadow is covered and opaque
@@ -1872,7 +1872,7 @@
ui::Transform translate;
translate.set(50, 50);
mLayer.layerFEState.geomLayerTransform = translate;
- mLayer.layerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.shadowSettings.length = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
@@ -1897,7 +1897,7 @@
ui::Transform translate;
translate.set(50, 50);
mLayer.layerFEState.geomLayerTransform = translate;
- mLayer.layerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.shadowSettings.length = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// Casting layer and its shadows are covered by an opaque region
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 1faf6a1..5b6591a 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -428,10 +428,12 @@
return;
}
- mHdrSdrRatioOverlay = std::make_unique<HdrSdrRatioOverlay>();
- mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
- mHdrSdrRatioOverlay->setViewport(getSize());
- updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+ mHdrSdrRatioOverlay = HdrSdrRatioOverlay::create();
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
+ mHdrSdrRatioOverlay->setViewport(getSize());
+ updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+ }
}
void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) {
@@ -468,11 +470,13 @@
// TODO(b/296636258) Update to use the render rate range in VRR mode.
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
- mRefreshRateOverlay->setLayerStack(getLayerStack());
- mRefreshRateOverlay->setViewport(getSize());
- updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
- setByHwc);
+ mRefreshRateOverlay = RefreshRateOverlay::create(fpsRange, features);
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->setLayerStack(getLayerStack());
+ mRefreshRateOverlay->setViewport(getSize());
+ updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
+ setByHwc);
+ }
}
void DisplayDevice::updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 899d2de..7a85da0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -382,7 +382,6 @@
sidebandStream = requested.sidebandStream;
}
if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
- shadowRadius = requested.shadowRadius;
shadowSettings.length = requested.shadowRadius;
}
if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
@@ -398,6 +397,15 @@
geomCrop = requested.crop;
}
+ if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(requested.defaultFrameRateCompatibility);
+ if (defaultFrameRateCompatibility != compatibility) {
+ clientChanges |= layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ }
+ defaultFrameRateCompatibility = compatibility;
+ }
+
if (forceUpdate ||
requested.what &
(layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index a5e9368..80a51ea 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -69,7 +69,6 @@
RoundedCornerState roundedCorner;
FloatRect transformedBounds;
Rect transformedBoundsWithoutTransparentRegion;
- renderengine::ShadowSettings shadowSettings;
bool premultipliedAlpha;
ui::Transform parentTransform;
Rect bufferSize;
@@ -86,6 +85,8 @@
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
+ scheduler::FrameRateCompatibility defaultFrameRateCompatibility =
+ scheduler::FrameRateCompatibility::Default;
ui::Transform::RotationFlags fixedTransformHint;
std::optional<ui::Transform::RotationFlags> transformHint;
bool handleSkipScreenshotFlag = false;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 4c9fb06..03c0993 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -353,7 +353,7 @@
snapshot.isSecure = false;
snapshot.color.a = 1.0_hf;
snapshot.colorTransformIsIdentity = true;
- snapshot.shadowRadius = 0.f;
+ snapshot.shadowSettings.length = 0.f;
snapshot.layerMetadata.mMap.clear();
snapshot.relativeLayerMetadata.mMap.clear();
snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
@@ -666,7 +666,7 @@
return;
}
- using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ using FrameRateCompatibility = scheduler::FrameRateCompatibility;
if (snapshot.frameRate.isValid()) {
// we already have a valid framerate.
return;
@@ -990,9 +990,12 @@
}
void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, const RequestedLayerState&,
- const renderengine::ShadowSettings& globalShadowSettings) {
- if (snapshot.shadowRadius > 0.f) {
- snapshot.shadowSettings = globalShadowSettings;
+ const ShadowSettings& globalShadowSettings) {
+ if (snapshot.shadowSettings.length > 0.f) {
+ snapshot.shadowSettings.ambientColor = globalShadowSettings.ambientColor;
+ snapshot.shadowSettings.spotColor = globalShadowSettings.spotColor;
+ snapshot.shadowSettings.lightPos = globalShadowSettings.lightPos;
+ snapshot.shadowSettings.lightRadius = globalShadowSettings.lightRadius;
// Note: this preserves existing behavior of shadowing the entire layer and not cropping
// it if transparent regions are present. This may not be necessary since shadows are
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 3d64b36..1506913 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -47,7 +47,7 @@
const DisplayInfos& displays;
// Set to true if there were display changes since last update.
bool displayChanges = false;
- const renderengine::ShadowSettings& globalShadowSettings;
+ const ShadowSettings& globalShadowSettings;
bool supportsBlur = true;
bool forceFullDamage = false;
std::optional<FloatRect> parentCrop = std::nullopt;
@@ -108,7 +108,7 @@
void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
- const renderengine::ShadowSettings& globalShadowSettings);
+ const ShadowSettings& globalShadowSettings);
void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
const Args& args);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index dc8694c..168267b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -122,8 +122,7 @@
isTrustedOverlay = false;
dropInputMode = gui::DropInputMode::NONE;
dimmingEnabled = true;
- defaultFrameRateCompatibility =
- static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
+ defaultFrameRateCompatibility = static_cast<int8_t>(scheduler::FrameRateCompatibility::Default);
frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
frameRateSelectionStrategy =
static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
index 186e878..dfb1c1e 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -96,7 +96,18 @@
return buffer;
}
-HdrSdrRatioOverlay::HdrSdrRatioOverlay()
+std::unique_ptr<HdrSdrRatioOverlay> HdrSdrRatioOverlay::create() {
+ std::unique_ptr<HdrSdrRatioOverlay> overlay =
+ std::make_unique<HdrSdrRatioOverlay>(ConstructorTag{});
+ if (overlay->initCheck()) {
+ return overlay;
+ }
+
+ ALOGE("%s: Failed to create HdrSdrRatioOverlay", __func__);
+ return {};
+}
+
+HdrSdrRatioOverlay::HdrSdrRatioOverlay(ConstructorTag)
: mSurfaceControl(
SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
if (!mSurfaceControl) {
@@ -109,6 +120,10 @@
.apply();
}
+bool HdrSdrRatioOverlay::initCheck() const {
+ return mSurfaceControl != nullptr;
+}
+
void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
mCurrentHdrSdrRatio = currentHdrSdrRatio;
animate();
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
index 69f95ec..72d401d 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.h
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -25,15 +25,22 @@
namespace android {
class HdrSdrRatioOverlay {
+private:
+ // Effectively making the constructor private, while keeping std::make_unique work
+ struct ConstructorTag {};
+
public:
- HdrSdrRatioOverlay();
+ static std::unique_ptr<HdrSdrRatioOverlay> create();
+
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
void animate();
void changeHdrSdrRatio(float currentRatio);
+ HdrSdrRatioOverlay(ConstructorTag);
+
private:
- float mCurrentHdrSdrRatio = 1.f;
+ bool initCheck() const;
static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags,
sp<GraphicBuffer>& ringBufer);
@@ -41,6 +48,7 @@
const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio);
+ float mCurrentHdrSdrRatio = 1.f;
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
size_t mIndex = 0;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a73c511..2dc8758 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -572,7 +572,7 @@
snapshot->outputFilter = getOutputFilter();
snapshot->isVisible = isVisible();
snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
- snapshot->shadowRadius = mEffectiveShadowRadius;
+ snapshot->shadowSettings.length = mEffectiveShadowRadius;
snapshot->contentDirty = contentDirty;
contentDirty = false;
@@ -1155,12 +1155,12 @@
if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
mDrawingState.defaultFrameRateCompatibility = compatibility;
mDrawingState.modified = true;
- mFlinger->mScheduler->setDefaultFrameRateCompatibility(this);
+ mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility);
setTransactionFlags(eTransactionNeeded);
return true;
}
-scheduler::LayerInfo::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
+scheduler::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
return mDrawingState.defaultFrameRateCompatibility;
}
@@ -1267,7 +1267,8 @@
return parentFrameRate;
}();
- *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
+ auto now = systemTime();
+ *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
// The frame rate is propagated to the children
bool childrenHaveFrameRate = false;
@@ -1283,7 +1284,8 @@
// layer as NoVote to allow the children to control the refresh rate
if (!frameRate.isValid() && childrenHaveFrameRate) {
*transactionNeeded |=
- setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+ setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote),
+ now);
}
// We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for
@@ -1492,7 +1494,7 @@
addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
}
-bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
@@ -1506,19 +1508,20 @@
setTransactionFlags(eTransactionNeeded);
mFlinger->mScheduler
- ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+ ->recordLayerHistory(sequence, getLayerProps(), now, now,
scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
return true;
}
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps,
+ nsecs_t now) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
mDrawingState.frameRateForLayerTree = frameRate;
mFlinger->mScheduler
- ->recordLayerHistory(sequence, layerProps, systemTime(),
+ ->recordLayerHistory(sequence, layerProps, now, now,
scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
return true;
}
@@ -3183,8 +3186,7 @@
} else {
// release sideband stream if it exists and a non null buffer is being set
if (mDrawingState.sidebandStream != nullptr) {
- mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
- mDrawingState.sidebandStream = nullptr;
+ setSidebandStream(nullptr, info, postTime);
}
}
@@ -3226,7 +3228,7 @@
mOwnerUid, postTime, getGameMode());
if (mFlinger->mLegacyFrontEndEnabled) {
- recordLayerHistoryBufferUpdate(getLayerProps());
+ recordLayerHistoryBufferUpdate(getLayerProps(), systemTime());
}
setFrameTimelineVsyncForBufferTransaction(info, postTime);
@@ -3257,7 +3259,7 @@
mDrawingState.isAutoTimestamp = isAutoTimestamp;
}
-void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps, nsecs_t now) {
ATRACE_CALL();
const nsecs_t presentTime = [&] {
if (!mDrawingState.isAutoTimestamp) {
@@ -3311,14 +3313,14 @@
ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
}
- mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
scheduler::LayerHistory::LayerUpdateType::Buffer);
}
-void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps, nsecs_t now) {
const nsecs_t presentTime =
mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
scheduler::LayerHistory::LayerUpdateType::AnimationTX);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 40882f4..0b66866 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -111,7 +111,7 @@
};
using FrameRate = scheduler::LayerInfo::FrameRate;
- using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ using FrameRateCompatibility = scheduler::FrameRateCompatibility;
using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
struct State {
@@ -908,10 +908,10 @@
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
- bool setFrameRateForLayerTreeLegacy(FrameRate);
- bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
- void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
- void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+ bool setFrameRateForLayerTreeLegacy(FrameRate, nsecs_t now);
+ bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&, nsecs_t now);
+ void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
+ void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
auto getLayerProps() const {
return scheduler::LayerProps{
.visible = isVisible(),
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 97c4145..48a9190 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -310,7 +310,7 @@
void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
const Rect& layerStackRect) const {
- renderengine::ShadowSettings state = mSnapshot->shadowSettings;
+ ShadowSettings state = mSnapshot->shadowSettings;
if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
return;
}
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 144e1f5..aa6026e 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -402,7 +402,7 @@
[&]() { return layerInfo->mutable_screen_bounds(); });
LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
[&]() { return layerInfo->mutable_corner_radius_crop(); });
- layerInfo->set_shadow_radius(snapshot.shadowRadius);
+ layerInfo->set_shadow_radius(snapshot.shadowSettings.length);
layerInfo->set_id(snapshot.uniqueSequence);
layerInfo->set_original_id(snapshot.sequence);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index be04c09..42676c6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -138,7 +138,20 @@
SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
+ ftl::Flags<Features> features) {
+ std::unique_ptr<RefreshRateOverlay> overlay =
+ std::make_unique<RefreshRateOverlay>(ConstructorTag{}, range, features);
+ if (overlay->initCheck()) {
+ return overlay;
+ }
+
+ ALOGE("%s: Failed to create RefreshRateOverlay", __func__);
+ return {};
+}
+
+RefreshRateOverlay::RefreshRateOverlay(ConstructorTag, FpsRange fpsRange,
+ ftl::Flags<Features> features)
: mFpsRange(fpsRange),
mFeatures(features),
mSurfaceControl(
@@ -154,6 +167,10 @@
.apply();
}
+bool RefreshRateOverlay::initCheck() const {
+ return mSurfaceControl != nullptr;
+}
+
auto RefreshRateOverlay::getOrCreateBuffers(Fps vsyncRate, Fps renderFps) -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index ae334e5..0fec470 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -37,6 +37,10 @@
class SurfaceFlinger;
class RefreshRateOverlay {
+private:
+ // Effectively making the constructor private, while keeping std::make_unique work
+ struct ConstructorTag {};
+
public:
enum class Features {
Spinner = 1 << 0,
@@ -45,7 +49,7 @@
SetByHwc = 1 << 3,
};
- RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
+ static std::unique_ptr<RefreshRateOverlay> create(FpsRange, ftl::Flags<Features>);
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
@@ -54,7 +58,11 @@
void animate();
bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
+ RefreshRateOverlay(ConstructorTag, FpsRange, ftl::Flags<Features>);
+
private:
+ bool initCheck() const;
+
using Buffers = std::vector<sp<GraphicBuffer>>;
static Buffers draw(int vsyncRate, int renderFps, SkColor, ui::Transform::RotationFlags,
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 21714da..9a55c94 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -38,6 +38,7 @@
#include <cutils/sched_policy.h>
#include <gui/DisplayEventReceiver.h>
+#include <gui/SchedulingPolicy.h>
#include <utils/Errors.h>
#include <utils/Trace.h>
@@ -171,10 +172,8 @@
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
- ResyncCallback resyncCallback,
EventRegistrationFlags eventRegistration)
- : resyncCallback(std::move(resyncCallback)),
- mOwnerUid(callingUid),
+ : mOwnerUid(callingUid),
mEventRegistration(eventRegistration),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -220,6 +219,10 @@
return binder::Status::ok();
}
+binder::Status EventThreadConnection::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ return gui::getSchedulingPolicy(outPolicy);
+}
+
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
constexpr auto toStatus = [](ssize_t size) {
return size < 0 ? status_t(size) : status_t(NO_ERROR);
@@ -250,9 +253,7 @@
EventThread::EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule> vsyncSchedule,
android::frametimeline::TokenManager* tokenManager,
- ThrottleVsyncCallback throttleVsyncCallback,
- GetVsyncPeriodFunction getVsyncPeriodFunction,
- std::chrono::nanoseconds workDuration,
+ IEventThreadCallback& callback, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration)
: mThreadName(name),
mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0),
@@ -261,11 +262,7 @@
mVsyncSchedule(std::move(vsyncSchedule)),
mVsyncRegistration(mVsyncSchedule->getDispatch(), createDispatchCallback(), name),
mTokenManager(tokenManager),
- mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
- mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) {
- LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
- "getVsyncPeriodFunction must not be null");
-
+ mCallback(callback) {
mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
std::unique_lock<std::mutex> lock(mMutex);
threadMain(lock);
@@ -307,10 +304,15 @@
}
sp<EventThreadConnection> EventThread::createEventConnection(
- ResyncCallback resyncCallback, EventRegistrationFlags eventRegistration) const {
- return sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
- IPCThreadState::self()->getCallingUid(),
- std::move(resyncCallback), eventRegistration);
+ EventRegistrationFlags eventRegistration) const {
+ auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
+ IPCThreadState::self()->getCallingUid(),
+ eventRegistration);
+ if (flags::misc1()) {
+ const int policy = SCHED_FIFO;
+ connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
+ }
+ return connection;
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -353,9 +355,7 @@
}
void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
- if (connection->resyncCallback) {
- connection->resyncCallback();
- }
+ mCallback.resync();
std::lock_guard<std::mutex> lock(mMutex);
@@ -371,20 +371,18 @@
const sp<EventThreadConnection>& connection) const {
// Resync so that the vsync is accurate with hardware. getLatestVsyncEventData is an alternate
// way to get vsync data (instead of posting callbacks to Choreographer).
- if (connection->resyncCallback) {
- connection->resyncCallback();
- }
+ mCallback.resync();
VsyncEventData vsyncEventData;
- nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid);
- vsyncEventData.frameInterval = frameInterval;
+ const Period frameInterval = mCallback.getVsyncPeriod(connection->mOwnerUid);
+ vsyncEventData.frameInterval = frameInterval.ns();
const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
std::lock_guard<std::mutex> lock(mMutex);
const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
return {vsyncTime, vsyncTime - mReadyDuration.count()};
}();
- generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC),
+ generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC),
presentTime, deadline);
return vsyncEventData;
}
@@ -549,9 +547,9 @@
connection->frameRate);
}
- return mThrottleVsyncCallback &&
- mThrottleVsyncCallback(event.vsync.vsyncData.preferredExpectedPresentationTime(),
- connection->mOwnerUid);
+ const auto expectedPresentTime =
+ TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime());
+ return mCallback.throttleVsync(expectedPresentTime, connection->mOwnerUid);
};
switch (event.header.type) {
@@ -671,9 +669,9 @@
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- const int64_t frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
- copy.vsync.vsyncData.frameInterval = frameInterval;
- generateFrameTimeline(copy.vsync.vsyncData, frameInterval, copy.header.timestamp,
+ const Period frameInterval = mCallback.getVsyncPeriod(consumer->mOwnerUid);
+ copy.vsync.vsyncData.frameInterval = frameInterval.ns();
+ generateFrameTimeline(copy.vsync.vsyncData, frameInterval.ns(), copy.header.timestamp,
event.vsync.vsyncData.preferredExpectedPresentationTime(),
event.vsync.vsyncData.preferredDeadlineTimestamp());
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 576910e..7842318 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -54,7 +54,6 @@
// ---------------------------------------------------------------------------
-using ResyncCallback = std::function<void()>;
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
enum class VSyncRequest {
@@ -69,7 +68,7 @@
class EventThreadConnection : public gui::BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
+ EventThreadConnection(EventThread*, uid_t callingUid,
EventRegistrationFlags eventRegistration = {});
virtual ~EventThreadConnection();
@@ -79,9 +78,7 @@
binder::Status setVsyncRate(int rate) override;
binder::Status requestNextVsync() override; // asynchronous
binder::Status getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) override;
-
- // Called in response to requestNextVsync.
- const ResyncCallback resyncCallback;
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
VSyncRequest vsyncRequest = VSyncRequest::None;
const uid_t mOwnerUid;
@@ -104,7 +101,7 @@
virtual ~EventThread();
virtual sp<EventThreadConnection> createEventConnection(
- ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0;
+ EventRegistrationFlags eventRegistration = {}) const = 0;
// Feed clients with fake VSYNC, e.g. while the display is off.
virtual void enableSyntheticVsync(bool) = 0;
@@ -136,20 +133,25 @@
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
};
+struct IEventThreadCallback {
+ virtual ~IEventThreadCallback() = default;
+
+ virtual bool throttleVsync(TimePoint, uid_t) = 0;
+ virtual Period getVsyncPeriod(uid_t) = 0;
+ virtual void resync() = 0;
+};
+
namespace impl {
class EventThread : public android::EventThread {
public:
- using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
- using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
-
EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule>,
- frametimeline::TokenManager*, ThrottleVsyncCallback, GetVsyncPeriodFunction,
+ frametimeline::TokenManager*, IEventThreadCallback& callback,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration);
~EventThread();
sp<EventThreadConnection> createEventConnection(
- ResyncCallback, EventRegistrationFlags eventRegistration = {}) const override;
+ EventRegistrationFlags eventRegistration = {}) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
@@ -214,8 +216,7 @@
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
- const ThrottleVsyncCallback mThrottleVsyncCallback;
- const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
+ IEventThreadCallback& mCallback;
std::thread mThread;
mutable std::mutex mMutex;
diff --git a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
new file mode 100644
index 0000000..405c982
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::scheduler {
+// FrameRateCompatibility specifies how we should interpret the frame rate associated with
+// the layer.
+enum class FrameRateCompatibility {
+ Default, // Layer didn't specify any specific handling strategy
+
+ Min, // Layer needs the minimum frame rate.
+
+ Exact, // Layer needs the exact frame rate.
+
+ ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+ // content properly. Any other value will result in a pull down.
+
+ NoVote, // Layer doesn't have any requirements for the refresh rate and
+ // should not be considered when the display refresh rate is determined.
+
+ ftl_last = NoVote
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 6b5327a..4e5659e 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -76,12 +76,12 @@
ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
-LayerHistory::LayerVoteType getVoteType(LayerInfo::FrameRateCompatibility compatibility,
+LayerHistory::LayerVoteType getVoteType(FrameRateCompatibility compatibility,
bool contentDetectionEnabled) {
LayerHistory::LayerVoteType voteType;
- if (!contentDetectionEnabled || compatibility == LayerInfo::FrameRateCompatibility::NoVote) {
+ if (!contentDetectionEnabled || compatibility == FrameRateCompatibility::NoVote) {
voteType = LayerHistory::LayerVoteType::NoVote;
- } else if (compatibility == LayerInfo::FrameRateCompatibility::Min) {
+ } else if (compatibility == FrameRateCompatibility::Min) {
voteType = LayerHistory::LayerVoteType::Min;
} else {
voteType = LayerHistory::LayerVoteType::Heuristic;
@@ -141,20 +141,20 @@
}
}
-void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDetectionEnabled) {
+void LayerHistory::setDefaultFrameRateCompatibility(int32_t id,
+ FrameRateCompatibility frameRateCompatibility,
+ bool contentDetectionEnabled) {
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;
- info->setDefaultLayerVote(
- getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
+ info->setDefaultLayerVote(getVoteType(frameRateCompatibility, contentDetectionEnabled));
}
auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5750ea7..bac1ec6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -29,6 +29,7 @@
#include "EventThread.h"
+#include "FrameRateCompatibility.h"
#include "RefreshRateSelector.h"
namespace android {
@@ -70,7 +71,8 @@
// Updates the default frame rate compatibility which takes effect when the app
// does not set a preference for refresh rate.
- void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
+ void setDefaultFrameRateCompatibility(int32_t id, FrameRateCompatibility frameRateCompatibility,
+ bool contentDetectionEnabled);
using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
@@ -89,6 +91,7 @@
private:
friend class LayerHistoryTest;
+ friend class LayerHistoryIntegrationTest;
friend class TestableScheduler;
using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 875bdc8..dd96930 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -455,7 +455,7 @@
return consistent;
}
-LayerInfo::FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
+FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
switch (compatibility) {
case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
return FrameRateCompatibility::Default;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 129b4c4..d580b58 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -28,6 +28,7 @@
#include <scheduler/Fps.h>
#include <scheduler/Seamlessness.h>
+#include "FrameRateCompatibility.h"
#include "LayerHistory.h"
#include "RefreshRateSelector.h"
@@ -61,6 +62,7 @@
static constexpr size_t kNumSmallDirtyThreshold = 2;
friend class LayerHistoryTest;
+ friend class LayerHistoryIntegrationTest;
friend class LayerInfoTest;
public:
@@ -78,24 +80,6 @@
using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>;
- // FrameRateCompatibility specifies how we should interpret the frame rate associated with
- // the layer.
- enum class FrameRateCompatibility {
- Default, // Layer didn't specify any specific handling strategy
-
- Min, // Layer needs the minimum frame rate.
-
- Exact, // Layer needs the exact frame rate.
-
- ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
- // content properly. Any other value will result in a pull down.
-
- NoVote, // Layer doesn't have any requirements for the refresh rate and
- // should not be considered when the display refresh rate is determined.
-
- ftl_last = NoVote
- };
-
enum class FrameRateSelectionStrategy {
Self,
OverrideChildren,
@@ -281,6 +265,7 @@
private:
friend class LayerHistoryTest;
+ friend class LayerHistoryIntegrationTest;
// Holds the refresh rate when it was calculated
struct RefreshRateData {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index aa24f56..5115c42 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -192,7 +192,8 @@
for (const auto& [id, display] : mDisplays) {
if (id == pacesetterId) continue;
- const FrameTargeter& targeter = *display.targeterPtr;
+ FrameTargeter& targeter = *display.targeterPtr;
+ targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
targets.try_emplace(id, &targeter.target());
}
@@ -206,11 +207,20 @@
if (id == pacesetterId) continue;
FrameTargeter& targeter = *display.targeterPtr;
- targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
-
targeters.try_emplace(id, &targeter);
}
+ if (flagutils::vrrConfigEnabled() &&
+ CC_UNLIKELY(mPacesetterFrameDurationFractionToSkip > 0.f)) {
+ const auto period = pacesetterTargeter.target().expectedFrameDuration();
+ const auto skipDuration = Duration::fromNs(
+ static_cast<nsecs_t>(period.ns() * mPacesetterFrameDurationFractionToSkip));
+ ATRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)",
+ mPacesetterFrameDurationFractionToSkip * 100, skipDuration.ns());
+ std::this_thread::sleep_for(skipDuration);
+ mPacesetterFrameDurationFractionToSkip = 0.f;
+ }
+
const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters);
compositor.sample();
@@ -242,36 +252,35 @@
return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), frameRate);
}
-impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
- return [this](nsecs_t expectedVsyncTime, uid_t uid) {
- return !isVsyncValid(TimePoint::fromNs(expectedVsyncTime), uid);
- };
+bool Scheduler::throttleVsync(android::TimePoint expectedPresentTime, uid_t uid) {
+ return !isVsyncValid(expectedPresentTime, uid);
}
-impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
- return [this](uid_t uid) {
- const auto [refreshRate, period] = [this] {
- std::scoped_lock lock(mDisplayLock);
- const auto pacesetterOpt = pacesetterDisplayLocked();
- LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
- const Display& pacesetter = *pacesetterOpt;
- return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
- pacesetter.schedulePtr->period());
- }();
+Period Scheduler::getVsyncPeriod(uid_t uid) {
+ const auto [refreshRate, period] = [this] {
+ std::scoped_lock lock(mDisplayLock);
+ const auto pacesetterOpt = pacesetterDisplayLocked();
+ LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+ const Display& pacesetter = *pacesetterOpt;
+ return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
+ pacesetter.schedulePtr->period());
+ }();
- const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
+ const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
- const auto frameRate = getFrameRateOverride(uid);
- if (!frameRate.has_value()) {
- return currentPeriod.ns();
- }
+ const auto frameRate = getFrameRateOverride(uid);
+ if (!frameRate.has_value()) {
+ return currentPeriod;
+ }
- const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
- if (divisor <= 1) {
- return currentPeriod.ns();
- }
- return currentPeriod.ns() * divisor;
- };
+ const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
+ if (divisor <= 1) {
+ return currentPeriod;
+ }
+
+ // TODO(b/299378819): the casting is not needed, but we need a flag as it might change
+ // behaviour.
+ return Period::fromNs(currentPeriod.ns() * divisor);
}
ConnectionHandle Scheduler::createEventThread(Cycle cycle,
@@ -279,9 +288,7 @@
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration) {
auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
- getVsyncSchedule(), tokenManager,
- makeThrottleVsyncCallback(),
- makeGetVsyncPeriodFunction(),
+ getVsyncSchedule(), tokenManager, *this,
workDuration, readyDuration);
auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
@@ -293,7 +300,7 @@
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
- auto connection = eventThread->createEventConnection([&] { resync(); });
+ auto connection = eventThread->createEventConnection();
std::lock_guard<std::mutex> lock(mConnectionsLock);
mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
@@ -307,8 +314,7 @@
std::scoped_lock lock(mConnectionsLock);
RETURN_IF_INVALID_HANDLE(handle, nullptr);
- return mConnections[handle].thread->createEventConnection([&] { resync(); },
- eventRegistration);
+ return mConnections[handle].thread->createEventConnection(eventRegistration);
}();
const auto layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
@@ -618,9 +624,9 @@
}
void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
- LayerHistory::LayerUpdateType updateType) {
+ nsecs_t now, LayerHistory::LayerUpdateType updateType) {
if (pacesetterSelectorPtr()->canSwitch()) {
- mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
+ mLayerHistory.record(id, layerProps, presentTime, now, updateType);
}
}
@@ -628,8 +634,9 @@
mLayerHistory.setModeChangePending(pending);
}
-void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) {
- mLayerHistory.setDefaultFrameRateCompatibility(layer,
+void Scheduler::setDefaultFrameRateCompatibility(
+ int32_t id, scheduler::FrameRateCompatibility frameRateCompatibility) {
+ mLayerHistory.setDefaultFrameRateCompatibility(id, frameRateCompatibility,
mFeatures.test(Feature::kContentDetection));
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 822f7cc..3441318 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -98,7 +98,7 @@
class VsyncSchedule;
-class Scheduler : android::impl::MessageQueue {
+class Scheduler : public IEventThreadCallback, android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
public:
@@ -217,7 +217,6 @@
ftl::FakeGuard guard(kMainThreadContext);
resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate);
}
- void resync() EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. Returns true if
@@ -231,9 +230,9 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
- LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
+ nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
- void setDefaultFrameRateCompatibility(Layer*);
+ void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
void deregisterLayer(Layer*);
void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
@@ -324,6 +323,11 @@
return mFeatures.test(Feature::kSmallDirtyContentDetection);
}
+ // Injects a delay that is a fraction of the predicted frame duration for the next frame.
+ void injectPacesetterDelay(float frameDurationFraction) REQUIRES(kMainThreadContext) {
+ mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
+ }
+
private:
friend class TestableScheduler;
@@ -420,8 +424,10 @@
void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
- android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
- android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
+ // IEventThreadCallback overrides
+ bool throttleVsync(TimePoint, uid_t) override;
+ Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
+ void resync() override EXCLUDES(mDisplayLock);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -451,6 +457,9 @@
// Timer used to monitor display power mode.
ftl::Optional<OneShotTimer> mDisplayPowerTimer;
+ // Injected delay prior to compositing, for simulating jank.
+ float mPacesetterFrameDurationFractionToSkip GUARDED_BY(kMainThreadContext) = 0.f;
+
ISchedulerCallback& mSchedulerCallback;
// mDisplayLock may be locked while under mPolicyLock.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 1f922f1..c4c9fa5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -28,10 +28,13 @@
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
+#include <com_android_graphics_surfaceflinger_flags.h>
+
#undef LOG_TAG
#define LOG_TAG "VSyncDispatch"
namespace android::scheduler {
+using namespace com::android::graphics::surfaceflinger;
using base::StringAppendF;
@@ -100,8 +103,14 @@
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
- if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
- return getExpectedCallbackTime(nextVsyncTime, timing);
+ if (flags::dont_skip_on_early()) {
+ if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
+ return getExpectedCallbackTime(mArmedInfo->mActualVsyncTime, timing);
+ }
+ } else {
+ if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
+ return getExpectedCallbackTime(nextVsyncTime, timing);
+ }
}
nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b7d2f9a..7e799bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -113,6 +113,7 @@
#include <vector>
#include <gui/LayerStatePermissions.h>
+#include <gui/SchedulingPolicy.h>
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
@@ -2202,35 +2203,46 @@
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;
- }
+void SurfaceFlinger::updateLayerHistory(nsecs_t now) {
+ for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ using Changes = frontend::RequestedLayerState::Changes;
+ if (snapshot->path.isClone()) {
+ continue;
+ }
- const auto layerProps = scheduler::LayerProps{
- .visible = snapshot.isVisible,
- .bounds = snapshot.geomLayerBounds,
- .transform = snapshot.geomLayerTransform,
- .setFrameRateVote = snapshot.frameRate,
- .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
- };
+ if (!snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
+ (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
+ continue;
+ }
- auto it = mLegacyLayers.find(snapshot.sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot.getDebugString().c_str());
+ const auto layerProps = scheduler::LayerProps{
+ .visible = snapshot->isVisible,
+ .bounds = snapshot->geomLayerBounds,
+ .transform = snapshot->geomLayerTransform,
+ .setFrameRateVote = snapshot->frameRate,
+ .frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
+ };
- if (snapshot.changes.test(Changes::Animation)) {
- it->second->recordLayerHistoryAnimationTx(layerProps);
- }
+ 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::FrameRate)) {
- it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
- }
+ if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
+ snapshot->defaultFrameRateCompatibility);
+ }
- if (snapshot.changes.test(Changes::Buffer)) {
- it->second->recordLayerHistoryBufferUpdate(layerProps);
+ if (snapshot->changes.test(Changes::Animation)) {
+ it->second->recordLayerHistoryAnimationTx(layerProps, now);
+ }
+
+ if (snapshot->changes.test(Changes::FrameRate)) {
+ it->second->setFrameRateForLayerTree(snapshot->frameRate, layerProps, now);
+ }
+
+ if (snapshot->changes.test(Changes::Buffer)) {
+ it->second->recordLayerHistoryBufferUpdate(layerProps, now);
+ }
}
}
@@ -2369,8 +2381,8 @@
mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
+ updateLayerHistory(latchTime);
mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- updateLayerHistory(snapshot);
if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) ==
mLayersIdsWithQueuedFrames.end())
return;
@@ -4015,7 +4027,7 @@
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
- if (base::GetBoolProperty("debug.sf.enable_small_dirty_detection"s, false)) {
+ if (flags::vrr_small_dirty_detection()) {
features |= Feature::kSmallDirtyContentDetection;
}
}
@@ -4822,7 +4834,7 @@
for (const auto& listener : listenerCallbacks) {
mTransactionCallbackInvoker.addEmptyTransaction(listener);
}
-
+ nsecs_t now = systemTime();
uint32_t clientStateFlags = 0;
for (auto& resolvedState : states) {
if (mLegacyFrontEndEnabled) {
@@ -4844,7 +4856,7 @@
.setFrameRateVote = layer->getFrameRateForLayerTree(),
.frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
};
- layer->recordLayerHistoryAnimationTx(layerProps);
+ layer->recordLayerHistoryAnimationTx(layerProps, now);
}
}
}
@@ -6434,6 +6446,8 @@
mMisc2FlagEarlyBootValue == mMisc2FlagLateBootValue ? "stable" : "modified");
StringAppendF(&result, "VrrConfigFlagValue: %s\n",
flagutils::vrrConfigEnabled() ? "true" : "false");
+ StringAppendF(&result, "DontSkipOnEarlyFlagValue: %s\n",
+ flags::dont_skip_on_early() ? "true" : "false");
getRenderEngine().dump(result);
@@ -6579,6 +6593,7 @@
case GET_ACTIVE_DISPLAY_MODE:
case GET_DISPLAY_COLOR_MODES:
case GET_DISPLAY_MODES:
+ case GET_SCHEDULING_POLICY:
// Calling setTransactionState is safe, because you need to have been
// granted a reference to Client* and Handle* to do anything with it.
case SET_TRANSACTION_STATE: {
@@ -6660,9 +6675,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1044 are currently used for backdoors. The code
+ // Numbers from 1000 to 1045 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1044) {
+ if (code >= 1000 && code <= 1045) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -7151,6 +7166,39 @@
}
return NO_ERROR;
}
+ // Inject jank
+ // First argument is a float that describes the fraction of frame duration to jank by.
+ // Second argument is a delay in ms for triggering the jank. This is useful for working
+ // with tools that steal the adb connection. This argument is optional.
+ case 1045: {
+ if (flagutils::vrrConfigEnabled()) {
+ float jankAmount = data.readFloat();
+ int32_t jankDelayMs = 0;
+ if (data.readInt32(&jankDelayMs) != NO_ERROR) {
+ jankDelayMs = 0;
+ }
+
+ const auto jankDelayDuration = Duration(std::chrono::milliseconds(jankDelayMs));
+
+ const bool jankAmountValid = jankAmount > 0.0 && jankAmount < 100.0;
+
+ if (!jankAmountValid) {
+ ALOGD("Ignoring invalid jank amount: %f", jankAmount);
+ reply->writeInt32(BAD_VALUE);
+ return BAD_VALUE;
+ }
+
+ (void)mScheduler->scheduleDelayed(
+ [&, jankAmount]() FTL_FAKE_GUARD(kMainThreadContext) {
+ mScheduler->injectPacesetterDelay(jankAmount);
+ scheduleComposite(FrameHint::kActive);
+ },
+ jankDelayDuration.ns());
+ reply->writeInt32(NO_ERROR);
+ return NO_ERROR;
+ }
+ return err;
+ }
}
}
return err;
@@ -8983,6 +9031,10 @@
const sp<Client> client = sp<Client>::make(mFlinger);
if (client->initCheck() == NO_ERROR) {
*outClient = client;
+ if (flags::misc1()) {
+ const int policy = SCHED_FIFO;
+ client->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
+ }
return binder::Status::ok();
} else {
*outClient = nullptr;
@@ -9764,6 +9816,10 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ return gui::getSchedulingPolicy(outPolicy);
+}
+
status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 42825f7..cbea312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -419,7 +419,7 @@
bool colorMatrixChanged = true;
mat4 colorMatrix;
- renderengine::ShadowSettings globalShadowSettings;
+ ShadowSettings globalShadowSettings;
void traverse(const LayerVector::Visitor& visitor) const;
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
@@ -732,7 +732,7 @@
bool& out) REQUIRES(kMainThreadContext);
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
- void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
+ void updateLayerHistory(nsecs_t now);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
@@ -1581,8 +1581,9 @@
gui::WindowInfosListenerInfo* outInfo) override;
binder::Status removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) override;
- binder::Status getStalledTransactionInfo(int pid,
- std::optional<gui::StalledTransactionInfo>* outInfo);
+ binder::Status getStalledTransactionInfo(
+ int pid, std::optional<gui::StalledTransactionInfo>* outInfo) override;
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 5512734..922fd07 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -2,6 +2,9 @@
"imports": [
{
"path": "frameworks/native/libs/gui"
+ },
+ {
+ "path": "frameworks/native/services/inputflinger"
}
],
"presubmit": [
@@ -33,6 +36,9 @@
"hwasan-presubmit": [
{
"name": "libscheduler_test"
+ },
+ {
+ "name": "libsurfaceflinger_unittest"
}
]
}
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 23fe8fa..c2d1954 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -56,7 +56,7 @@
frontend::LayerSnapshotBuilder snapshotBuilder;
ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
- renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
+ ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
char value[PROPERTY_VALUE_MAX];
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlur = atoi(value);
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 7062a4e..effbfdb 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -56,31 +56,35 @@
ATRACE_NAME("WindowInfosListenerInvoker::removeWindowInfosListener");
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
- mWindowInfosListeners.erase(asBinder);
+ eraseListenerAndAckMessages(asBinder);
}});
}
void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) {
BackgroundExecutor::getInstance().sendCallbacks({[this, who]() {
ATRACE_NAME("WindowInfosListenerInvoker::binderDied");
- auto it = mWindowInfosListeners.find(who);
- int64_t listenerId = it->second.first;
- mWindowInfosListeners.erase(who);
-
- std::vector<int64_t> vsyncIds;
- for (auto& [vsyncId, state] : mUnackedState) {
- if (std::find(state.unackedListenerIds.begin(), state.unackedListenerIds.end(),
- listenerId) != state.unackedListenerIds.end()) {
- vsyncIds.push_back(vsyncId);
- }
- }
-
- for (int64_t vsyncId : vsyncIds) {
- ackWindowInfosReceived(vsyncId, listenerId);
- }
+ eraseListenerAndAckMessages(who);
}});
}
+void WindowInfosListenerInvoker::eraseListenerAndAckMessages(const wp<IBinder>& binder) {
+ auto it = mWindowInfosListeners.find(binder);
+ int64_t listenerId = it->second.first;
+ mWindowInfosListeners.erase(binder);
+
+ std::vector<int64_t> vsyncIds;
+ for (auto& [vsyncId, state] : mUnackedState) {
+ if (std::find(state.unackedListenerIds.begin(), state.unackedListenerIds.end(),
+ listenerId) != state.unackedListenerIds.end()) {
+ vsyncIds.push_back(vsyncId);
+ }
+ }
+
+ for (int64_t vsyncId : vsyncIds) {
+ ackWindowInfosReceived(vsyncId, listenerId);
+ }
+}
+
void WindowInfosListenerInvoker::windowInfosChanged(
gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
bool forceImmediateCall) {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index f36b0ed..261fd0f 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -67,6 +67,7 @@
std::optional<gui::WindowInfosUpdate> mDelayedUpdate;
WindowInfosReportedListenerSet mReportedListeners;
+ void eraseListenerAndAckMessages(const wp<IBinder>&);
struct UnackedState {
ftl::SmallVector<int64_t, kStaticCapacity> unackedListenerIds;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index ed8bb7f..d13189e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -94,6 +94,7 @@
BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER,
BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER,
+ BnSurfaceComposer::GET_SCHEDULING_POLICY,
};
static constexpr uint32_t kMinCode = 1000;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index cbbfa16..2f5a360 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -97,21 +97,26 @@
return displayId;
}
+struct EventThreadCallback : public IEventThreadCallback {
+ bool throttleVsync(TimePoint, uid_t) override { return false; }
+ Period getVsyncPeriod(uid_t) override { return kSyncPeriod; }
+ void resync() override {}
+};
+
void SchedulerFuzzer::fuzzEventThread() {
mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
new scheduler::VsyncSchedule(getPhysicalDisplayId(),
std::make_shared<mock::VSyncTracker>(),
std::make_shared<mock::VSyncDispatch>(), nullptr));
- const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
+ EventThreadCallback callback;
std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
- android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, nullptr, getVsyncPeriod,
+ android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, callback,
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
(std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
sp<EventThreadConnection> connection =
- sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>(),
- nullptr);
+ sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>());
thread->requestNextVsync(connection);
thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection);
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index cf23169..9599452 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -149,6 +149,9 @@
// publish gui::ISurfaceComposer, the new AIDL interface
sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
+ if (flags::misc1()) {
+ composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
+ }
sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index bfc03aa..becbb87 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -11,7 +11,7 @@
flag {
name: "connected_display"
namespace: "core_graphics"
- description: "Controls SurfaceFlinger support for Connected Displays"
+ description: "Controls SurfaceFlinger support for Connected Displays in 24Q1"
bug: "278199093"
is_fixed_read_only: true
}
@@ -30,3 +30,18 @@
bug: "284845445"
is_fixed_read_only: true
}
+
+flag {
+ name: "vrr_small_dirty_detection"
+ namespace: "core_graphics"
+ description: "Controls small dirty detection for VRR"
+ bug: "283055450"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "dont_skip_on_early"
+ namespace: "core_graphics"
+ description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
+ bug: "273702768"
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index b5168b0..67f2dca 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -30,6 +30,7 @@
test_suites: ["device-tests"],
srcs: [
"BootDisplayMode_test.cpp",
+ "Binder_test.cpp",
"BufferGenerator.cpp",
"CommonTypes_test.cpp",
"Credentials_test.cpp",
@@ -66,6 +67,7 @@
static_libs: [
"liblayers_proto",
"android.hardware.graphics.composer@2.1",
+ "libsurfaceflingerflags",
],
shared_libs: [
"android.hardware.graphics.common@1.2",
diff --git a/services/surfaceflinger/tests/Binder_test.cpp b/services/surfaceflinger/tests/Binder_test.cpp
new file mode 100644
index 0000000..3152973
--- /dev/null
+++ b/services/surfaceflinger/tests/Binder_test.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sched.h>
+
+#include <android/gui/ISurfaceComposer.h>
+#include <android/gui/ISurfaceComposerClient.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+
+#include <com_android_graphics_surfaceflinger_flags.h>
+
+namespace android::test {
+using namespace com::android::graphics::surfaceflinger;
+
+class BinderTest : public ::testing::Test {
+protected:
+ BinderTest();
+
+ void SetUp() override;
+
+ void getSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+ void getNonAidlSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+ void getClientSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+ void getDisplayEventConnectionSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+
+private:
+ sp<gui::ISurfaceComposer> mISurfaceComposerAidl;
+ sp<ISurfaceComposer> mISurfaceComposer;
+ sp<gui::ISurfaceComposerClient> mISurfaceComposerClient;
+ sp<gui::IDisplayEventConnection> mConnection;
+};
+
+BinderTest::BinderTest() {
+ const String16 name("SurfaceFlingerAIDL");
+ mISurfaceComposerAidl = waitForService<gui::ISurfaceComposer>(String16("SurfaceFlingerAIDL"));
+ mISurfaceComposer = waitForService<ISurfaceComposer>(String16("SurfaceFlinger"));
+ mISurfaceComposerAidl->createConnection(&mISurfaceComposerClient);
+ mISurfaceComposerAidl
+ ->createDisplayEventConnection(gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+ gui::ISurfaceComposer::EventRegistration(0), {},
+ &mConnection);
+}
+
+void BinderTest::SetUp() {
+ ASSERT_TRUE(mISurfaceComposerAidl);
+ ASSERT_TRUE(mISurfaceComposer);
+ ASSERT_TRUE(mISurfaceComposerClient);
+ ASSERT_TRUE(mConnection);
+}
+
+void BinderTest::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ const auto status = mISurfaceComposerAidl->getSchedulingPolicy(outPolicy);
+ ASSERT_TRUE(status.isOk());
+}
+
+void BinderTest::getNonAidlSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ Parcel data, reply;
+ const status_t status =
+ IInterface::asBinder(mISurfaceComposer)
+ ->transact(BnSurfaceComposer::GET_SCHEDULING_POLICY, data, &reply);
+ ASSERT_EQ(OK, status);
+
+ outPolicy->policy = reply.readInt32();
+ outPolicy->priority = reply.readInt32();
+}
+
+void BinderTest::getClientSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ const auto status = mISurfaceComposerClient->getSchedulingPolicy(outPolicy);
+ ASSERT_TRUE(status.isOk());
+}
+
+void BinderTest::getDisplayEventConnectionSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ const auto status = mConnection->getSchedulingPolicy(outPolicy);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(BinderTest, SchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTest, NonAidlSchedulingPolicy) {
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getNonAidlSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTest, ClientSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getClientSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTest, DisplayEventConnectionSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getDisplayEventConnectionSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+class BinderTestRtCaller : public BinderTest {
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+private:
+ int mOrigPolicy;
+ int mOrigPriority;
+};
+
+void BinderTestRtCaller::SetUp() {
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ mOrigPolicy = sched_getscheduler(0);
+ struct sched_param origSchedParam;
+ ASSERT_GE(0, sched_getparam(0, &origSchedParam)) << "errno: " << strerror(errno);
+ mOrigPriority = origSchedParam.sched_priority;
+
+ struct sched_param param;
+ param.sched_priority = priority;
+ ASSERT_GE(0, sched_setscheduler(0, policy, ¶m)) << "errno: " << strerror(errno);
+}
+
+void BinderTestRtCaller::TearDown() {
+ struct sched_param origSchedParam;
+ origSchedParam.sched_priority = mOrigPriority;
+ ASSERT_GE(0, sched_setscheduler(0, mOrigPolicy, &origSchedParam))
+ << "errno: " << strerror(errno);
+}
+
+TEST_F(BinderTestRtCaller, SchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTestRtCaller, NonAidlSchedulingPolicy) {
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getNonAidlSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTestRtCaller, ClientSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getClientSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTestRtCaller, DisplayEventConnectionSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getDisplayEventConnectionSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+} // namespace android::test
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 2fcb9e0..3c09422 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -126,7 +126,7 @@
<< info.touchableRegionBounds.right << "," << info.touchableRegionBounds.bottom << "}";
}
-struct find_id : std::unary_function<LayerInfo, bool> {
+struct find_id {
uint64_t id;
find_id(uint64_t id) : id(id) {}
bool operator()(LayerInfo const& m) const { return m.id == id; }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f4516c7..91910c7 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,6 +42,13 @@
],
}
+cc_aconfig_library {
+ name: "libsurfaceflingerflags_test",
+ aconfig_declarations: "surfaceflinger_flags",
+ // TODO(b/304338314): uncomment the below line once the bug is fixed
+ // test: true,
+}
+
cc_test {
name: "libsurfaceflinger_unittest",
defaults: [
@@ -50,22 +57,6 @@
"surfaceflinger_defaults",
],
test_suites: ["device-tests"],
- sanitize: {
- // Using the address sanitizer not only helps uncover issues in the code
- // covered by the tests, but also covers some of the tricky injection of
- // fakes the unit tests currently do.
- //
- // Note: If you get an runtime link error like:
- //
- // CANNOT LINK EXECUTABLE "/data/local/tmp/libsurfaceflinger_unittest": library "libclang_rt.asan-aarch64-android.so" not found
- //
- // it is because the address sanitizer shared objects are not installed
- // by default in the system image.
- //
- // You can either "make dist tests" before flashing, or set this
- // option to false temporarily.
- address: true,
- },
static_libs: ["libc++fs"],
srcs: [
":libsurfaceflinger_mock_sources",
@@ -93,6 +84,7 @@
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
+ "LayerHistoryIntegrationTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
"LayerHierarchyTest.cpp",
@@ -183,6 +175,7 @@
"libtimestats_proto",
"libtonemap",
"perfetto_trace_protos",
+ "libsurfaceflingerflags_test",
],
shared_libs: [
"android.hardware.configstore-utils",
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index e32cf88..fa31643 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -67,15 +67,13 @@
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(mEventThread,
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ .WillOnce(Return(
+ sp<EventThreadConnection>::make(mEventThread, mock::EventThread::kCallingUid)));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread,
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 9b46009..8891c06 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -57,7 +57,7 @@
} // namespace
-class EventThreadTest : public testing::Test {
+class EventThreadTest : public testing::Test, public IEventThreadCallback {
protected:
static constexpr std::chrono::nanoseconds kWorkDuration = 0ms;
static constexpr std::chrono::nanoseconds kReadyDuration = 3ms;
@@ -65,10 +65,8 @@
class MockEventThreadConnection : public EventThreadConnection {
public:
MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
- ResyncCallback&& resyncCallback,
EventRegistrationFlags eventRegistration)
- : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
- eventRegistration) {}
+ : EventThreadConnection(eventThread, callingUid, eventRegistration) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -78,7 +76,14 @@
EventThreadTest();
~EventThreadTest() override;
- void setupEventThread(std::chrono::nanoseconds vsyncPeriod);
+ void SetUp() override { mVsyncPeriod = VSYNC_PERIOD; }
+
+ // IEventThreadCallback overrides
+ bool throttleVsync(TimePoint, uid_t) override;
+ Period getVsyncPeriod(uid_t) override;
+ void resync() override;
+
+ void setupEventThread();
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
EventRegistrationFlags eventRegistration = {},
uid_t ownerUid = mConnectionUid);
@@ -92,8 +97,7 @@
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventFrameTimelinesCorrect(
nsecs_t expectedTimestamp, gui::VsyncEventData::FrameTimeline preferredVsyncData);
- void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData,
- std::chrono::nanoseconds vsyncPeriod);
+ void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -133,6 +137,8 @@
sp<MockEventThreadConnection> mThrottledConnection;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
+ std::chrono::nanoseconds mVsyncPeriod;
+
static constexpr uid_t mConnectionUid = 443;
static constexpr uid_t mThrottledConnectionUid = 177;
};
@@ -168,17 +174,24 @@
EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value());
}
-void EventThreadTest::setupEventThread(std::chrono::nanoseconds vsyncPeriod) {
- const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
- mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
- return (uid == mThrottledConnectionUid);
- };
- const auto getVsyncPeriod = [vsyncPeriod](uid_t uid) { return vsyncPeriod.count(); };
+bool EventThreadTest::throttleVsync(android::TimePoint expectedVsyncTimestamp, uid_t uid) {
+ mThrottleVsyncCallRecorder.recordCall(expectedVsyncTimestamp.ns(), uid);
+ return (uid == mThrottledConnectionUid);
+}
+Period EventThreadTest::getVsyncPeriod(uid_t) {
+ return mVsyncPeriod;
+}
+
+void EventThreadTest::resync() {
+ mResyncCallRecorder.recordCall();
+}
+
+void EventThreadTest::setupEventThread() {
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule,
- mTokenManager.get(), throttleVsync,
- getVsyncPeriod, kWorkDuration, kReadyDuration);
+ mTokenManager.get(), *this, kWorkDuration,
+ kReadyDuration);
// EventThread should register itself as VSyncSource callback.
EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
@@ -200,9 +213,7 @@
ConnectionEventRecorder& recorder, EventRegistrationFlags eventRegistration,
uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
- sp<MockEventThreadConnection>::make(mThread.get(), ownerUid,
- mResyncCallRecorder.getInvocable(),
- eventRegistration);
+ sp<MockEventThreadConnection>::make(mThread.get(), ownerUid, eventRegistration);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -292,10 +303,9 @@
}
}
-void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(
- VsyncEventData vsyncEventData, std::chrono::nanoseconds vsyncPeriod) {
+void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData) {
float nonPreferredTimelinesAmount =
- scheduler::VsyncConfig::kEarlyLatchMaxThreshold / vsyncPeriod;
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold / mVsyncPeriod;
EXPECT_LE(vsyncEventData.frameTimelinesLength, nonPreferredTimelinesAmount + 1)
<< "Amount of non-preferred frame timelines too many;"
<< " expected presentation time will be over threshold";
@@ -357,7 +367,7 @@
*/
TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value());
@@ -368,7 +378,7 @@
}
TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
@@ -381,7 +391,7 @@
}
TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -414,7 +424,7 @@
}
TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -428,12 +438,12 @@
}
TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesValidLength) {
+ setupEventThread();
// The VsyncEventData should not have kFrameTimelinesCapacity amount of valid frame timelines,
// due to longer vsync period and kEarlyLatchMaxThreshold. Use length-2 to avoid decimal
// truncation (e.g. 60Hz has 16.6... ms vsync period).
- std::chrono::nanoseconds vsyncPeriod(scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
- (VsyncEventData::kFrameTimelinesCapacity - 2));
- setupEventThread(vsyncPeriod);
+ mVsyncPeriod = (scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
+ (VsyncEventData::kFrameTimelinesCapacity - 2));
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -449,11 +459,11 @@
ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
<< expectedTimestamp;
const VsyncEventData vsyncEventData = std::get<0>(args.value()).vsync.vsyncData;
- expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, vsyncPeriod);
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData);
}
TEST_F(EventThreadTest, getLatestVsyncEventData) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const nsecs_t now = systemTime();
const nsecs_t preferredExpectedPresentationTime = now + 20000000;
@@ -469,7 +479,7 @@
// Check EventThread immediately requested a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
- expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, VSYNC_PERIOD);
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData);
EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
<< "Deadline timestamp should be greater than frame time";
for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
@@ -508,7 +518,7 @@
}
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
@@ -537,7 +547,7 @@
}
TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setVsyncRate(1, mConnection);
@@ -562,7 +572,7 @@
}
TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setVsyncRate(2, mConnection);
@@ -590,7 +600,7 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setVsyncRate(1, mConnection);
@@ -609,7 +619,7 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -635,7 +645,7 @@
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -661,42 +671,42 @@
}
TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->setDuration(321ns, 456ns);
expectVSyncSetDurationCallReceived(321ns, 456ns);
}
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugInternalConnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugExternalConnect) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
@@ -710,7 +720,7 @@
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(EXTERNAL_DISPLAY_ID)
@@ -724,7 +734,7 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(DISPLAY_ID_64BIT)
@@ -737,7 +747,7 @@
}
TEST_F(EventThreadTest, suppressConfigChanged) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
ConnectionEventRecorder suppressConnectionEventRecorder{0};
sp<MockEventThreadConnection> suppressConnection =
@@ -758,7 +768,7 @@
}
TEST_F(EventThreadTest, postUidFrameRateMapping) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
@@ -771,7 +781,7 @@
}
TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
@@ -791,7 +801,7 @@
}
TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
- setupEventThread(VSYNC_PERIOD);
+ setupEventThread();
// Signal that we want the next vsync event to be posted to the throttled connection
mThread->requestNextVsync(mThrottledConnection);
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h
new file mode 100644
index 0000000..7103684
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FlagUtils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
+
+namespace android {
+class TestFlagSetter {
+public:
+ TestFlagSetter(bool (*getter)(), void((*setter)(bool)), bool flagValue) {
+ const bool initialValue = getter();
+ setter(flagValue);
+ mResetFlagValue = [=] { setter(initialValue); };
+ }
+
+ ~TestFlagSetter() { mResetFlagValue(); }
+
+private:
+ std::function<void()> mResetFlagValue;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index d7ac038..d319dcc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -18,11 +18,14 @@
#include <gtest/gtest.h>
#include <gui/fake/BufferData.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <ui/ShadowSettings.h>
#include "Client.h" // temporarily needed for LayerCreationArgs
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerSnapshotBuilder.h"
namespace android::surfaceflinger::frontend {
@@ -358,6 +361,19 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.defaultFrameRateCompatibility =
+ defaultFrameRateCompatibility;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setRoundedCorners(uint32_t id, float radius) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -384,6 +400,16 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setBuffer(uint32_t id) {
+ static uint64_t sBufferId = 1;
+ setBuffer(id,
+ std::make_shared<renderengine::mock::
+ FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ sBufferId++,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED /*usage*/));
+ }
+
void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -406,7 +432,64 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setShadowRadius(uint32_t id, float shadowRadius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.shadowRadius = shadowRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
+class LayerSnapshotTestBase : public LayerHierarchyTestBase {
+protected:
+ LayerSnapshotTestBase() : LayerHierarchyTestBase() {}
+
+ void createRootLayer(uint32_t id) override {
+ LayerHierarchyTestBase::createRootLayer(id);
+ setColor(id);
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) override {
+ LayerHierarchyTestBase::createLayer(id, parentId);
+ setColor(parentId);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
+ LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
+ setColor(id);
+ }
+
+ void update(LayerSnapshotBuilder& snapshotBuilder) {
+ if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ mHierarchyBuilder.update(mLifecycleManager.getLayers(),
+ mLifecycleManager.getDestroyedLayers());
+ }
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = mHasDisplayChanges,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ snapshotBuilder.update(args);
+
+ mLifecycleManager.commitChanges();
+ }
+
+ LayerHierarchyBuilder mHierarchyBuilder{{}};
+
+ DisplayInfos mFrontEndDisplayInfos;
+ bool mHasDisplayChanges = false;
+
+ ShadowSettings globalShadowSettings;
+};
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
new file mode 100644
index 0000000..a462082
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -0,0 +1,873 @@
+/*
+ * 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 "LayerHistoryIntegrationTest"
+
+#include <Layer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <renderengine/mock/FakeExternalTexture.h>
+
+#include "FpsOps.h"
+#include "LayerHierarchyTest.h"
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockSchedulerCallback.h"
+
+namespace android::scheduler {
+
+using android::mock::createDisplayMode;
+
+class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
+protected:
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs;
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize;
+ static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
+
+ static constexpr Fps LO_FPS = 30_Hz;
+ static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
+
+ static constexpr Fps HI_FPS = 90_Hz;
+ static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
+
+ LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
+ mFlinger.resetScheduler(mScheduler);
+ mLifecycleManager = {};
+ mHierarchyBuilder = {{}};
+ }
+
+ void updateLayerSnapshotsAndLayerHistory(nsecs_t now) {
+ LayerSnapshotTestBase::update(mFlinger.mutableLayerSnapshotBuilder());
+ mFlinger.updateLayerHistory(now);
+ }
+
+ void setBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) {
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ layer->setDesiredPresentTime(time, false /*autotimestamp*/);
+ updateLayerSnapshotsAndLayerHistory(time);
+ }
+
+ LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+
+ LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
+ // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
+ // however, for testing only, a stable order is required, therefore we sort the list here.
+ // Any tests requiring ordered results must create layers with names.
+ auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
+ std::sort(summary.begin(), summary.end(),
+ [](const RefreshRateSelector::LayerRequirement& lhs,
+ const RefreshRateSelector::LayerRequirement& rhs) -> bool {
+ return lhs.name < rhs.name;
+ });
+ return summary;
+ }
+
+ size_t layerCount() const { return mScheduler->layerHistorySize(); }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
+ return history().mActiveLayerInfos.size();
+ }
+
+ auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now).isFrequent;
+ });
+ }
+
+ auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isAnimating(now);
+ });
+ }
+
+ auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now).clearHistory;
+ });
+ }
+
+ void setDefaultLayerVote(Layer* layer,
+ LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+ auto [found, layerPair] = history().findLayer(layer->getSequence());
+ if (found != LayerHistory::LayerStatus::NotFound) {
+ layerPair->second->setDefaultLayerVote(vote);
+ }
+ }
+
+ auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+ std::string layerName = "test layer:" + std::to_string(sequence);
+ const auto layer =
+ sp<Layer>::make(LayerCreationArgs{mFlinger.flinger(),
+ nullptr,
+ layerName,
+ 0,
+ {},
+ std::make_optional<uint32_t>(sequence)});
+ mFlinger.injectLegacyLayer(layer);
+ createRootLayer(sequence);
+ return layer;
+ }
+
+ auto destroyLayer(sp<Layer>& layer) {
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ mFlinger.releaseLegacyLayer(sequence);
+ layer.clear();
+ destroyLayerHandle(sequence);
+ }
+
+ void recordFramesAndExpect(sp<Layer>& layer, nsecs_t& time, Fps frameRate,
+ Fps desiredRefreshRate, int numFrames) {
+ LayerHistory::Summary summary;
+ for (int i = 0; i < numFrames; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += frameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
+ }
+
+ std::shared_ptr<RefreshRateSelector> mSelector =
+ std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+ LO_FPS),
+ createDisplayMode(DisplayModeId(1),
+ HI_FPS)),
+ DisplayModeId(0));
+
+ mock::SchedulerCallback mSchedulerCallback;
+
+ TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+
+ TestableSurfaceFlinger mFlinger;
+};
+
+namespace {
+
+TEST_F(LayerHistoryIntegrationTest, singleLayerNoVoteDefaultCompatibility) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ setDefaultFrameRateCompatibility(1, ANATIVEWINDOW_FRAME_RATE_NO_VOTE);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, singleLayerMinVoteDefaultCompatibility) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ setDefaultFrameRateCompatibility(1, ANATIVEWINDOW_FRAME_RATE_MIN);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+
+ hideLayer(1);
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) {
+ createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote) {
+ createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
+ createLegacyAndFrontedEndLayer(1);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, multipleLayers) {
+ auto layer1 = createLegacyAndFrontedEndLayer(1);
+ auto layer2 = createLegacyAndFrontedEndLayer(2);
+ auto layer3 = createLegacyAndFrontedEndLayer(3);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(3u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // layer1 is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer1, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer2 is frequent and has high refresh rate.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer2, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ // layer1 is still active but infrequent.
+ setBufferWithPresentTime(layer1, time);
+
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
+
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer1 is no longer active.
+ // layer2 is frequent and has low refresh rate.
+ for (size_t i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer2, time);
+ time += LO_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 has high refresh rate but not enough history.
+ constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ if (i % RATIO == 0) {
+ setBufferWithPresentTime(layer2, time);
+ }
+
+ setBufferWithPresentTime(layer3, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer3 becomes recently active.
+ setBufferWithPresentTime(layer3, time);
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer1 expires.
+ destroyLayer(layer1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+ EXPECT_EQ(2u, layerCount());
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 becomes inactive.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer2, time);
+ time += LO_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 expires.
+ destroyLayer(layer2);
+ updateLayerSnapshotsAndLayerHistory(time);
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer3 becomes active and has high refresh rate.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+ setBufferWithPresentTime(layer3, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer3 expires.
+ destroyLayer(layer3);
+ updateLayerSnapshotsAndLayerHistory(time);
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(0u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, inactiveLayers) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+
+ // the very first updates makes the layer frequent
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ setBufferWithPresentTime(layer, time);
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // advance the time for the previous frame to be inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+ // Now even if we post a quick few frame we should stay infrequent
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
+
+ // More quick frames will get us to frequent again
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(1, 60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRate(2, 90.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBufferWithPresentTime(explicitVisiblelayer, time);
+ setBufferWithPresentTime(explicitInvisiblelayer, time);
+
+ EXPECT_EQ(2u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, infrequentAnimatingLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // another update with the same cadence keep in infrequent
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->changes |=
+ frontend::RequestedLayerState::Changes::Animation;
+ mFlinger.updateLayerHistory(time);
+ // an update as animation will immediately vote for Max
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, frequentLayerBecomingInfrequentAndBack) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // Fill up the window with frequent updates
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += (60_Hz).getPeriodNsecs();
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // posting a buffer after long inactivity should retain the layer as active
+ time += std::chrono::nanoseconds(3s).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more infrequent buffer should make the layer infrequent
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ setBufferWithPresentTime(layer, time);
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting another buffer should keep the layer infrequent
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more buffers would mean starting of an animation, so making the layer frequent
+ setBufferWithPresentTime(layer, time);
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(1, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting a buffer after long inactivity should retain the layer as active
+ time += std::chrono::nanoseconds(3s).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting another buffer should keep the layer frequent
+ time += (60_Hz).getPeriodNsecs();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, inconclusiveLayerBecomingFrequent) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // Fill up the window with frequent updates
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += (60_Hz).getPeriodNsecs();
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // posting infrequent buffers after long inactivity should make the layer
+ // inconclusive but frequent.
+ time += std::chrono::nanoseconds(3s).count();
+ setBufferWithPresentTime(layer, time);
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more buffers should make the layer frequent and switch the refresh rate to max
+ // by clearing the history
+ setBufferWithPresentTime(layer, time);
+ setBufferWithPresentTime(layer, time);
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(1, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, getFramerate) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayer60Hz) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+ for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+ recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ }
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayer60_30Hz) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ auto props = layer->getLayerProps();
+ if (i % 3 == 0) {
+ props.isSmallDirty = false;
+ } else {
+ props.isSmallDirty = true;
+ }
+
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
+}
+
+TEST_F(LayerHistoryIntegrationTest, DISABLED_smallDirtyInMultiLayer) {
+ auto uiLayer = createLegacyAndFrontedEndLayer(1);
+ auto videoLayer = createLegacyAndFrontedEndLayer(2);
+ setFrameRate(2, 30.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(2u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // uiLayer is updating small dirty.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+ auto props = uiLayer->getLayerProps();
+ props.isSmallDirty = true;
+ setBuffer(1);
+ uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/);
+ updateLayerSnapshotsAndLayerHistory(time);
+ setBufferWithPresentTime(videoLayer, time);
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
+}
+
+class LayerHistoryIntegrationTestParameterized
+ : public LayerHistoryIntegrationTest,
+ public testing::WithParamInterface<std::chrono::nanoseconds> {};
+
+TEST_P(LayerHistoryIntegrationTestParameterized, HeuristicLayerWithInfrequentLayer) {
+ std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+ auto heuristicLayer = createLegacyAndFrontedEndLayer(1);
+ auto infrequentLayer = createLegacyAndFrontedEndLayer(2);
+
+ const nsecs_t startTime = systemTime();
+
+ const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+ setBufferWithPresentTime(heuristicLayer, startTime);
+ setBufferWithPresentTime(infrequentLayer, startTime);
+
+ nsecs_t time = startTime;
+ nsecs_t lastInfrequentUpdate = startTime;
+ const size_t totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+ size_t infrequentLayerUpdates = 0;
+ while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+ time += heuristicUpdateDelta.count();
+ setBufferWithPresentTime(heuristicLayer, time);
+
+ if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+ ALOGI("submitting infrequent frame [%zu/%zu]", infrequentLayerUpdates,
+ totalInfrequentLayerUpdates);
+ lastInfrequentUpdate = time;
+ setBufferWithPresentTime(infrequentLayer, time);
+ infrequentLayerUpdates++;
+ }
+
+ if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+ ASSERT_NE(0u, summarizeLayerHistory(time).size());
+ ASSERT_GE(2u, summarizeLayerHistory(time).size());
+
+ bool max = false;
+ bool min = false;
+ Fps heuristic;
+ for (const auto& layer : summarizeLayerHistory(time)) {
+ if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+ heuristic = layer.desiredRefreshRate;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+ max = true;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+ min = true;
+ }
+ }
+
+ if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+ EXPECT_EQ(24_Hz, heuristic);
+ EXPECT_FALSE(max);
+ if (summarizeLayerHistory(time).size() == 2) {
+ EXPECT_TRUE(min);
+ }
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
+ ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 7e3e61f..33c1d86 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -45,6 +45,8 @@
using android::mock::createDisplayMode;
+// WARNING: LEGACY TESTS FOR LEGACY FRONT END
+// Update LayerHistoryIntegrationTest instead
class LayerHistoryTest : public testing::Test {
protected:
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
@@ -152,7 +154,7 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(LayerInfo::FrameRateCompatibility::NoVote));
+ .WillOnce(Return(FrameRateCompatibility::NoVote));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -165,7 +167,10 @@
history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+ history().setDefaultFrameRateCompatibility(layer->getSequence(),
+
+ layer->getDefaultFrameRateCompatibility(),
+ true /* contentDetectionEnabled */);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(1, activeLayerCount());
@@ -176,7 +181,7 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(LayerInfo::FrameRateCompatibility::Min));
+ .WillOnce(Return(FrameRateCompatibility::Min));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -188,7 +193,9 @@
history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+ history().setDefaultFrameRateCompatibility(layer->getSequence(),
+ layer->getDefaultFrameRateCompatibility(),
+ true /* contentDetectionEnabled */);
auto summary = summarizeLayerHistory(time);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 1a9233d..50dfcaa 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -50,27 +50,12 @@
--gtest_filter="LayerSnapshotTest.*" --gtest_brief=1
*/
-class LayerSnapshotTest : public LayerHierarchyTestBase {
+class LayerSnapshotTest : public LayerSnapshotTestBase {
protected:
- LayerSnapshotTest() : LayerHierarchyTestBase() {
+ LayerSnapshotTest() : LayerSnapshotTestBase() {
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
}
- void createRootLayer(uint32_t id) override {
- LayerHierarchyTestBase::createRootLayer(id);
- setColor(id);
- }
-
- void createLayer(uint32_t id, uint32_t parentId) override {
- LayerHierarchyTestBase::createLayer(id, parentId);
- setColor(parentId);
- }
-
- void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
- LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
- setColor(id);
- }
-
void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
mHierarchyBuilder.update(mLifecycleManager.getLayers(),
@@ -111,11 +96,7 @@
LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) {
return mSnapshotBuilder.getSnapshot(path);
}
-
- LayerHierarchyBuilder mHierarchyBuilder{{}};
LayerSnapshotBuilder mSnapshotBuilder;
- DisplayInfos mFrontEndDisplayInfos;
- renderengine::ShadowSettings globalShadowSettings;
static const std::vector<uint32_t> STARTING_ZORDER;
};
const std::vector<uint32_t> LayerSnapshotTest::STARTING_ZORDER = {1, 11, 111, 12, 121,
@@ -315,14 +296,11 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
- EXPECT_EQ(getSnapshot(11)->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
EXPECT_EQ(getSnapshot(111)->frameRate.vote.rate.getIntValue(), 90);
- EXPECT_EQ(getSnapshot(111)->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
EXPECT_EQ(getSnapshot(1)->frameRate.vote.rate.getIntValue(), 0);
- EXPECT_EQ(getSnapshot(1)->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote);
}
TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
@@ -550,20 +528,20 @@
// verify parent is gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer and children get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
// reparent and verify the child gets the new parent's framerate
@@ -574,23 +552,23 @@
// verify parent is gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
// verify layer and children get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
// reparent and verify the new parent gets no vote
@@ -601,30 +579,30 @@
// verify old parent has invalid framerate (default)
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify new parent get no vote
EXPECT_FALSE(getSnapshot({.id = 2})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 2})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 2})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer and children get the requested votes (unchanged)
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
}
TEST_F(LayerSnapshotTest, translateDataspace) {
@@ -653,33 +631,33 @@
// verify parent 1 gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 11 and children 111 get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify parent 12 gets no vote
EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 122 and children 1221 get the requested votes
EXPECT_FALSE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(
@@ -688,7 +666,7 @@
EXPECT_FALSE(getSnapshot({.id = 1221})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 1221})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(
@@ -713,32 +691,32 @@
// verify parent is gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
// verify layer 11 and children 111 get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
// verify layer 122 and children 1221 get the requested category vote (unchanged from
// reparenting)
EXPECT_FALSE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_FALSE(getSnapshot({.id = 1221})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 1221})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
}
@@ -762,7 +740,7 @@
// verify parent 1 gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 12 and all descendants (121, 122, 1221) get the requested vote
@@ -875,4 +853,12 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
}
+
+TEST_F(LayerSnapshotTest, setShadowRadius) {
+ static constexpr float SHADOW_RADIUS = 123.f;
+ setShadowRadius(1, SHADOW_RADIUS);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->shadowSettings.length, SHADOW_RADIUS);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 3200003..87fae2c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -55,8 +55,7 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, /*callingUid*/ static_cast<uid_t>(0),
- ResyncCallback()) {}
+ : EventThreadConnection(eventThread, /*callingUid*/ static_cast<uid_t>(0)) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel));
@@ -193,7 +192,7 @@
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
@@ -219,7 +218,7 @@
kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
@@ -274,7 +273,7 @@
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(),
LayerHistory::LayerUpdateType::Buffer);
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -445,6 +444,63 @@
}
}
+TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) {
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
+ mScheduler->registerDisplay(kDisplayId2,
+ std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+ kDisplay2Mode60->getId()));
+
+ using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>;
+
+ struct Compositor final : ICompositor {
+ VsyncIds vsyncIds;
+ bool committed = true;
+
+ void configure() override {}
+
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets& targets) override {
+ vsyncIds.clear();
+
+ for (const auto& [id, target] : targets) {
+ vsyncIds.emplace_back(id, target->vsyncId());
+ }
+
+ return committed;
+ }
+
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ CompositeResultsPerDisplay results;
+
+ for (const auto& [id, _] : vsyncIds) {
+ results.try_emplace(id,
+ CompositeResult{.compositionCoverage =
+ CompositionCoverage::Hwc});
+ }
+
+ return results;
+ }
+
+ void sample() override {}
+ } compositor;
+
+ mScheduler->doFrameSignal(compositor, VsyncId(42));
+
+ const auto makeVsyncIds = [](VsyncId vsyncId) -> VsyncIds {
+ return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}};
+ };
+
+ EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds);
+
+ compositor.committed = false;
+ mScheduler->doFrameSignal(compositor, VsyncId(43));
+
+ // FrameTargets should be updated despite the skipped commit.
+ EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds);
+}
+
class AttachedChoreographerTest : public SchedulerTest {
protected:
void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 3a5fdf9..aeac80d 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -124,14 +124,12 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
index a2c54ac..db6df22 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -26,8 +26,6 @@
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;
@@ -66,17 +64,15 @@
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)>)};
+ std::vector<gui::HdrConversionStrategy> strategies = {
+ gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::passthrough>(),
+ gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>(),
+ gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::forceHdrConversion>(),
+ };
int32_t outPreferredHdrOutputType = 0;
- for (HdrConversionStrategy strategy : strategies) {
- binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+ for (const gui::HdrConversionStrategy& strategy : strategies) {
+ binder::Status status = sf->setHdrConversionStrategy(strategy, &outPreferredHdrOutputType);
if (hdrOutputConversionSupport) {
ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 151b178..014d07c 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -59,6 +59,12 @@
MOCK_METHOD(void, scheduleFrame, (), (override));
MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
+ void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) {
+ ftl::FakeGuard guard1(kMainThreadContext);
+ ftl::FakeGuard guard2(mDisplayLock);
+ Scheduler::onFrameSignal(compositor, vsyncId, TimePoint());
+ }
+
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 908c9ab..b54392e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -281,14 +281,12 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid,
- ResyncCallback())));
+ mock::EventThread::kCallingUid)));
auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
@@ -606,6 +604,13 @@
return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
}
+ void injectLegacyLayer(sp<Layer> layer) {
+ mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer;
+ };
+
+ void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+
+ auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -646,8 +651,8 @@
}
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
-
auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+ auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; };
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 7af1da6..a1eda94 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -30,13 +30,17 @@
#include <scheduler/TimeKeeper.h>
+#include "FlagUtils.h"
#include "Scheduler/VSyncDispatchTimerQueue.h"
#include "Scheduler/VSyncTracker.h"
+#include <com_android_graphics_surfaceflinger_flags.h>
+
using namespace testing;
using namespace std::literals;
namespace android::scheduler {
+using namespace com::android::graphics::surfaceflinger;
class MockVSyncTracker : public VSyncTracker {
public:
@@ -1061,6 +1065,52 @@
EXPECT_THAT(cb.mReadyTime[0], Eq(2000));
}
+// TODO(b/304338314): Set the flag value instead of skipping the test
+TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) {
+ // SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+ if (flags::dont_skip_on_early()) GTEST_SKIP();
+
+ EXPECT_CALL(mMockClock, alarmAt(_, 500));
+ CountingCallback cb(mDispatch);
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
+ mMockClock.advanceBy(300);
+
+ result = mDispatch->schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(1200, *result);
+
+ advanceToNextCallback();
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// TODO(b/304338314): Set the flag value instead of skipping the test
+TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) {
+ // SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+ if (!flags::dont_skip_on_early()) GTEST_SKIP();
+
+ EXPECT_CALL(mMockClock, alarmAt(_, 500));
+ CountingCallback cb(mDispatch);
+ auto result =
+ mDispatch->schedule(cb,
+ {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(500, *result);
+ mMockClock.advanceBy(300);
+
+ result = mDispatch->schedule(cb,
+ {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+ EXPECT_TRUE(result.has_value());
+ EXPECT_EQ(200, *result);
+
+ advanceToNextCallback();
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
diff --git a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
index c7b845e..cfb047c 100644
--- a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
@@ -245,4 +245,42 @@
EXPECT_EQ(callCount, 1);
}
+// Test that WindowInfosListenerInvoker#removeWindowInfosListener acks any unacked messages for
+// the removed listener.
+TEST_F(WindowInfosListenerInvokerTest, removeListenerAcks) {
+ // Don't ack in this listener to ensure there's an unacked message when the listener is later
+ // removed.
+ gui::WindowInfosListenerInfo listenerToBeRemovedInfo;
+ auto listenerToBeRemoved = sp<Listener>::make([](const gui::WindowInfosUpdate&) {});
+ mInvoker->addWindowInfosListener(listenerToBeRemoved, &listenerToBeRemovedInfo);
+
+ std::mutex mutex;
+ std::condition_variable cv;
+ int callCount = 0;
+ gui::WindowInfosListenerInfo listenerInfo;
+ mInvoker->addWindowInfosListener(sp<Listener>::make([&](const gui::WindowInfosUpdate& update) {
+ std::scoped_lock lock{mutex};
+ callCount++;
+ cv.notify_one();
+ listenerInfo.windowInfosPublisher
+ ->ackWindowInfosReceived(update.vsyncId,
+ listenerInfo.listenerId);
+ }),
+ &listenerInfo);
+
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
+ mInvoker->removeWindowInfosListener(listenerToBeRemoved);
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
+
+ // Verify that the second listener is called twice. If unacked messages aren't removed when the
+ // first listener is removed, this will fail.
+ {
+ std::unique_lock lock{mutex};
+ cv.wait(lock, [&]() { return callCount == 2; });
+ }
+ EXPECT_EQ(callCount, 2);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8e782eb..866af3b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -29,8 +29,16 @@
EventThread();
~EventThread() override;
- MOCK_METHOD(sp<EventThreadConnection>, createEventConnection,
- (ResyncCallback, EventRegistrationFlags), (const, override));
+ // TODO(b/302035909): workaround otherwise gtest complains about
+ // error: no viable conversion from
+ // 'tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration> &&>' to 'const
+ // tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration>>'
+ sp<EventThreadConnection> createEventConnection(EventRegistrationFlags flags) const override {
+ return createEventConnection(false, flags);
+ }
+ MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (bool, EventRegistrationFlags),
+ (const));
+
MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 50e07fc..4cc78fe 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -25,7 +25,7 @@
MockLayer(SurfaceFlinger* flinger, std::string name)
: Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
- .WillOnce(testing::Return(scheduler::LayerInfo::FrameRateCompatibility::Default));
+ .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
@@ -34,8 +34,7 @@
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD1(createClone, sp<Layer>(uint32_t));
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
- MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility,
- scheduler::LayerInfo::FrameRateCompatibility());
+ MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
};
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index e78f470..7c8e695 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -60,7 +60,12 @@
*
* This version of the extension cleans up a bug introduced in version 9
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
+ *
+ * This version of the extension deprecates the last of grallocusage
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -151,6 +156,8 @@
* pNext: NULL or a pointer to a structure extending this structure
* format: value specifying the format the image will be created with
* imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ *
+ * DEPRECATED in SPEC_VERSION 10
*/
typedef struct {
VkStructureType sType;
@@ -167,6 +174,8 @@
* format: value specifying the format the image will be created with
* imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
* swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ *
+ * DEPRECATED in SPEC_VERSION 11
*/
typedef struct {
VkStructureType sType;
@@ -198,7 +207,7 @@
const VkGrallocUsageInfoANDROID* grallocUsageInfo,
uint64_t* grallocUsage);
-/* ADDED in SPEC_VERSION 10 */
+/* DEPRECATED in SPEC_VERSION 11 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)(
VkDevice device,
const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
@@ -245,7 +254,7 @@
uint64_t* grallocUsage
);
-/* ADDED in SPEC_VERSION 10 */
+/* DEPRECATED in SPEC_VERSION 11 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID(
VkDevice device,
const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index bdba27e..5d7a4aa 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1456,6 +1456,7 @@
}
data->driver_device = dev;
+ data->driver_physical_device = physicalDevice;
*pDevice = dev;
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 4d2bbd6..4b855e5 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,6 +98,7 @@
VkDevice driver_device;
DeviceDriverTable driver;
+ VkPhysicalDevice driver_physical_device;
};
bool OpenHAL();
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index d059f8f..c073579 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -74,6 +74,7 @@
filename_(filename),
dlhandle_(nullptr),
native_bridge_(false),
+ opened_with_native_loader_(false),
refcount_(0) {}
LayerLibrary(LayerLibrary&& other) noexcept
@@ -81,6 +82,7 @@
filename_(std::move(other.filename_)),
dlhandle_(other.dlhandle_),
native_bridge_(other.native_bridge_),
+ opened_with_native_loader_(other.opened_with_native_loader_),
refcount_(other.refcount_) {
other.dlhandle_ = nullptr;
other.refcount_ = 0;
@@ -120,6 +122,7 @@
std::mutex mutex_;
void* dlhandle_;
bool native_bridge_;
+ bool opened_with_native_loader_;
size_t refcount_;
};
@@ -136,7 +139,7 @@
if (app_namespace &&
!android::base::StartsWith(path_, kSystemLayerLibraryDir)) {
char* error_msg = nullptr;
- dlhandle_ = OpenNativeLibraryInNamespace(
+ dlhandle_ = android::OpenNativeLibraryInNamespace(
app_namespace, path_.c_str(), &native_bridge_, &error_msg);
if (!dlhandle_) {
ALOGE("failed to load layer library '%s': %s", path_.c_str(), error_msg);
@@ -144,14 +147,16 @@
refcount_ = 0;
return false;
}
+ opened_with_native_loader_ = true;
} else {
- dlhandle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL);
+ dlhandle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL);
if (!dlhandle_) {
ALOGE("failed to load layer library '%s': %s", path_.c_str(),
dlerror());
refcount_ = 0;
return false;
}
+ opened_with_native_loader_ = false;
}
}
return true;
@@ -161,14 +166,25 @@
std::lock_guard<std::mutex> lock(mutex_);
if (--refcount_ == 0) {
ALOGV("closing layer library '%s'", path_.c_str());
- char* error_msg = nullptr;
- if (!android::CloseNativeLibrary(dlhandle_, native_bridge_, &error_msg)) {
- ALOGE("failed to unload library '%s': %s", path_.c_str(), error_msg);
- android::NativeLoaderFreeErrorMessage(error_msg);
- refcount_++;
+ // we close the .so same way as we opened. It's importain, because
+ // android::CloseNativeLibrary lives in libnativeloader.so, which is
+ // not accessible for early loaded services like SurfaceFlinger
+ if (opened_with_native_loader_) {
+ char* error_msg = nullptr;
+ if (!android::CloseNativeLibrary(dlhandle_, native_bridge_, &error_msg)) {
+ ALOGE("failed to unload library '%s': %s", path_.c_str(), error_msg);
+ android::NativeLoaderFreeErrorMessage(error_msg);
+ refcount_++;
+ return;
+ }
} else {
- dlhandle_ = nullptr;
+ if (dlclose(dlhandle_) != 0) {
+ ALOGE("failed to unload library '%s': %s", path_.c_str(), dlerror());
+ refcount_++;
+ return;
+ }
}
+ dlhandle_ = nullptr;
}
}
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index dcef54d..0c48611 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,8 +16,10 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <android/hardware/graphics/common/1.0/types.h>
+#include <android/hardware_buffer.h>
#include <grallocusage/GrallocUsageConversion.h>
#include <graphicsenv/GraphicsEnv.h>
#include <hardware/gralloc.h>
@@ -37,6 +39,7 @@
#include "driver.h"
using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+using DataSpace = aidl::android::hardware::graphics::common::Dataspace;
using android::hardware::graphics::common::V1_0::BufferUsage;
namespace vulkan {
@@ -532,61 +535,51 @@
return native_format;
}
-android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace,
- PixelFormat pixelFormat) {
+DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace,
+ PixelFormat pixelFormat) {
switch (colorspace) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
- return HAL_DATASPACE_V0_SRGB;
+ return DataSpace::SRGB;
case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
- return HAL_DATASPACE_DISPLAY_P3;
+ return DataSpace::DISPLAY_P3;
case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
- return HAL_DATASPACE_V0_SCRGB_LINEAR;
+ return DataSpace::SCRGB_LINEAR;
case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
- return HAL_DATASPACE_V0_SCRGB;
+ return DataSpace::SCRGB;
case VK_COLOR_SPACE_DCI_P3_LINEAR_EXT:
- return HAL_DATASPACE_DCI_P3_LINEAR;
+ return DataSpace::DCI_P3_LINEAR;
case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
- return HAL_DATASPACE_DCI_P3;
+ return DataSpace::DCI_P3;
case VK_COLOR_SPACE_BT709_LINEAR_EXT:
- return HAL_DATASPACE_V0_SRGB_LINEAR;
+ return DataSpace::SRGB_LINEAR;
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
- return HAL_DATASPACE_V0_SRGB;
+ return DataSpace::SRGB;
case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
if (pixelFormat == PixelFormat::RGBA_FP16) {
- return static_cast<android_dataspace>(
- HAL_DATASPACE_STANDARD_BT2020 |
- HAL_DATASPACE_TRANSFER_LINEAR |
- HAL_DATASPACE_RANGE_EXTENDED);
+ return DataSpace::BT2020_LINEAR_EXTENDED;
} else {
- return HAL_DATASPACE_BT2020_LINEAR;
+ return DataSpace::BT2020_LINEAR;
}
case VK_COLOR_SPACE_HDR10_ST2084_EXT:
- return static_cast<android_dataspace>(
- HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
- HAL_DATASPACE_RANGE_FULL);
+ return DataSpace::BT2020_PQ;
case VK_COLOR_SPACE_DOLBYVISION_EXT:
- return static_cast<android_dataspace>(
- HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
- HAL_DATASPACE_RANGE_FULL);
+ return DataSpace::BT2020_PQ;
case VK_COLOR_SPACE_HDR10_HLG_EXT:
- return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
+ return DataSpace::BT2020_HLG;
case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
- return static_cast<android_dataspace>(
- HAL_DATASPACE_STANDARD_ADOBE_RGB |
- HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL);
+ return DataSpace::ADOBE_RGB_LINEAR;
case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
- return HAL_DATASPACE_ADOBE_RGB;
-
+ return DataSpace::ADOBE_RGB;
// Pass through is intended to allow app to provide data that is passed
// to the display system without modification.
case VK_COLOR_SPACE_PASS_THROUGH_EXT:
- return HAL_DATASPACE_ARBITRARY;
+ return DataSpace::ARBITRARY;
default:
// This indicates that we don't know about the
// dataspace specified and we should indicate that
// it's unsupported
- return HAL_DATASPACE_UNKNOWN;
+ return DataSpace::UNKNOWN;
}
}
@@ -1367,6 +1360,187 @@
allocator->pfnFree(allocator->pUserData, swapchain);
}
+static VkResult getProducerUsage(const VkDevice& device,
+ const VkSwapchainCreateInfoKHR* create_info,
+ const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
+ bool create_protected_swapchain,
+ uint64_t* producer_usage) {
+ // Get the physical device to query the appropriate producer usage
+ const VkPhysicalDevice& pdev = GetData(device).driver_physical_device;
+ const InstanceData& instance_data = GetData(pdev);
+ const InstanceDriverTable& instance_dispatch = instance_data.driver;
+ if (!instance_dispatch.GetPhysicalDeviceImageFormatProperties2 &&
+ !instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
+ uint64_t native_usage = 0;
+ void* usage_info_pNext = nullptr;
+ VkResult result;
+ VkImageCompressionControlEXT image_compression = {};
+ const auto& dispatch = GetData(device).driver;
+ if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+ VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType =
+ VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+ gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+ VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ uint64_t consumer_usage, producer_usage;
+ ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
+ result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ swapchain_image_usage, &consumer_usage, &producer_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ native_usage =
+ convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
+ } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
+ int32_t legacy_usage = 0;
+ result = dispatch.GetSwapchainGrallocUsageANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ &legacy_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ native_usage = static_cast<uint64_t>(legacy_usage);
+ }
+ *producer_usage = native_usage;
+
+ return VK_SUCCESS;
+ }
+
+ // call GetPhysicalDeviceImageFormatProperties2KHR
+ VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
+ .pNext = nullptr,
+ .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+ };
+
+ // AHB does not have an sRGB format so we can't pass it to GPDIFP
+ // We need to convert the format to unorm if it is srgb
+ VkFormat format = create_info->imageFormat;
+ if (format == VK_FORMAT_R8G8B8A8_SRGB) {
+ format = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+
+ VkPhysicalDeviceImageFormatInfo2 image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+ .pNext = &external_image_format_info,
+ .format = format,
+ .type = VK_IMAGE_TYPE_2D,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = create_info->imageUsage,
+ .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+ };
+
+ VkAndroidHardwareBufferUsageANDROID ahb_usage;
+ ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
+ ahb_usage.pNext = nullptr;
+
+ VkImageFormatProperties2 image_format_properties;
+ image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+ image_format_properties.pNext = &ahb_usage;
+
+ if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) {
+ VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2(
+ pdev, &image_format_info, &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ }
+ else {
+ VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR(
+ pdev, &image_format_info,
+ &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d",
+ result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ }
+
+ *producer_usage = ahb_usage.androidHardwareBufferUsage;
+
+ return VK_SUCCESS;
+}
+
VKAPI_ATTR
VkResult CreateSwapchainKHR(VkDevice device,
const VkSwapchainCreateInfoKHR* create_info,
@@ -1393,9 +1567,9 @@
PixelFormat native_pixel_format =
GetNativePixelFormat(create_info->imageFormat);
- android_dataspace native_dataspace =
+ DataSpace native_dataspace =
GetNativeDataspace(create_info->imageColorSpace, native_pixel_format);
- if (native_dataspace == HAL_DATASPACE_UNKNOWN) {
+ if (native_dataspace == DataSpace::UNKNOWN) {
ALOGE(
"CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "
"failed: Unsupported color space",
@@ -1501,8 +1675,9 @@
}
/* Respect consumer default dataspace upon HAL_DATASPACE_ARBITRARY. */
- if (native_dataspace != HAL_DATASPACE_ARBITRARY) {
- err = native_window_set_buffers_data_space(window, native_dataspace);
+ if (native_dataspace != DataSpace::ARBITRARY) {
+ err = native_window_set_buffers_data_space(
+ window, static_cast<android_dataspace_t>(native_dataspace));
if (err != android::OK) {
ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
native_dataspace, strerror(-err), err);
@@ -1601,120 +1776,48 @@
num_images = 1;
}
+ // Look through the create_info pNext chain passed to createSwapchainKHR
+ // for an image compression control struct.
+ // if one is found AND the appropriate extensions are enabled, create a
+ // VkImageCompressionControlEXT structure to pass on to VkImageCreateInfo
+ // TODO check for imageCompressionControlSwapchain feature is enabled
void* usage_info_pNext = nullptr;
VkImageCompressionControlEXT image_compression = {};
- uint64_t native_usage = 0;
- if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
- VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
- gralloc_usage_info.sType =
- VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
- gralloc_usage_info.format = create_info->imageFormat;
- gralloc_usage_info.imageUsage = create_info->imageUsage;
- gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
- // Look through the pNext chain for an image compression control struct
- // if one is found AND the appropriate extensions are enabled,
- // append it to be the gralloc usage pNext chain
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
- create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(
- create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- usage_info_pNext = &image_compression;
- } break;
-
- default:
- // Ignore all other info structs
- break;
- }
+ default:
+ // Ignore all other info structs
+ break;
}
- gralloc_usage_info.pNext = usage_info_pNext;
-
- result = dispatch.GetSwapchainGrallocUsage4ANDROID(
- device, &gralloc_usage_info, &native_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
- VkGrallocUsageInfoANDROID gralloc_usage_info = {};
- gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
- gralloc_usage_info.format = create_info->imageFormat;
- gralloc_usage_info.imageUsage = create_info->imageUsage;
-
- // Look through the pNext chain for an image compression control struct
- // if one is found AND the appropriate extensions are enabled,
- // append it to be the gralloc usage pNext chain
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
- create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(
- create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- usage_info_pNext = &image_compression;
- } break;
-
- default:
- // Ignore all other info structs
- break;
- }
- }
- gralloc_usage_info.pNext = usage_info_pNext;
-
- result = dispatch.GetSwapchainGrallocUsage3ANDROID(
- device, &gralloc_usage_info, &native_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
- uint64_t consumer_usage, producer_usage;
- ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
- result = dispatch.GetSwapchainGrallocUsage2ANDROID(
- device, create_info->imageFormat, create_info->imageUsage,
- swapchain_image_usage, &consumer_usage, &producer_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- native_usage =
- convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
- } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
- int32_t legacy_usage = 0;
- result = dispatch.GetSwapchainGrallocUsageANDROID(
- device, create_info->imageFormat, create_info->imageUsage,
- &legacy_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- native_usage = static_cast<uint64_t>(legacy_usage);
}
- native_usage |= surface.consumer_usage;
- bool createProtectedSwapchain = false;
+ // Get the appropriate native_usage for the images
+ // Get the consumer usage
+ uint64_t native_usage = surface.consumer_usage;
+ // Determine if the swapchain is protected
+ bool create_protected_swapchain = false;
if (create_info->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) {
- createProtectedSwapchain = true;
+ create_protected_swapchain = true;
native_usage |= BufferUsage::PROTECTED;
}
+ // Get the producer usage
+ uint64_t producer_usage;
+ result = getProducerUsage(device, create_info, swapchain_image_usage, create_protected_swapchain, &producer_usage);
+ if (result != VK_SUCCESS) {
+ return result;
+ }
+ native_usage |= producer_usage;
+
err = native_window_set_usage(window, native_usage);
if (err != android::OK) {
ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
@@ -1742,8 +1845,10 @@
void* mem = allocator->pfnAllocation(allocator->pUserData,
sizeof(Swapchain), alignof(Swapchain),
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
if (!mem)
return VK_ERROR_OUT_OF_HOST_MEMORY;
+
Swapchain* swapchain = new (mem)
Swapchain(surface, num_images, create_info->presentMode,
TranslateVulkanToNativeTransform(create_info->preTransform),
@@ -1767,7 +1872,7 @@
VkImageCreateInfo image_create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
- .flags = createProtectedSwapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+ .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
.imageType = VK_IMAGE_TYPE_2D,
.format = create_info->imageFormat,
.extent = {