Merge "OpenGL: Update test mapping to include EGL_test"
diff --git a/Android.bp b/Android.bp
index 615a7a8..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
cc_library_headers {
name: "libandroid_sensor_headers",
- vendor: true,
+ vendor_available: true,
+ host_supported: true,
export_include_dirs: ["include_sensor"],
}
diff --git a/cmds/atrace/README.md b/cmds/atrace/README.md
new file mode 100644
index 0000000..faa43b2
--- /dev/null
+++ b/cmds/atrace/README.md
@@ -0,0 +1,48 @@
+# Atrace categories
+
+The atrace command (and the perfetto configuration) allow listing **categories**
+to select subsets of events to be traced.
+
+Each category can include some userspace events and some ftrace events.
+
+## Vendor categories
+
+It's possible to extend exiting categories (or to define new categories) from
+the /vendor partition in order to add hardware specific ftrace events.
+
+Since android 14, if the file `/vendor/etc/atrace/atrace_categories.txt`, atrace
+and perfetto will consider the categories and ftrace events listed there.
+
+The file contains a list of categories, and for each category (on the following
+lines, indented with one or more spaces of time), a list of ftrace events that
+should be enabled when the category is enabled.
+
+Each ftrace event should be a subdirectory in `/sys/kernel/tracing/events/` and
+should be of the form `group/event`. Listing a whole group is not supported,
+each event needs to be listed explicitly.
+
+It is not an error if an ftrace event is listed in the file, but not present on
+the tracing file system.
+
+Example:
+
+```
+gfx
+ mali/gpu_power_state
+ mali/mali_pm_status
+thermal_tj
+ thermal_exynos/thermal_cpu_pressure
+ thermal_exynos/thermal_exynos_arm_update
+```
+
+The file lists two categories (`gfx` and `thermal_tj`). When the `gfx` category
+is enabled, atrace (or perfetto) will enable
+`/sys/kernel/tracing/events/mali/gpu_power_state` and
+`/sys/kernel/tracing/events/mali/mali_pm_status`. When the `thermal_tj` category
+is enabled, atrace (or perfetto) will enable
+`/sys/kernel/tracing/events/thermal_exynos/thermal_cpu_pressure` and
+`/sys/kernel/tracing/events/thermal_exynos/thermal_exynos_arm_update`.
+
+Since android 14, if the file `/vendor/etc/atrace/atrace_categories.txt` exists
+on the file system, perfetto and atrace do not query the android.hardware.atrace
+HAL (which is deprecated).
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 48d48ac..8105626 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -49,6 +49,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <android-base/stringprintf.h>
using namespace android;
@@ -62,7 +63,7 @@
using std::string;
-#define MAX_SYS_FILES 12
+#define MAX_SYS_FILES 13
const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
@@ -73,7 +74,9 @@
const char* k_pdxServiceCategory = "pdx";
const char* k_coreServicesProp = "ro.atrace.core.services";
-typedef enum { OPT, REQ } requiredness ;
+const char* kVendorCategoriesPath = "/vendor/etc/atrace/atrace_categories.txt";
+
+typedef enum { OPT, REQ } requiredness;
struct TracingCategory {
// The name identifying the category.
@@ -189,6 +192,8 @@
{ OPT, "events/f2fs/f2fs_sync_file_exit/enable" },
{ OPT, "events/f2fs/f2fs_write_begin/enable" },
{ OPT, "events/f2fs/f2fs_write_end/enable" },
+ { OPT, "events/f2fs/f2fs_iostat/enable" },
+ { OPT, "events/f2fs/f2fs_iostat_latency/enable" },
{ OPT, "events/ext4/ext4_da_write_begin/enable" },
{ OPT, "events/ext4/ext4_da_write_end/enable" },
{ OPT, "events/ext4/ext4_sync_file_enter/enable" },
@@ -253,7 +258,20 @@
} },
};
-struct TracingVendorCategory {
+// A category in the vendor categories file.
+struct TracingVendorFileCategory {
+ // The name identifying the category.
+ std::string name;
+
+ // If the category is enabled through command.
+ bool enabled = false;
+
+ // Paths to the ftrace enable files (relative to g_traceFolder).
+ std::vector<std::string> ftrace_enable_paths;
+};
+
+// A category in the vendor HIDL HAL.
+struct TracingVendorHalCategory {
// The name identifying the category.
std::string name;
@@ -263,11 +281,8 @@
// If the category is enabled through command.
bool enabled;
- TracingVendorCategory(string &&name, string &&description, bool enabled)
- : name(std::move(name))
- , description(std::move(description))
- , enabled(enabled)
- {}
+ TracingVendorHalCategory(string&& name, string&& description, bool enabled)
+ : name(std::move(name)), description(std::move(description)), enabled(enabled) {}
};
/* Command line options */
@@ -287,8 +302,9 @@
static bool g_traceAborted = false;
static bool g_categoryEnables[arraysize(k_categories)] = {};
static std::string g_traceFolder;
+static std::vector<TracingVendorFileCategory> g_vendorFileCategories;
static sp<IAtraceDevice> g_atraceHal;
-static std::vector<TracingVendorCategory> g_vendorCategories;
+static std::vector<TracingVendorHalCategory> g_vendorHalCategories;
/* Sys file paths */
static const char* k_traceClockPath =
@@ -645,6 +661,13 @@
}
}
}
+ for (const TracingVendorFileCategory& c : g_vendorFileCategories) {
+ for (const std::string& path : c.ftrace_enable_paths) {
+ if (fileIsWritable(path.c_str())) {
+ ok &= setKernelOptionEnable(path.c_str(), false);
+ }
+ }
+ }
return ok;
}
@@ -724,7 +747,13 @@
static bool setCategoryEnable(const char* name)
{
bool vendor_found = false;
- for (auto &c : g_vendorCategories) {
+ for (auto& c : g_vendorFileCategories) {
+ if (strcmp(name, c.name.c_str()) == 0) {
+ c.enabled = true;
+ vendor_found = true;
+ }
+ }
+ for (auto& c : g_vendorHalCategories) {
if (strcmp(name, c.name.c_str()) == 0) {
c.enabled = true;
vendor_found = true;
@@ -870,6 +899,16 @@
}
}
+ for (const TracingVendorFileCategory& c : g_vendorFileCategories) {
+ if (c.enabled) {
+ for (const std::string& path : c.ftrace_enable_paths) {
+ if (fileIsWritable(path.c_str())) {
+ ok &= setKernelOptionEnable(path.c_str(), true);
+ }
+ }
+ }
+ }
+
return ok;
}
@@ -1055,7 +1094,10 @@
printf(" %10s - %s\n", c.name, c.longname);
}
}
- for (const auto &c : g_vendorCategories) {
+ for (const auto& c : g_vendorFileCategories) {
+ printf(" %10s - (VENDOR)\n", c.name.c_str());
+ }
+ for (const auto& c : g_vendorHalCategories) {
printf(" %10s - %s (HAL)\n", c.name.c_str(), c.description.c_str());
}
}
@@ -1114,8 +1156,38 @@
return true;
}
-void initVendorCategories()
-{
+void initVendorCategoriesFromFile() {
+ std::ifstream is(kVendorCategoriesPath);
+ for (std::string line; std::getline(is, line);) {
+ if (line.empty()) {
+ continue;
+ }
+ if (android::base::StartsWith(line, ' ') || android::base::StartsWith(line, '\t')) {
+ if (g_vendorFileCategories.empty()) {
+ fprintf(stderr, "Malformed vendor categories file\n");
+ exit(1);
+ return;
+ }
+ std::string_view path = std::string_view(line).substr(1);
+ while (android::base::StartsWith(path, ' ') || android::base::StartsWith(path, '\t')) {
+ path.remove_prefix(1);
+ }
+ if (path.empty()) {
+ continue;
+ }
+ std::string enable_path = "events/";
+ enable_path += path;
+ enable_path += "/enable";
+ g_vendorFileCategories.back().ftrace_enable_paths.push_back(std::move(enable_path));
+ } else {
+ TracingVendorFileCategory cat;
+ cat.name = line;
+ g_vendorFileCategories.push_back(std::move(cat));
+ }
+ }
+}
+
+void initVendorCategoriesFromHal() {
g_atraceHal = IAtraceDevice::getService();
if (g_atraceHal == nullptr) {
@@ -1123,27 +1195,34 @@
return;
}
- Return<void> ret = g_atraceHal->listCategories(
- [](const auto& list) {
- g_vendorCategories.reserve(list.size());
- for (const auto& category : list) {
- g_vendorCategories.emplace_back(category.name, category.description, false);
- }
- });
+ Return<void> ret = g_atraceHal->listCategories([](const auto& list) {
+ g_vendorHalCategories.reserve(list.size());
+ for (const auto& category : list) {
+ g_vendorHalCategories.emplace_back(category.name, category.description, false);
+ }
+ });
if (!ret.isOk()) {
fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str());
}
}
-static bool setUpVendorTracing()
-{
+void initVendorCategories() {
+ // If kVendorCategoriesPath exists on the filesystem, do not use the HAL.
+ if (access(kVendorCategoriesPath, F_OK) != -1) {
+ initVendorCategoriesFromFile();
+ } else {
+ initVendorCategoriesFromHal();
+ }
+}
+
+static bool setUpVendorTracingWithHal() {
if (g_atraceHal == nullptr) {
// No atrace HAL
return true;
}
std::vector<hidl_string> categories;
- for (const auto &c : g_vendorCategories) {
+ for (const auto& c : g_vendorHalCategories) {
if (c.enabled) {
categories.emplace_back(c.name);
}
@@ -1164,15 +1243,14 @@
return true;
}
-static bool cleanUpVendorTracing()
-{
+static bool cleanUpVendorTracingWithHal() {
if (g_atraceHal == nullptr) {
// No atrace HAL
return true;
}
- if (!g_vendorCategories.size()) {
- // No vendor categories
+ if (!g_vendorHalCategories.size()) {
+ // No vendor HAL categories
return true;
}
@@ -1326,7 +1404,7 @@
if (ok && traceStart && !onlyUserspace) {
ok &= setUpKernelTracing();
- ok &= setUpVendorTracing();
+ ok &= setUpVendorTracingWithHal();
ok &= startTrace();
}
@@ -1397,7 +1475,7 @@
if (traceStop) {
cleanUpUserspaceTracing();
if (!onlyUserspace) {
- cleanUpVendorTracing();
+ cleanUpVendorTracingWithHal();
cleanUpKernelTracing();
}
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 33dceff..69a1df2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -194,6 +194,8 @@
static const std::string TOMBSTONE_FILE_PREFIX = "tombstone_";
static const std::string ANR_DIR = "/data/anr/";
static const std::string ANR_FILE_PREFIX = "anr_";
+static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/";
+static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-";
// TODO: temporary variables and functions used during C++ refactoring
@@ -1110,6 +1112,16 @@
RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
}
+static void DumpShutdownCheckpoints() {
+ const bool shutdown_checkpoints_dumped = AddDumps(
+ ds.shutdown_checkpoints_.begin(), ds.shutdown_checkpoints_.end(),
+ "SHUTDOWN CHECKPOINTS", false /* add_to_zip */);
+ if (!shutdown_checkpoints_dumped) {
+ printf("*** NO SHUTDOWN CHECKPOINTS to dump in %s\n\n",
+ SHUTDOWN_CHECKPOINTS_DIR.c_str());
+ }
+}
+
static void DumpDynamicPartitionInfo() {
if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
return;
@@ -1704,6 +1716,8 @@
DoKmsg();
+ DumpShutdownCheckpoints();
+
DumpIpAddrAndRules();
dump_route_tables();
@@ -1861,6 +1875,8 @@
if (!PropertiesHelper::IsDryRun()) {
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX);
ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX);
+ ds.shutdown_checkpoints_ = GetDumpFds(
+ SHUTDOWN_CHECKPOINTS_DIR, SHUTDOWN_CHECKPOINTS_FILE_PREFIX);
}
ds.AddDir(RECOVERY_DIR, true);
@@ -2915,6 +2931,7 @@
}
tombstone_data_.clear();
anr_data_.clear();
+ shutdown_checkpoints_.clear();
// Instead of shutdown the pool, we delete temporary files directly since
// shutdown blocking the call.
@@ -3202,6 +3219,7 @@
tombstone_data_.clear();
anr_data_.clear();
+ shutdown_checkpoints_.clear();
return (consent_callback_ != nullptr &&
consent_callback_->getResult() == UserConsentResult::UNAVAILABLE)
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 5f3acfd..9f894b5 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -514,6 +514,9 @@
// List of open ANR dump files.
std::vector<DumpData> anr_data_;
+ // List of open shutdown checkpoint files.
+ std::vector<DumpData> shutdown_checkpoints_;
+
// A thread pool to execute dump tasks simultaneously if the parallel run is enabled.
std::unique_ptr<android::os::dumpstate::DumpPool> dump_pool_;
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b1283eb..bb6639e 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -501,10 +501,6 @@
}
static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
- if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
- return true;
- }
-
int32_t uid = multiuser_get_uid(userId, appId);
int shared_app_gid = multiuser_get_shared_gid(userId, appId);
if (shared_app_gid == -1) {
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 61f931e..1386660 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -48,17 +48,6 @@
}
cc_binary {
- name: "servicemanager.microdroid",
- defaults: ["servicemanager_defaults"],
- init_rc: ["servicemanager.microdroid.rc"],
- srcs: ["main.cpp"],
- bootstrap: true,
- // Prevent this from being installed when running tests in this directory.
- // This is okay because microdorid build rule can bundle this anyway.
- installable: false,
-}
-
-cc_binary {
name: "servicemanager.recovery",
stem: "servicemanager",
recovery: true,
diff --git a/cmds/servicemanager/servicemanager.microdroid.rc b/cmds/servicemanager/servicemanager.microdroid.rc
deleted file mode 100644
index 8819e1e..0000000
--- a/cmds/servicemanager/servicemanager.microdroid.rc
+++ /dev/null
@@ -1,8 +0,0 @@
-service servicemanager /system/bin/servicemanager.microdroid
- class core
- user system
- group system readproc
- critical
- onrestart setprop servicemanager.ready false
- onrestart restart apexd
- shutdown critical
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index e5b5db2..d4ba321 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -829,6 +829,8 @@
AKEYCODE_STYLUS_BUTTON_TERTIARY = 310,
/** A button on the tail end of a stylus. */
AKEYCODE_STYLUS_BUTTON_TAIL = 311,
+ /** Key to open recent apps (a.k.a. Overview) */
+ AKEYCODE_RECENT_APPS = 312,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index eed6b33..4a5bd5e 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -88,36 +88,6 @@
typedef struct APerformanceHintSession APerformanceHintSession;
/**
- * Hints for the session used by {@link APerformanceHint_sendHint} to signal upcoming changes
- * in the mode or workload.
- */
-enum SessionHint {
- /**
- * This hint indicates a sudden increase in CPU workload intensity. It means
- * that this hint session needs extra CPU resources immediately to meet the
- * target duration for the current work cycle.
- */
- CPU_LOAD_UP = 0,
- /**
- * This hint indicates a decrease in CPU workload intensity. It means that
- * this hint session can reduce CPU resources and still meet the target duration.
- */
- CPU_LOAD_DOWN = 1,
- /*
- * This hint indicates an upcoming CPU workload that is completely changed and
- * unknown. It means that the hint session should reset CPU resources to a known
- * baseline to prepare for an arbitrary load, and must wake up if inactive.
- */
- CPU_LOAD_RESET = 2,
- /*
- * This hint indicates that the most recent CPU workload is resuming after a
- * period of inactivity. It means that the hint session should allocate similar
- * CPU resources to what was used previously, and must wake up if inactive.
- */
- CPU_LOAD_RESUME = 3,
-};
-
-/**
* Acquire an instance of the performance hint manager.
*
* @return manager instance on success, nullptr on failure.
@@ -190,15 +160,21 @@
APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__);
/**
- * Sends performance hints to inform the hint session of changes in the workload.
+ * Set a list of threads to the performance hint session. This operation will replace
+ * the current list of threads with the given list of threads.
*
- * @param session The performance hint session instance to update.
- * @param hint The hint to send to the session.
- * @return 0 on success
+ * @param session The performance hint session instance for the threads.
+ * @param threadIds The list of threads to be associated with this session. They must be part of
+ * this app's thread group.
+ * @param size the size of the list of threadIds.
+ * @return 0 on success.
+ * EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group.
* EPIPE if communication with the system service has failed.
*/
-int APerformanceHint_sendHint(
- APerformanceHintSession* session, int hint) __INTRODUCED_IN(__ANDROID_API_U__);
+int APerformanceHint_setThreads(
+ APerformanceHintSession* session,
+ const int32_t* threadIds,
+ size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
__END_DECLS
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 105f952..085fc27 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -601,12 +601,15 @@
float accuracy;
} AHeadingEvent;
+// LINT.IfChange
/**
* Information that describes a sensor event, refer to
* <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional
* documentation.
+ *
+ * NOTE: changes to this struct has to be backward compatible and reflected in
+ * sensors_event_t
*/
-/* NOTE: changes to this struct has to be backward compatible */
typedef struct ASensorEvent {
int32_t version; /* sizeof(struct ASensorEvent) */
int32_t sensor; /** The sensor that generates this event */
@@ -651,6 +654,7 @@
uint32_t flags;
int32_t reserved1[3];
} ASensorEvent;
+// LINT.ThenChange (hardware/libhardware/include/hardware/sensors.h)
struct ASensorManager;
/**
diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/include/ftl/details/mixins.h
similarity index 62%
copy from libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
copy to include/ftl/details/mixins.h
index a8bc994..9ab9e08 100644
--- a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
+++ b/include/ftl/details/mixins.h
@@ -14,10 +14,17 @@
* limitations under the License.
*/
-package android.gui;
+#pragma once
-/** @hide */
-parcelable SupportedBufferCombinations {
- int[] pixelFormats;
- int[] dataspaces;
-}
+namespace android::ftl::details {
+
+template <typename Self, template <typename> class>
+class Mixin {
+ protected:
+ constexpr Self& self() { return *static_cast<Self*>(this); }
+ constexpr const Self& self() const { return *static_cast<const Self*>(this); }
+
+ constexpr auto& mut() { return self().value_; }
+};
+
+} // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
index 82af1d6..075d12b 100644
--- a/include/ftl/enum.h
+++ b/include/ftl/enum.h
@@ -92,7 +92,7 @@
// enum class E { A, B, C };
// static_assert(ftl::to_underlying(E::B) == 1);
//
-template <typename E>
+template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
constexpr auto to_underlying(E v) {
return static_cast<std::underlying_type_t<E>>(v);
}
diff --git a/include/ftl/mixins.h b/include/ftl/mixins.h
new file mode 100644
index 0000000..0e1d200
--- /dev/null
+++ b/include/ftl/mixins.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/details/mixins.h>
+
+namespace android::ftl {
+
+// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common
+// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must
+// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic,
+// etc.) are enabled through inheritance:
+//
+// struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
+// using Constructible::Constructible;
+// };
+//
+// static_assert(!std::is_default_constructible_v<Id>);
+//
+// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is
+// zero-initialized unless specified:
+//
+// struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
+// ftl::Equatable<Color>,
+// ftl::Orderable<Color> {
+// using DefaultConstructible::DefaultConstructible;
+// };
+//
+// static_assert(Color() == Color(0u));
+// static_assert(ftl::to_underlying(Color(-1)) == 255u);
+// static_assert(Color(1u) < Color(2u));
+//
+// struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
+// ftl::Equatable<Sequence>,
+// ftl::Orderable<Sequence>,
+// ftl::Incrementable<Sequence> {
+// using DefaultConstructible::DefaultConstructible;
+// };
+//
+// static_assert(Sequence() == Sequence(-1));
+//
+// The underlying type need not be a fundamental type:
+//
+// struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
+// ftl::Equatable<Timeout>,
+// ftl::Addable<Timeout> {
+// using DefaultConstructible::DefaultConstructible;
+// };
+//
+// using namespace std::chrono_literals;
+// static_assert(Timeout() + Timeout(5s) == Timeout(15s));
+//
+template <typename Self, typename T>
+struct Constructible {
+ explicit constexpr Constructible(T value) : value_(value) {}
+
+ explicit constexpr operator const T&() const { return value_; }
+
+ private:
+ template <typename, template <typename> class>
+ friend class details::Mixin;
+
+ T value_;
+};
+
+template <typename Self, typename T, auto kDefault = T{}>
+struct DefaultConstructible : Constructible<Self, T> {
+ using Constructible<Self, T>::Constructible;
+ constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {}
+};
+
+// Shorthand for casting a type-safe wrapper to its underlying value.
+template <typename Self, typename T>
+constexpr const T& to_underlying(const Constructible<Self, T>& c) {
+ return static_cast<const T&>(c);
+}
+
+// Comparison operators for equality.
+template <typename Self>
+struct Equatable : details::Mixin<Self, Equatable> {
+ constexpr bool operator==(const Self& other) const {
+ return to_underlying(this->self()) == to_underlying(other);
+ }
+
+ constexpr bool operator!=(const Self& other) const { return !(*this == other); }
+};
+
+// Comparison operators for ordering.
+template <typename Self>
+struct Orderable : details::Mixin<Self, Orderable> {
+ constexpr bool operator<(const Self& other) const {
+ return to_underlying(this->self()) < to_underlying(other);
+ }
+
+ constexpr bool operator>(const Self& other) const { return other < this->self(); }
+ constexpr bool operator>=(const Self& other) const { return !(*this < other); }
+ constexpr bool operator<=(const Self& other) const { return !(*this > other); }
+};
+
+// Pre-increment and post-increment operators.
+template <typename Self>
+struct Incrementable : details::Mixin<Self, Incrementable> {
+ constexpr Self& operator++() {
+ ++this->mut();
+ return this->self();
+ }
+
+ constexpr Self operator++(int) {
+ const Self tmp = this->self();
+ operator++();
+ return tmp;
+ }
+};
+
+// Additive operators, including incrementing.
+template <typename Self>
+struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> {
+ constexpr Self& operator+=(const Self& other) {
+ this->mut() += to_underlying(other);
+ return this->self();
+ }
+
+ constexpr Self operator+(const Self& other) const {
+ Self tmp = this->self();
+ return tmp += other;
+ }
+
+ private:
+ using Base = details::Mixin<Self, Addable>;
+ using Base::mut;
+ using Base::self;
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index 626507f..a818128 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -95,8 +95,26 @@
if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
return R();
}
+
+ // Delete new for this class. Its base doesn't have a virtual destructor, and
+ // if it got deleted via base class pointer, it would cause undefined
+ // behavior. There's not a good reason to allocate this object on the heap
+ // anyway.
+ static void* operator new(size_t) = delete;
+ static void* operator new[](size_t) = delete;
+
};
+template <typename T, typename U>
+constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
+ return static_cast<std::optional<T>>(lhs) == static_cast<std::optional<U>>(rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
+ return !(lhs == rhs);
+}
+
// Deduction guides.
template <typename T>
Optional(T) -> Optional<T>;
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 98a18c9..7457496 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -21,6 +21,7 @@
#include <ftl/string.h>
#include <gui/constants.h>
#include <input/Input.h>
+#include <ui/Rotation.h>
#include <cinttypes>
#include <optional>
@@ -29,13 +30,6 @@
namespace android {
-enum {
- DISPLAY_ORIENTATION_0 = 0,
- DISPLAY_ORIENTATION_90 = 1,
- DISPLAY_ORIENTATION_180 = 2,
- DISPLAY_ORIENTATION_270 = 3
-};
-
/**
* Describes the different type of viewports supported by input flinger.
* Keep in sync with values in InputManagerService.java.
@@ -54,7 +48,7 @@
*/
struct DisplayViewport {
int32_t displayId; // -1 if invalid
- int32_t orientation;
+ ui::Rotation orientation;
int32_t logicalLeft;
int32_t logicalTop;
int32_t logicalRight;
@@ -74,7 +68,7 @@
DisplayViewport()
: displayId(ADISPLAY_ID_NONE),
- orientation(DISPLAY_ORIENTATION_0),
+ orientation(ui::ROTATION_0),
logicalLeft(0),
logicalTop(0),
logicalRight(0),
@@ -111,7 +105,7 @@
void setNonDisplayViewport(int32_t width, int32_t height) {
displayId = ADISPLAY_ID_NONE;
- orientation = DISPLAY_ORIENTATION_0;
+ orientation = ui::ROTATION_0;
logicalLeft = 0;
logicalTop = 0;
logicalRight = width;
diff --git a/include/input/Input.h b/include/input/Input.h
index d298d81..015efdd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -577,7 +577,7 @@
inline const ui::Transform& getTransform() const { return mTransform; }
- int getSurfaceRotation() const;
+ std::optional<ui::Rotation> getSurfaceRotation() const;
inline float getXPrecision() const { return mXPrecision; }
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index e911734..5fa9fda 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -205,6 +205,16 @@
int32_t id;
};
+struct KeyboardLayoutInfo {
+ explicit KeyboardLayoutInfo(std::string languageTag, std::string layoutType)
+ : languageTag(languageTag), layoutType(layoutType) {}
+
+ // A BCP 47 conformant language tag such as "en-US".
+ std::string languageTag;
+ // The layout type such as QWERTY or AZERTY.
+ std::string layoutType;
+};
+
/*
* Describes the characteristics and capabilities of an input device.
*/
@@ -256,6 +266,11 @@
void setKeyboardType(int32_t keyboardType);
inline int32_t getKeyboardType() const { return mKeyboardType; }
+ void setKeyboardLayoutInfo(KeyboardLayoutInfo keyboardLayoutInfo);
+ inline const std::optional<KeyboardLayoutInfo>& getKeyboardLayoutInfo() const {
+ return mKeyboardLayoutInfo;
+ }
+
inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
mKeyCharacterMap = value;
}
@@ -296,6 +311,7 @@
bool mIsExternal;
bool mHasMic;
hardware::input::InputDeviceCountryCode mCountryCode;
+ std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
uint32_t mSources;
int32_t mKeyboardType;
std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dc928b8..867a089 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -125,14 +125,21 @@
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
Vector<KeyEvent>& outEvents) const;
+ /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
+ * and usageCodes are mapped to corresponding Android Keycode */
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
/* Maps a scan code and usage code to a key code, in case this key map overrides
* the mapping in some way. */
status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
- /* Tries to find a replacement key code for a given key code and meta state
- * in character map. */
- void tryRemapKey(int32_t scanCode, int32_t metaState,
- int32_t* outKeyCode, int32_t* outMetaState) const;
+ /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
+ int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+
+ /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
+ * that tries to find a replacement key code based on current meta state */
+ std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
+ int32_t metaState) const;
#ifdef __linux__
/* Reads a key map from a parcel. */
@@ -227,8 +234,9 @@
std::string mLoadFileName;
bool mLayoutOverlayApplied;
- KeyedVector<int32_t, int32_t> mKeysByScanCode;
- KeyedVector<int32_t, int32_t> mKeysByUsageCode;
+ std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
+ std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
+ std::map<int32_t /* fromHidUsageCode */, int32_t /* toAndroidKeyCode */> mKeysByUsageCode;
KeyCharacterMap(const std::string& filename);
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index a616a95..1e4f6e7 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ui/Rotation.h>
+
#include <stdint.h>
#include <sys/time.h>
#include <vector>
@@ -58,7 +60,7 @@
* Rotate the video frame.
* The rotation value is an enum from ui/Rotation.h
*/
- void rotate(int32_t orientation);
+ void rotate(ui::Rotation orientation);
private:
uint32_t mHeight;
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index f27f5f1..eaf3b5e 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -24,6 +24,51 @@
*/
void APerformanceHint_setIHintManagerForTesting(void* iManager);
+/**
+ * Hints for the session used to signal upcoming changes in the mode or workload.
+ */
+enum SessionHint {
+ /**
+ * This hint indicates a sudden increase in CPU workload intensity. It means
+ * that this hint session needs extra CPU resources immediately to meet the
+ * target duration for the current work cycle.
+ */
+ CPU_LOAD_UP = 0,
+ /**
+ * This hint indicates a decrease in CPU workload intensity. It means that
+ * this hint session can reduce CPU resources and still meet the target duration.
+ */
+ CPU_LOAD_DOWN = 1,
+ /*
+ * This hint indicates an upcoming CPU workload that is completely changed and
+ * unknown. It means that the hint session should reset CPU resources to a known
+ * baseline to prepare for an arbitrary load, and must wake up if inactive.
+ */
+ CPU_LOAD_RESET = 2,
+ /*
+ * This hint indicates that the most recent CPU workload is resuming after a
+ * period of inactivity. It means that the hint session should allocate similar
+ * CPU resources to what was used previously, and must wake up if inactive.
+ */
+ CPU_LOAD_RESUME = 3,
+};
+
+/**
+ * Sends performance hints to inform the hint session of changes in the workload.
+ *
+ * @param session The performance hint session instance to update.
+ * @param hint The hint to send to the session.
+ * @return 0 on success
+ * EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_sendHint(void* session, int hint);
+
+/**
+ * Return the list of thread ids, this API should only be used for testing only.
+ */
+int APerformanceHint_getThreadIds(void* aPerformanceHintSession,
+ int32_t* const threadIds, size_t* const size);
+
__END_DECLS
#endif // ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 11c8e5d..6d64e1e 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -45,11 +45,11 @@
#define IF_LOG_TRANSACTIONS() if (false)
#define IF_LOG_COMMANDS() if (false)
-#define LOG_REMOTEREFS(...)
+#define LOG_REMOTEREFS(...)
#define IF_LOG_REMOTEREFS() if (false)
-#define LOG_THREADPOOL(...)
-#define LOG_ONEWAY(...)
+#define LOG_THREADPOOL(...)
+#define LOG_ONEWAY(...)
#else
@@ -132,12 +132,21 @@
} else {
out << "\ttarget.ptr=" << btd->target.ptr;
}
- out << "\t (cookie " << btd->cookie << ")"
- << "\n"
+ out << "\t (cookie " << btd->cookie << ")\n"
<< "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n"
- << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)"
- << "\n"
- << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)";
+ << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)\n"
+ << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)\n";
+ return btd + 1;
+}
+
+static const void* printBinderTransactionDataSecCtx(std::ostream& out, const void* data) {
+ const binder_transaction_data_secctx* btd = (const binder_transaction_data_secctx*)data;
+
+ printBinderTransactionData(out, &btd->transaction_data);
+
+ char* secctx = (char*)btd->secctx;
+ out << "\tsecctx=" << secctx << "\n";
+
return btd+1;
}
@@ -156,6 +165,11 @@
out << "\t" << kReturnStrings[cmdIndex];
switch (code) {
+ case BR_TRANSACTION_SEC_CTX: {
+ out << ": ";
+ cmd = (const int32_t*)printBinderTransactionDataSecCtx(out, cmd);
+ } break;
+
case BR_TRANSACTION:
case BR_REPLY: {
out << ": ";
@@ -394,14 +408,92 @@
// context, so we don't abort
}
+constexpr uint32_t encodeExplicitIdentity(bool hasExplicitIdentity, pid_t callingPid) {
+ uint32_t as_unsigned = static_cast<uint32_t>(callingPid);
+ if (hasExplicitIdentity) {
+ return as_unsigned | (1 << 30);
+ } else {
+ return as_unsigned & ~(1 << 30);
+ }
+}
+
+constexpr int64_t packCallingIdentity(bool hasExplicitIdentity, uid_t callingUid,
+ pid_t callingPid) {
+ // Calling PID is a 32-bit signed integer, but doesn't consume the entire 32 bit space.
+ // To future-proof this and because we have extra capacity, we decided to also support -1,
+ // since this constant is used to represent invalid UID in other places of the system.
+ // Thus, we pack hasExplicitIdentity into the 2nd bit from the left. This allows us to
+ // preserve the (left-most) bit for the sign while also encoding the value of
+ // hasExplicitIdentity.
+ // 32b | 1b | 1b | 30b
+ // token = [ calling uid | calling pid(sign) | has explicit identity | calling pid(rest) ]
+ uint64_t token = (static_cast<uint64_t>(callingUid) << 32) |
+ encodeExplicitIdentity(hasExplicitIdentity, callingPid);
+ return static_cast<int64_t>(token);
+}
+
+constexpr bool unpackHasExplicitIdentity(int64_t token) {
+ return static_cast<int32_t>(token) & (1 << 30);
+}
+
+constexpr uid_t unpackCallingUid(int64_t token) {
+ return static_cast<uid_t>(token >> 32);
+}
+
+constexpr pid_t unpackCallingPid(int64_t token) {
+ int32_t encodedPid = static_cast<int32_t>(token);
+ if (encodedPid & (1 << 31)) {
+ return encodedPid | (1 << 30);
+ } else {
+ return encodedPid & ~(1 << 30);
+ }
+}
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, 9999)) == true,
+ "pack true hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(true, 1000, 9999)) == 1000, "pack true uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(true, 1000, 9999)) == 9999, "pack true pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, 9999)) == false,
+ "pack false hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(false, 1000, 9999)) == 1000, "pack false uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(false, 1000, 9999)) == 9999, "pack false pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(true, 1000, -1)) == true,
+ "pack true (negative) hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(true, 1000, -1)) == 1000,
+ "pack true (negative) uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(true, 1000, -1)) == -1,
+ "pack true (negative) pid");
+
+static_assert(unpackHasExplicitIdentity(packCallingIdentity(false, 1000, -1)) == false,
+ "pack false (negative) hasExplicit");
+
+static_assert(unpackCallingUid(packCallingIdentity(false, 1000, -1)) == 1000,
+ "pack false (negative) uid");
+
+static_assert(unpackCallingPid(packCallingIdentity(false, 1000, -1)) == -1,
+ "pack false (negative) pid");
+
int64_t IPCThreadState::clearCallingIdentity()
{
// ignore mCallingSid for legacy reasons
- int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
+ int64_t token = packCallingIdentity(mHasExplicitIdentity, mCallingUid, mCallingPid);
clearCaller();
+ mHasExplicitIdentity = true;
return token;
}
+bool IPCThreadState::hasExplicitIdentity() {
+ return mHasExplicitIdentity;
+}
+
void IPCThreadState::setStrictModePolicy(int32_t policy)
{
mStrictModePolicy = policy;
@@ -474,9 +566,10 @@
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
- mCallingUid = (int)(token>>32);
+ mCallingUid = unpackCallingUid(token);
mCallingSid = nullptr; // not enough data to restore
- mCallingPid = (int)token;
+ mCallingPid = unpackCallingPid(token);
+ mHasExplicitIdentity = unpackHasExplicitIdentity(token);
}
void IPCThreadState::clearCaller()
@@ -889,6 +982,7 @@
mCallRestriction(mProcess->mCallRestriction) {
pthread_setspecific(gTLS, this);
clearCaller();
+ mHasExplicitIdentity = false;
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
@@ -937,6 +1031,10 @@
if (!reply && !acquireResult) goto finish;
break;
+ case BR_TRANSACTION_PENDING_FROZEN:
+ ALOGW("Sending oneway calls to frozen process.");
+ goto finish;
+
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
@@ -1279,6 +1377,7 @@
const pid_t origPid = mCallingPid;
const char* origSid = mCallingSid;
const uid_t origUid = mCallingUid;
+ const bool origHasExplicitIdentity = mHasExplicitIdentity;
const int32_t origStrictModePolicy = mStrictModePolicy;
const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
const int32_t origWorkSource = mWorkSource;
@@ -1292,6 +1391,7 @@
mCallingPid = tr.sender_pid;
mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
mCallingUid = tr.sender_euid;
+ mHasExplicitIdentity = false;
mLastTransactionBinderFlags = tr.flags;
// ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
@@ -1367,6 +1467,7 @@
mCallingPid = origPid;
mCallingSid = origSid;
mCallingUid = origUid;
+ mHasExplicitIdentity = origHasExplicitIdentity;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
mWorkSource = origWorkSource;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a0c4334..2408307 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -444,7 +444,7 @@
bool declared;
if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
!status.isOk()) {
- ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+ ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(),
status.toString8().c_str());
return false;
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 07d0a65..44ff62b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -614,11 +614,14 @@
if (status_t status = readInt32(&fdIndex); status != OK) {
return status;
}
- const auto& oldFd = otherRpcFields->mFds->at(fdIndex);
+ int oldFd = toRawFd(otherRpcFields->mFds->at(fdIndex));
// To match kernel binder behavior, we always dup, even if the
// FD was unowned in the source parcel.
- rpcFields->mFds->emplace_back(
- base::unique_fd(fcntl(toRawFd(oldFd), F_DUPFD_CLOEXEC, 0)));
+ int newFd = -1;
+ if (status_t status = dupFileDescriptor(oldFd, &newFd); status != OK) {
+ ALOGW("Failed to duplicate file descriptor %d: %s", oldFd, strerror(-status));
+ }
+ rpcFields->mFds->emplace_back(base::unique_fd(newFd));
// Fixup the index in the data.
mDataPos = newDataPos + 4;
if (status_t status = writeInt32(rpcFields->mFds->size() - 1); status != OK) {
@@ -966,7 +969,15 @@
}
}
+void Parcel::setEnforceNoDataAvail(bool enforceNoDataAvail) {
+ mEnforceNoDataAvail = enforceNoDataAvail;
+}
+
binder::Status Parcel::enforceNoDataAvail() const {
+ if (!mEnforceNoDataAvail) {
+ return binder::Status::ok();
+ }
+
const auto n = dataAvail();
if (n == 0) {
return binder::Status::ok();
@@ -1464,7 +1475,7 @@
#ifdef BINDER_WITH_KERNEL_IPC
flat_binder_object obj;
obj.hdr.type = BINDER_TYPE_FD;
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj.flags = 0;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = fd;
obj.cookie = takeOwnership ? 1 : 0;
@@ -3077,6 +3088,7 @@
mAllowFds = true;
mDeallocZero = false;
mOwner = nullptr;
+ mEnforceNoDataAvail = true;
}
void Parcel::scanForFds() const {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1f311ac..254dda8 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -439,6 +439,10 @@
return mCurrentThreads;
}
+bool ProcessState::isThreadPoolStarted() const {
+ return mThreadPoolStarted;
+}
+
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
static const char* const names[] = {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 0820cd1..0d06e9e 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -50,7 +50,8 @@
RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {}
RpcServer::~RpcServer() {
- (void)shutdown();
+ RpcMutexUniqueLock _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Must call shutdown() before destructor");
}
sp<RpcServer> RpcServer::make(std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
@@ -70,11 +71,8 @@
return setupSocketServer(UnixSocketAddress(path));
}
-status_t RpcServer::setupVsockServer(unsigned int port) {
- // realizing value w/ this type at compile time to avoid ubsan abort
- constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
-
- return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+status_t RpcServer::setupVsockServer(unsigned int bindCid, unsigned int port) {
+ return setupSocketServer(VsockSocketAddress(bindCid, port));
}
status_t RpcServer::setupInetServer(const char* address, unsigned int port,
@@ -157,6 +155,12 @@
mRootObjectFactory = std::move(makeObject);
}
+void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& filter) {
+ RpcMutexLockGuard _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
+ mConnectionFilter = std::move(filter);
+}
+
sp<IBinder> RpcServer::getRootObject() {
RpcMutexLockGuard _l(mLock);
bool hasWeak = mRootObjectWeak.unsafe_get();
@@ -200,11 +204,15 @@
iovec iov{&zero, sizeof(zero)};
std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
- if (receiveMessageFromSocket(server.mServer, &iov, 1, &fds) < 0) {
+ ssize_t num_bytes = receiveMessageFromSocket(server.mServer, &iov, 1, &fds);
+ if (num_bytes < 0) {
int savedErrno = errno;
ALOGE("Failed recvmsg: %s", strerror(savedErrno));
return -savedErrno;
}
+ if (num_bytes == 0) {
+ return DEAD_OBJECT;
+ }
if (fds.size() != 1) {
ALOGE("Expected exactly one fd from recvmsg, got %zu", fds.size());
return -EINVAL;
@@ -239,16 +247,25 @@
socklen_t addrLen = addr.size();
RpcTransportFd clientSocket;
- if (mAcceptFn(*this, &clientSocket) != OK) {
- continue;
+ if ((status = mAcceptFn(*this, &clientSocket)) != OK) {
+ if (status == DEAD_OBJECT)
+ break;
+ else
+ continue;
}
+
+ LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+
if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
&addrLen)) {
ALOGE("Could not getpeername socket: %s", strerror(errno));
continue;
}
- LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+ if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) {
+ ALOGE("Dropped client connection fd %d", clientSocket.fd.get());
+ continue;
+ }
{
RpcMutexLockGuard _l(mLock);
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 453279c..8b3ddfb 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -63,12 +63,14 @@
if (pfd.revents & POLLERR) {
return DEAD_OBJECT;
}
+ if (pfd.revents & POLLIN) {
+ // Copied from FdTrigger.cpp: Even though POLLHUP may also be set,
+ // treat it as a success condition to ensure data is drained.
+ return OK;
+ }
if (pfd.revents & POLLHUP) {
return DEAD_OBJECT;
}
- if (pfd.revents & POLLIN) {
- return OK;
- }
return WOULD_BLOCK;
}
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+#ifndef BR_TRANSACTION_PENDING_FROZEN
+// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
+#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
+#endif // BR_TRANSACTION_PENDING_FROZEN
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index dc572ac..8cc8105 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -230,7 +230,6 @@
"android.graphicsenv.IGpuService",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
"android.hardware.ICamera",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index c01e92f..d261c21 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -139,12 +139,15 @@
int64_t clearCallingIdentity();
// Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
+ bool hasExplicitIdentity();
+ // For main functions - dangerous for libraries to use
status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
bool flushIfNeeded();
+ // For main functions - dangerous for libraries to use
void joinThreadPool(bool isMain = true);
// Stop the local process.
@@ -241,6 +244,7 @@
bool mPropagateWorkSource;
bool mIsLooper;
bool mIsFlushing;
+ bool mHasExplicitIdentity;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
CallRestriction mCallRestriction;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 79e771f..2af512e 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -67,7 +67,8 @@
* a system property, or in the case of services in the VINTF manifest, it can be checked
* with isDeclared).
*/
- virtual sp<IBinder> getService( const String16& name) const = 0;
+ [[deprecated("this polls for 5s, prefer waitForService or checkService")]]
+ virtual sp<IBinder> getService(const String16& name) const = 0;
/**
* Retrieve an existing service, non-blocking.
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 6de6ce8..f730acb 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -150,6 +150,9 @@
// Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
binder::Status enforceNoDataAvail() const;
+ // This Api is used by fuzzers to skip dataAvail checks.
+ void setEnforceNoDataAvail(bool enforceNoDataAvail);
+
void freeData();
size_t objectsCount() const;
@@ -1329,6 +1332,9 @@
// data to be overridden with zero when deallocated
mutable bool mDeallocZero;
+ // Set this to false to skip dataAvail checks.
+ bool mEnforceNoDataAvail;
+
release_func mOwner;
size_t mReserved;
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9679a5f..bad8cb1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,6 +50,7 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
+ // For main functions - dangerous for libraries to use
void startThreadPool();
bool becomeContextManager();
@@ -57,8 +58,10 @@
sp<IBinder> getStrongProxyForHandle(int32_t handle);
void expungeHandle(int32_t handle, IBinder* binder);
+ // TODO: deprecate.
void spawnPooledThread(bool isMain);
+ // For main functions - dangerous for libraries to use
status_t setThreadPoolMaxThreadCount(size_t maxThreads);
status_t enableOnewaySpamDetection(bool enable);
void giveThreadPoolName();
@@ -94,6 +97,11 @@
*/
size_t getThreadPoolMaxTotalThreadCount() const;
+ /**
+ * Check to see if the thread pool has started.
+ */
+ bool isThreadPoolStarted() const;
+
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 4ad0a47..25193a3 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -81,9 +81,9 @@
[[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);
/**
- * Creates an RPC server at the current port.
+ * Creates an RPC server binding to the given CID at the given port.
*/
- [[nodiscard]] status_t setupVsockServer(unsigned int port);
+ [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port);
/**
* Creates an RPC server at the current port using IPv4.
@@ -171,6 +171,16 @@
sp<IBinder> getRootObject();
/**
+ * Set optional filter of incoming connections based on the peer's address.
+ *
+ * Takes one argument: a callable that is invoked on each accept()-ed
+ * connection and returns false if the connection should be dropped.
+ * See the description of setPerSessionRootObject() for details about
+ * the callable's arguments.
+ */
+ void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
+
+ /**
* See RpcTransportCtx::getCertificate
*/
std::vector<uint8_t> getCertificate(RpcCertificateFormat);
@@ -253,6 +263,7 @@
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
+ std::function<bool(const void*, size_t)> mConnectionFilter;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
RpcConditionVariable mShutdownCv;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index dd177af..3ebbed6 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -17,51 +17,108 @@
#pragma once
#include <sys/socket.h>
+#include <stdint.h>
extern "C" {
struct AIBinder;
+struct ARpcServer;
+struct ARpcSession;
+
+enum class ARpcSession_FileDescriptorTransportMode {
+ None,
+ Unix,
+ Trusty,
+};
// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server and joins before returning.
-bool RunVsockRpcServer(AIBinder* service, unsigned int port);
+// The server will only accept connections from the given CID.
+// Set `cid` to VMADDR_CID_ANY to accept connections from any client.
+// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
+ unsigned int port);
-// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
-bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
- void (*readyCallback)(void* param), void* param);
-
-// Starts an RPC server on a given port and a given root IBinder factory.
-// RunVsockRpcServerWithFactory acts like RunVsockRpcServerCallback, but instead of
-// assigning single root IBinder object to all connections, factory is called
-// whenever a client connects, making it possible to assign unique IBinder
-// object to each client.
-bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
- void* factoryContext, unsigned int port);
-
-AIBinder* VsockRpcClient(unsigned int cid, unsigned int port);
-
-// Starts a Unix domain RPC server with a given init-managed Unix domain `name` and
-// a given root IBinder object.
+// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
+// and a given root IBinder object.
// The socket should be created in init.rc with the same `name`.
-//
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
-bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name,
- void (*readyCallback)(void* param), void* param);
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name);
-// Gets the service via the RPC binder with Unix domain socket with the given
-// Unix socket `name`.
-// The final Unix domain socket path name is /dev/socket/`name`.
-AIBinder* UnixDomainRpcClient(const char* name);
+// Starts an RPC server that bootstraps sessions using an existing Unix domain
+// socket pair, with a given root IBinder object.
+// Callers should create a pair of SOCK_STREAM Unix domain sockets, pass one to
+// this function and the other to UnixDomainBootstrapClient(). Multiple client
+// session can be created from the client end of the pair.
+// Does not take ownership of `service`.
+// Returns an opaque handle to the running server instance, or null if the server
+// could not be started.
+[[nodiscard]] ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd);
-// Connect to an RPC server with preconnected file descriptors.
+// Sets the list of supported file descriptor transport modes of this RPC server.
+void ARpcServer_setSupportedFileDescriptorTransportModes(
+ ARpcServer* handle,
+ const ARpcSession_FileDescriptorTransportMode modes[],
+ size_t modes_len);
+
+// Runs ARpcServer_join() in a background thread. Immediately returns.
+void ARpcServer_start(ARpcServer* server);
+
+// Joins the thread of a running RpcServer instance. At any given point, there
+// can only be one thread calling ARpcServer_join().
+// If a client needs to actively terminate join, call ARpcServer_shutdown() in
+// a separate thread.
+void ARpcServer_join(ARpcServer* server);
+
+// Shuts down any running ARpcServer_join().
+[[nodiscard]] bool ARpcServer_shutdown(ARpcServer* server);
+
+// Frees the ARpcServer handle and drops the reference count on the underlying
+// RpcServer instance. The handle must not be reused afterwards.
+// This automatically calls ARpcServer_shutdown().
+void ARpcServer_free(ARpcServer* server);
+
+// Allocates a new RpcSession object and returns an opaque handle to it.
+[[nodiscard]] ARpcSession* ARpcSession_new();
+
+// Connects to an RPC server over vsock at a given CID on a given port.
+// Returns the root Binder object of the server.
+AIBinder* ARpcSession_setupVsockClient(ARpcSession* session, unsigned int cid,
+ unsigned int port);
+
+// Connects to an RPC server over a Unix Domain Socket of the given name.
+// The final Unix Domain Socket path name is /dev/socket/`name`.
+// Returns the root Binder object of the server.
+AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* session, const char* name);
+
+// Connects to an RPC server over the given bootstrap Unix domain socket.
+// Does NOT take ownership of `bootstrapFd`.
+AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* session,
+ int bootstrapFd);
+
+// Connects to an RPC server with preconnected file descriptors.
//
// requestFd should connect to the server and return a valid file descriptor, or
// -1 if connection fails.
//
// param will be passed to requestFd. Callers can use param to pass contexts to
// the requestFd function.
-AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param);
+AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session,
+ int (*requestFd)(void* param),
+ void* param);
+
+// Sets the file descriptor transport mode for this session.
+void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session,
+ ARpcSession_FileDescriptorTransportMode mode);
+
+// Sets the maximum number of incoming threads.
+void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads);
+
+// Sets the maximum number of outgoing threads.
+void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads);
+
+// Decrements the refcount of the underlying RpcSession object.
+void ARpcSession_free(ARpcSession* session);
}
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index 9edb3b6..78dae4b 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <binder_rpc_unstable.hpp>
+
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android/binder_libbinder.h>
@@ -25,63 +27,158 @@
using android::OK;
using android::RpcServer;
using android::RpcSession;
+using android::sp;
using android::status_t;
using android::statusToString;
using android::base::unique_fd;
+// Opaque handle for RpcServer.
+struct ARpcServer {};
+
+// Opaque handle for RpcSession.
+struct ARpcSession {};
+
+template <typename A, typename T>
+static A* createObjectHandle(sp<T>& server) {
+ auto ref = server.get();
+ ref->incStrong(ref);
+ return reinterpret_cast<A*>(ref);
+}
+
+template <typename T, typename A>
+static void freeObjectHandle(A* handle) {
+ LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null");
+ auto ref = reinterpret_cast<T*>(handle);
+ ref->decStrong(ref);
+}
+
+template <typename T, typename A>
+static sp<T> handleToStrongPointer(A* handle) {
+ LOG_ALWAYS_FATAL_IF(handle == nullptr, "Handle cannot be null");
+ auto ref = reinterpret_cast<T*>(handle);
+ return sp<T>::fromExisting(ref);
+}
+
+RpcSession::FileDescriptorTransportMode toTransportMode(
+ ARpcSession_FileDescriptorTransportMode mode) {
+ switch (mode) {
+ case ARpcSession_FileDescriptorTransportMode::None:
+ return RpcSession::FileDescriptorTransportMode::NONE;
+ case ARpcSession_FileDescriptorTransportMode::Unix:
+ return RpcSession::FileDescriptorTransportMode::UNIX;
+ case ARpcSession_FileDescriptorTransportMode::Trusty:
+ return RpcSession::FileDescriptorTransportMode::TRUSTY;
+ default:
+ return RpcSession::FileDescriptorTransportMode::NONE;
+ }
+}
+
extern "C" {
-void RunRpcServer(android::sp<RpcServer>& server, AIBinder* service,
- void (*readyCallback)(void* param), void* param) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
+ auto server = RpcServer::make();
+
+ unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
+ if (cid == VMADDR_CID_LOCAL) {
+ bindCid = VMADDR_CID_LOCAL; // bind to the local interface
+ cid = VMADDR_CID_ANY; // no need for a connection filter
+ }
+
+ if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
+ LOG(ERROR) << "Failed to set up vsock server with port " << port
+ << " error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ if (cid != VMADDR_CID_ANY) {
+ server->setConnectionFilter([=](const void* addr, size_t addrlen) {
+ LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
+ const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+ LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
+ if (cid != vaddr->svm_cid) {
+ LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid;
+ return false;
+ }
+ return true;
+ });
+ }
server->setRootObject(AIBinder_toPlatformBinder(service));
-
- if (readyCallback) readyCallback(param);
- server->join();
-
- // Shutdown any open sessions since server failed.
- (void)server->shutdown();
+ return createObjectHandle<ARpcServer>(server);
}
-bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
- void* factoryContext, unsigned int port) {
+ARpcServer* ARpcServer_newInitUnixDomain(AIBinder* service, const char* name) {
auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
- LOG(ERROR) << "Failed to set up vsock server with port " << port
- << " error: " << statusToString(status).c_str();
- return false;
+ auto fd = unique_fd(android_get_control_socket(name));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Failed to get fd for the socket:" << name;
+ return nullptr;
}
- server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
- LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
- const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
- LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
- return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
- });
-
- server->join();
-
- // Shutdown any open sessions since server failed.
- (void)server->shutdown();
- return true;
+ if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
+ LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
+ << " error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ server->setRootObject(AIBinder_toPlatformBinder(service));
+ return createObjectHandle<ARpcServer>(server);
}
-bool RunVsockRpcServerCallback(AIBinder* service, unsigned int port,
- void (*readyCallback)(void* param), void* param) {
+ARpcServer* ARpcServer_newUnixDomainBootstrap(AIBinder* service, int bootstrapFd) {
auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
- LOG(ERROR) << "Failed to set up vsock server with port " << port
- << " error: " << statusToString(status).c_str();
- return false;
+ auto fd = unique_fd(bootstrapFd);
+ if (!fd.ok()) {
+ LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+ return nullptr;
}
- RunRpcServer(server, service, readyCallback, param);
- return true;
+ if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd));
+ status != OK) {
+ LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd
+ << " error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ server->setRootObject(AIBinder_toPlatformBinder(service));
+ return createObjectHandle<ARpcServer>(server);
}
-bool RunVsockRpcServer(AIBinder* service, unsigned int port) {
- return RunVsockRpcServerCallback(service, port, nullptr, nullptr);
+void ARpcServer_setSupportedFileDescriptorTransportModes(
+ ARpcServer* handle, const ARpcSession_FileDescriptorTransportMode modes[],
+ size_t modes_len) {
+ auto server = handleToStrongPointer<RpcServer>(handle);
+ std::vector<RpcSession::FileDescriptorTransportMode> modevec;
+ for (size_t i = 0; i < modes_len; i++) {
+ modevec.push_back(toTransportMode(modes[i]));
+ }
+ server->setSupportedFileDescriptorTransportModes(modevec);
}
-AIBinder* VsockRpcClient(unsigned int cid, unsigned int port) {
+void ARpcServer_start(ARpcServer* handle) {
+ handleToStrongPointer<RpcServer>(handle)->start();
+}
+
+void ARpcServer_join(ARpcServer* handle) {
+ handleToStrongPointer<RpcServer>(handle)->join();
+}
+
+bool ARpcServer_shutdown(ARpcServer* handle) {
+ return handleToStrongPointer<RpcServer>(handle)->shutdown();
+}
+
+void ARpcServer_free(ARpcServer* handle) {
+ // Ignore the result of ARpcServer_shutdown - either it had been called
+ // earlier, or the RpcServer destructor will panic.
+ (void)ARpcServer_shutdown(handle);
+ freeObjectHandle<RpcServer>(handle);
+}
+
+ARpcSession* ARpcSession_new() {
auto session = RpcSession::make();
+ return createObjectHandle<ARpcSession>(session);
+}
+
+void ARpcSession_free(ARpcSession* handle) {
+ freeObjectHandle<RpcSession>(handle);
+}
+
+AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
if (status_t status = session->setupVsockClient(cid, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
<< " error: " << statusToString(status).c_str();
@@ -90,27 +187,10 @@
return AIBinder_fromPlatformBinder(session->getRootObject());
}
-bool RunInitUnixDomainRpcServer(AIBinder* service, const char* name,
- void (*readyCallback)(void* param), void* param) {
- auto server = RpcServer::make();
- auto fd = unique_fd(android_get_control_socket(name));
- if (!fd.ok()) {
- LOG(ERROR) << "Failed to get fd for the socket:" << name;
- return false;
- }
- if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
- LOG(ERROR) << "Failed to set up Unix Domain RPC server with name " << name
- << " error: " << statusToString(status).c_str();
- return false;
- }
- RunRpcServer(server, service, readyCallback, param);
- return true;
-}
-
-AIBinder* UnixDomainRpcClient(const char* name) {
+AIBinder* ARpcSession_setupUnixDomainClient(ARpcSession* handle, const char* name) {
std::string pathname(name);
pathname = ANDROID_SOCKET_DIR "/" + pathname;
- auto session = RpcSession::make();
+ auto session = handleToStrongPointer<RpcSession>(handle);
if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) {
LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname
<< " error: " << statusToString(status).c_str();
@@ -119,8 +199,25 @@
return AIBinder_fromPlatformBinder(session->getRootObject());
}
-AIBinder* RpcPreconnectedClient(int (*requestFd)(void* param), void* param) {
- auto session = RpcSession::make();
+AIBinder* ARpcSession_setupUnixDomainBootstrapClient(ARpcSession* handle, int bootstrapFd) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
+ auto fd = unique_fd(dup(bootstrapFd));
+ if (!fd.ok()) {
+ LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+ return nullptr;
+ }
+ if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd));
+ status != OK) {
+ LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd
+ << " error: " << statusToString(status).c_str();
+ return nullptr;
+ }
+ return AIBinder_fromPlatformBinder(session->getRootObject());
+}
+
+AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* handle, int (*requestFd)(void* param),
+ void* param) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
auto request = [=] { return unique_fd{requestFd(param)}; };
if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str();
@@ -128,4 +225,20 @@
}
return AIBinder_fromPlatformBinder(session->getRootObject());
}
+
+void ARpcSession_setFileDescriptorTransportMode(ARpcSession* handle,
+ ARpcSession_FileDescriptorTransportMode mode) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
+ session->setFileDescriptorTransportMode(toTransportMode(mode));
+}
+
+void ARpcSession_setMaxIncomingThreads(ARpcSession* handle, size_t threads) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
+ session->setMaxIncomingThreads(threads);
+}
+
+void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) {
+ auto session = handleToStrongPointer<RpcSession>(handle);
+ session->setMaxOutgoingThreads(threads);
+}
}
diff --git a/libs/binder/libbinder_rpc_unstable.map.txt b/libs/binder/libbinder_rpc_unstable.map.txt
index f9c7bcf..1bc2416 100644
--- a/libs/binder/libbinder_rpc_unstable.map.txt
+++ b/libs/binder/libbinder_rpc_unstable.map.txt
@@ -1,9 +1,12 @@
LIBBINDER_RPC_UNSTABLE_SHIM { # platform-only
global:
- RunVsockRpcServer;
- RunVsockRpcServerCallback;
+ ARpcServer_free;
+ ARpcServer_join;
+ ARpcServer_newInitUnixDomain;
+ ARpcServer_newVsock;
+ ARpcServer_shutdown;
+ ARpcServer_start;
VsockRpcClient;
- RunInitUnixDomainRpcServer;
UnixDomainRpcClient;
RpcPreconnectedClient;
local:
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 81975e7..9949de2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -196,6 +196,10 @@
bool isRemote() override final { return false; }
+ static std::string makeServiceName(std::string_view instance) {
+ return INTERFACE::descriptor + ("/" + std::string(instance));
+ }
+
protected:
/**
* This function should only be called by asBinder. Otherwise, there is a possibility of
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 6a25db2..2a00736 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -160,7 +160,7 @@
template <typename _T>
std::string ToString(const _T& t) {
if constexpr (details::ToEmptyString<_T>::value) {
- return "";
+ return "<unimplemented>";
} else if constexpr (std::is_same_v<bool, _T>) {
return t ? "true" : "false";
} else if constexpr (std::is_same_v<char16_t, _T>) {
@@ -176,9 +176,11 @@
return t;
#ifdef HAS_NDK_INTERFACE
} else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
- return (t.get() == nullptr) ? "(null)" : "";
+ std::stringstream ss;
+ ss << "binder:" << std::hex << t.get();
+ return ss.str();
} else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
- return (t.get() == -1) ? "(null)" : "";
+ return "fd:" + std::to_string(t.get());
#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4163897..1af2119 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -613,7 +613,8 @@
*
* 1. If the binder died, shortly after the call to onBinderDied.
* 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
- * AIBinder_DeathRecipient_delete.
+ * AIBinder_DeathRecipient_delete, after any pending onBinderDied calls
+ * finish.
* 3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
*
* It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index c234270..ad4188f 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -68,6 +68,7 @@
*
* \param instance identifier of the service used to lookup the service.
*/
+[[deprecated("this polls 5s, use AServiceManager_waitForService or AServiceManager_checkService")]]
__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance)
__INTRODUCED_IN(29);
@@ -108,6 +109,67 @@
__INTRODUCED_IN(31);
/**
+ * Function to call when a service is registered. The instance is passed as well as
+ * ownership of the binder named 'registered'.
+ *
+ * WARNING: a lock is held when this method is called in order to prevent races with
+ * AServiceManager_NotificationRegistration_delete. Do not make synchronous binder calls when
+ * implementing this method to avoid deadlocks.
+ *
+ * \param instance instance name of service registered
+ * \param registered ownership-passed instance of service registered
+ * \param cookie data passed during registration for notifications
+ */
+typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered,
+ void* cookie);
+
+/**
+ * Represents a registration to servicemanager which can be cleared anytime.
+ */
+struct AServiceManager_NotificationRegistration;
+
+/**
+ * Get notifications when a service is registered. If the service is already registered,
+ * you will immediately get a notification.
+ *
+ * WARNING: it is strongly recommended to use AServiceManager_waitForService API instead.
+ * That API will wait synchronously, which is what you usually want in cases, including
+ * using some feature or during boot up. There is a history of bugs where waiting for
+ * notifications like this races with service startup. Also, when this API is used, a service
+ * bug will result in silent failure (rather than a debuggable deadlock). Furthermore, there
+ * is a history of this API being used to know when a service is up as a proxy for whethre
+ * that service should be started. This should only be used if you are intending to get
+ * ahold of the service as a client. For lazy services, whether a service is registered
+ * should not be used as a proxy for when it should be registered, which is only known
+ * by the real client.
+ *
+ * WARNING: if you use this API, you must also ensure that you check missing services are
+ * started and crash otherwise. If service failures are ignored, the system rots.
+ *
+ * \param instance name of service to wait for notifications about
+ * \param onRegister callback for when service is registered
+ * \param cookie data associated with this callback
+ *
+ * \return the token for this registration. Deleting this token will unregister.
+ */
+__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration*
+AServiceManager_registerForServiceNotifications(const char* instance,
+ AServiceManager_onRegister onRegister, void* cookie)
+ __INTRODUCED_IN(34);
+
+/**
+ * Unregister for notifications and delete the object.
+ *
+ * After this method is called, the callback is guaranteed to no longer be invoked. This will block
+ * until any in-progress onRegister callbacks have completed. It is therefore safe to immediately
+ * destroy the void* cookie that was registered when this method returns.
+ *
+ * \param notification object to dismiss
+ */
+void AServiceManager_NotificationRegistration_delete(
+ AServiceManager_NotificationRegistration* notification) __INTRODUCED_IN(34);
+
+/**
* Check if a service is declared (e.g. VINTF manifest).
*
* \param instance identifier of the service.
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index f408fad..ffcad55 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -28,17 +28,33 @@
*
* When using this, it is expected that ABinderProcess_setupPolling and
* ABinderProcess_handlePolledCommands are not used.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_startThreadPool();
/**
* This sets the maximum number of threads that can be started in the threadpool. By default, after
* startThreadPool is called, this is 15. If it is called additional times, it will only prevent
* the kernel from starting new threads and will not delete already existing threads.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
/**
+ * Check if the threadpool has already been started.
+ * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually,
+ * you should use this in a library to abort if the threadpool is not started.
+ * Programs should configure binder threadpools once at the beginning.
+ */
+bool ABinderProcess_isThreadPoolStarted();
+/**
* This adds the current thread to the threadpool. This may cause the threadpool to exceed the
* maximum size.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_joinThreadPool();
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 683a433..c1f62e5 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -50,6 +50,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the vendor partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToVendorStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
@@ -79,6 +88,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the system partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToSystemStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 32ca564..54e4628 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -154,7 +154,10 @@
LIBBINDER_NDK34 { # introduced=UpsideDownCake
global:
+ ABinderProcess_isThreadPoolStarted; # systemapi llndk
AServiceManager_getUpdatableApexName; # systemapi
+ AServiceManager_registerForServiceNotifications; # systemapi llndk
+ AServiceManager_NotificationRegistration_delete; # systemapi llndk
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index ac582a4..bc6610e 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -31,6 +31,9 @@
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
}
+bool ABinderProcess_isThreadPoolStarted() {
+ return ProcessState::self()->isThreadPoolStarted();
+}
void ABinderProcess_joinThreadPool() {
IPCThreadState::self()->joinThreadPool();
}
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index a12d0e9..e107c83 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -28,6 +28,7 @@
using ::android::IServiceManager;
using ::android::sp;
using ::android::status_t;
+using ::android::statusToString;
using ::android::String16;
using ::android::String8;
@@ -86,6 +87,67 @@
AIBinder_incStrong(ret.get());
return ret.get();
}
+typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered,
+ void* cookie);
+
+struct AServiceManager_NotificationRegistration
+ : public IServiceManager::LocalRegistrationCallback {
+ std::mutex m;
+ const char* instance = nullptr;
+ void* cookie = nullptr;
+ AServiceManager_onRegister onRegister = nullptr;
+
+ virtual void onServiceRegistration(const String16& smInstance, const sp<IBinder>& binder) {
+ std::lock_guard<std::mutex> l(m);
+ if (onRegister == nullptr) return;
+
+ CHECK_EQ(String8(smInstance), instance);
+
+ sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder_incStrong(ret.get());
+
+ onRegister(instance, ret.get(), cookie);
+ }
+
+ void clear() {
+ std::lock_guard<std::mutex> l(m);
+ instance = nullptr;
+ cookie = nullptr;
+ onRegister = nullptr;
+ }
+};
+
+__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration*
+AServiceManager_registerForServiceNotifications(const char* instance,
+ AServiceManager_onRegister onRegister,
+ void* cookie) {
+ CHECK_NE(instance, nullptr);
+ CHECK_NE(onRegister, nullptr) << instance;
+ // cookie can be nullptr
+
+ auto cb = sp<AServiceManager_NotificationRegistration>::make();
+ cb->instance = instance;
+ cb->onRegister = onRegister;
+ cb->cookie = cookie;
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) {
+ LOG(ERROR) << "Failed to register for service notifications for " << instance << ": "
+ << statusToString(res);
+ return nullptr;
+ }
+
+ cb->incStrong(nullptr);
+ return cb.get();
+}
+
+void AServiceManager_NotificationRegistration_delete(
+ AServiceManager_NotificationRegistration* notification) {
+ CHECK_NE(notification, nullptr);
+ notification->clear();
+ notification->decStrong(nullptr);
+}
+
bool AServiceManager_isDeclared(const char* instance) {
if (instance == nullptr) {
return false;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index e221e4c..9d5ef68 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -254,6 +254,47 @@
AIBinder_decStrong(binder);
}
+struct ServiceData {
+ std::string instance;
+ ndk::SpAIBinder binder;
+
+ static void fillOnRegister(const char* instance, AIBinder* binder, void* cookie) {
+ ServiceData* d = reinterpret_cast<ServiceData*>(cookie);
+ d->instance = instance;
+ d->binder = ndk::SpAIBinder(binder);
+ }
+};
+
+TEST(NdkBinder, RegisterForServiceNotificationsNonExisting) {
+ ServiceData data;
+ auto* notif = AServiceManager_registerForServiceNotifications(
+ "DOES_NOT_EXIST", ServiceData::fillOnRegister, (void*)&data);
+ ASSERT_NE(notif, nullptr);
+
+ sleep(1); // give us a chance to fail
+ AServiceManager_NotificationRegistration_delete(notif);
+
+ // checking after deleting to avoid needing a mutex over the data - otherwise
+ // in an environment w/ multiple threads, you would need to guard access
+ EXPECT_EQ(data.instance, "");
+ EXPECT_EQ(data.binder, nullptr);
+}
+
+TEST(NdkBinder, RegisterForServiceNotificationsExisting) {
+ ServiceData data;
+ auto* notif = AServiceManager_registerForServiceNotifications(
+ kExistingNonNdkService, ServiceData::fillOnRegister, (void*)&data);
+ ASSERT_NE(notif, nullptr);
+
+ sleep(1); // give us a chance to fail
+ AServiceManager_NotificationRegistration_delete(notif);
+
+ // checking after deleting to avoid needing a mutex over the data - otherwise
+ // in an environment w/ multiple threads, you would need to guard access
+ EXPECT_EQ(data.instance, kExistingNonNdkService);
+ EXPECT_EQ(data.binder, ndk::SpAIBinder(AServiceManager_checkService(kExistingNonNdkService)));
+}
+
TEST(NdkBinder, UnimplementedDump) {
sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
ASSERT_NE(foo, nullptr);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 738d16a..afd414a 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -17,7 +17,6 @@
rustlibs: [
"libbinder_ndk_sys",
"libdowncast_rs",
- "liblazy_static",
"liblibc",
],
host_supported: true,
@@ -160,7 +159,6 @@
rustlibs: [
"libbinder_ndk_sys",
"libdowncast_rs",
- "liblazy_static",
"liblibc",
],
}
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 9771cc9..afb73e9 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -19,6 +19,7 @@
"libbinder_rpc_unstable_bindgen_sys",
"libbinder_rs",
"libdowncast_rs",
+ "libforeign_types",
"liblibc",
"liblog_rust",
],
@@ -67,10 +68,13 @@
visibility: [":__subpackages__"],
source_stem: "bindings",
bindgen_flags: [
+ "--size_t-is-usize",
"--blocklist-type",
"AIBinder",
"--raw-line",
"use binder_ndk_sys::AIBinder;",
+ "--rustified-enum",
+ "ARpcSession_FileDescriptorTransportMode",
],
rustlibs: [
"libbinder_ndk_sys",
diff --git a/libs/binder/rust/rpcbinder/src/client.rs b/libs/binder/rust/rpcbinder/src/client.rs
deleted file mode 100644
index 48c787b..0000000
--- a/libs/binder/rust/rpcbinder/src/client.rs
+++ /dev/null
@@ -1,111 +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.
- */
-
-use binder::{unstable_api::new_spibinder, FromIBinder, SpIBinder, StatusCode, Strong};
-use std::ffi::CString;
-use std::os::{
- raw::{c_int, c_void},
- unix::io::RawFd,
-};
-
-/// Connects to an RPC Binder server over vsock.
-pub fn get_vsock_rpc_service(cid: u32, port: u32) -> Option<SpIBinder> {
- // SAFETY: AIBinder returned by VsockRpcClient has correct reference count,
- // and the ownership can safely be taken by new_spibinder.
- unsafe { new_spibinder(binder_rpc_unstable_bindgen::VsockRpcClient(cid, port)) }
-}
-
-/// Connects to an RPC Binder server for a particular interface over vsock.
-pub fn get_vsock_rpc_interface<T: FromIBinder + ?Sized>(
- cid: u32,
- port: u32,
-) -> Result<Strong<T>, StatusCode> {
- interface_cast(get_vsock_rpc_service(cid, port))
-}
-
-/// Connects to an RPC Binder server over Unix domain socket.
-pub fn get_unix_domain_rpc_service(socket_name: &str) -> Option<SpIBinder> {
- let socket_name = match CString::new(socket_name) {
- Ok(s) => s,
- Err(e) => {
- log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
- return None;
- }
- };
- // SAFETY: AIBinder returned by UnixDomainRpcClient has correct reference count,
- // and the ownership can safely be taken by new_spibinder.
- unsafe { new_spibinder(binder_rpc_unstable_bindgen::UnixDomainRpcClient(socket_name.as_ptr())) }
-}
-
-/// Connects to an RPC Binder server for a particular interface over Unix domain socket.
-pub fn get_unix_domain_rpc_interface<T: FromIBinder + ?Sized>(
- socket_name: &str,
-) -> Result<Strong<T>, StatusCode> {
- interface_cast(get_unix_domain_rpc_service(socket_name))
-}
-
-/// Connects to an RPC Binder server, using the given callback to get (and take ownership of)
-/// file descriptors already connected to it.
-pub fn get_preconnected_rpc_service(
- mut request_fd: impl FnMut() -> Option<RawFd>,
-) -> Option<SpIBinder> {
- // Double reference the factory because trait objects aren't FFI safe.
- let mut request_fd_ref: RequestFd = &mut request_fd;
- let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
-
- // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
- // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
- // of param, only passing it to request_fd_wrapper.
- unsafe {
- new_spibinder(binder_rpc_unstable_bindgen::RpcPreconnectedClient(
- Some(request_fd_wrapper),
- param,
- ))
- }
-}
-
-type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
-
-unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
- // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
- // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
- // initialized instance.
- let request_fd_ptr = param as *mut RequestFd;
- let request_fd = request_fd_ptr.as_mut().unwrap();
- if let Some(fd) = request_fd() {
- fd
- } else {
- -1
- }
-}
-
-/// Connects to an RPC Binder server for a particular interface, using the given callback to get
-/// (and take ownership of) file descriptors already connected to it.
-pub fn get_preconnected_rpc_interface<T: FromIBinder + ?Sized>(
- request_fd: impl FnMut() -> Option<RawFd>,
-) -> Result<Strong<T>, StatusCode> {
- interface_cast(get_preconnected_rpc_service(request_fd))
-}
-
-fn interface_cast<T: FromIBinder + ?Sized>(
- service: Option<SpIBinder>,
-) -> Result<Strong<T>, StatusCode> {
- if let Some(service) = service {
- FromIBinder::try_from(service)
- } else {
- Err(StatusCode::NAME_NOT_FOUND)
- }
-}
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index 89a49a4..a957385 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -16,13 +16,8 @@
//! API for RPC Binder services.
-mod client;
mod server;
+mod session;
-pub use client::{
- get_preconnected_rpc_interface, get_preconnected_rpc_service, get_unix_domain_rpc_interface,
- get_unix_domain_rpc_service, get_vsock_rpc_interface, get_vsock_rpc_service,
-};
-pub use server::{
- run_init_unix_domain_rpc_server, run_vsock_rpc_server, run_vsock_rpc_server_with_factory,
-};
+pub use server::{RpcServer, RpcServerRef};
+pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index b350a13..761b306 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -14,161 +14,137 @@
* limitations under the License.
*/
-use binder::{
- unstable_api::{AIBinder, AsNative},
- SpIBinder,
-};
-use std::{ffi::CString, os::raw, ptr::null_mut};
+use crate::session::FileDescriptorTransportMode;
+use binder::{unstable_api::AsNative, SpIBinder};
+use binder_rpc_unstable_bindgen::ARpcServer;
+use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::ffi::CString;
+use std::io::{Error, ErrorKind};
+use std::os::unix::io::{IntoRawFd, OwnedFd};
-/// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
-/// port.
-///
-/// If and when the server is ready for connections (it is listening on the port), `on_ready` is
-/// called to allow appropriate action to be taken - e.g. to notify clients that they may now
-/// attempt to connect.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_vsock_rpc_server<F>(service: SpIBinder, port: u32, on_ready: F) -> bool
-where
- F: FnOnce(),
-{
- let mut ready_notifier = ReadyNotifier(Some(on_ready));
- ready_notifier.run_vsock_server(service, port)
+foreign_type! {
+ type CType = binder_rpc_unstable_bindgen::ARpcServer;
+ fn drop = binder_rpc_unstable_bindgen::ARpcServer_free;
+
+ /// A type that represents a foreign instance of RpcServer.
+ #[derive(Debug)]
+ pub struct RpcServer;
+ /// A borrowed RpcServer.
+ pub struct RpcServerRef;
}
-/// Runs a binder RPC server, serving the supplied binder service implementation on the given
-/// socket file name. The socket should be initialized in init.rc with the same name.
-///
-/// If and when the server is ready for connections, `on_ready` is called to allow appropriate
-/// action to be taken - e.g. to notify clients that they may now attempt to connect.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_init_unix_domain_rpc_server<F>(
- service: SpIBinder,
- socket_name: &str,
- on_ready: F,
-) -> bool
-where
- F: FnOnce(),
-{
- let mut ready_notifier = ReadyNotifier(Some(on_ready));
- ready_notifier.run_init_unix_domain_server(service, socket_name)
-}
+/// SAFETY - The opaque handle can be cloned freely.
+unsafe impl Send for RpcServer {}
+/// SAFETY - The underlying C++ RpcServer class is thread-safe.
+unsafe impl Sync for RpcServer {}
-struct ReadyNotifier<F>(Option<F>)
-where
- F: FnOnce();
-
-impl<F> ReadyNotifier<F>
-where
- F: FnOnce(),
-{
- fn run_vsock_server(&mut self, mut service: SpIBinder, port: u32) -> bool {
+impl RpcServer {
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// vsock port. Only connections from the given CID are accepted.
+ ///
+ // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
+ // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
+ pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
let service = service.as_native_mut();
- let param = self.as_void_ptr();
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
- // RunVsockRpcServerCallback does not retain a reference to `ready_callback` or `param`; it only
- // uses them before it returns, which is during the lifetime of `self`.
unsafe {
- binder_rpc_unstable_bindgen::RunVsockRpcServerCallback(
- service,
- port,
- Some(Self::ready_callback),
- param,
- )
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
+ service, cid, port,
+ ))
}
}
- fn run_init_unix_domain_server(&mut self, mut service: SpIBinder, socket_name: &str) -> bool {
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// socket file name. The socket should be initialized in init.rc with the same name.
+ pub fn new_init_unix_domain(
+ mut service: SpIBinder,
+ socket_name: &str,
+ ) -> Result<RpcServer, Error> {
let socket_name = match CString::new(socket_name) {
Ok(s) => s,
Err(e) => {
log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
- return false;
+ return Err(Error::from(ErrorKind::InvalidInput));
}
};
let service = service.as_native_mut();
- let param = self.as_void_ptr();
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
- // RunInitUnixDomainRpcServer does not retain a reference to `ready_callback` or `param`;
- // it only uses them before it returns, which is during the lifetime of `self`.
unsafe {
- binder_rpc_unstable_bindgen::RunInitUnixDomainRpcServer(
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInitUnixDomain(
service,
socket_name.as_ptr(),
- Some(Self::ready_callback),
- param,
+ ))
+ }
+ }
+
+ /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket
+ /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix
+ /// domain sockets, pass one to the server and the other to the client. Multiple client session
+ /// can be created from the client end of the pair.
+ pub fn new_unix_domain_bootstrap(
+ mut service: SpIBinder,
+ bootstrap_fd: OwnedFd,
+ ) -> Result<RpcServer, Error> {
+ let service = service.as_native_mut();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ // The server takes ownership of the bootstrap FD.
+ unsafe {
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap(
+ service,
+ bootstrap_fd.into_raw_fd(),
+ ))
+ }
+ }
+
+ unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> {
+ if ptr.is_null() {
+ return Err(Error::new(ErrorKind::Other, "Failed to start server"));
+ }
+ Ok(RpcServer::from_ptr(ptr))
+ }
+}
+
+impl RpcServerRef {
+ /// Sets the list of file descriptor transport modes supported by this server.
+ pub fn set_supported_file_descriptor_transport_modes(
+ &self,
+ modes: &[FileDescriptorTransportMode],
+ ) {
+ // SAFETY - Does not keep the pointer after returning does, nor does it
+ // read past its boundary. Only passes the 'self' pointer as an opaque handle.
+ unsafe {
+ binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes(
+ self.as_ptr(),
+ modes.as_ptr(),
+ modes.len(),
)
}
}
- fn as_void_ptr(&mut self) -> *mut raw::c_void {
- self as *mut _ as *mut raw::c_void
+ /// Starts a new background thread and calls join(). Returns immediately.
+ pub fn start(&self) {
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
}
- unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
- // SAFETY: This is only ever called by `RunVsockRpcServerCallback`, within the lifetime of the
- // `ReadyNotifier`, with `param` taking the value returned by `as_void_ptr` (so a properly
- // aligned non-null pointer to an initialized instance).
- let ready_notifier = param as *mut Self;
- ready_notifier.as_mut().unwrap().notify()
+ /// Joins the RpcServer thread. The call blocks until the server terminates.
+ /// This must be called from exactly one thread.
+ pub fn join(&self) {
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) };
}
- fn notify(&mut self) {
- if let Some(on_ready) = self.0.take() {
- on_ready();
+ /// Shuts down the running RpcServer. Can be called multiple times and from
+ /// multiple threads. Called automatically during drop().
+ pub fn shutdown(&self) -> Result<(), Error> {
+ if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } {
+ Ok(())
+ } else {
+ Err(Error::from(ErrorKind::UnexpectedEof))
}
}
}
-
-type RpcServerFactoryRef<'a> = &'a mut (dyn FnMut(u32) -> Option<SpIBinder> + Send + Sync);
-
-/// Runs a binder RPC server, using the given factory function to construct a binder service
-/// implementation for each connection.
-///
-/// The current thread is joined to the binder thread pool to handle incoming messages.
-///
-/// Returns true if the server has shutdown normally, false if it failed in some way.
-pub fn run_vsock_rpc_server_with_factory(
- port: u32,
- mut factory: impl FnMut(u32) -> Option<SpIBinder> + Send + Sync,
-) -> bool {
- // Double reference the factory because trait objects aren't FFI safe.
- // NB: The type annotation is necessary to ensure that we have a `dyn` rather than an `impl`.
- let mut factory_ref: RpcServerFactoryRef = &mut factory;
- let context = &mut factory_ref as *mut RpcServerFactoryRef as *mut raw::c_void;
-
- // SAFETY: `factory_wrapper` is only ever called by `RunVsockRpcServerWithFactory`, with context
- // taking the pointer value above (so a properly aligned non-null pointer to an initialized
- // `RpcServerFactoryRef`), within the lifetime of `factory_ref` (i.e. no more calls will be made
- // after `RunVsockRpcServerWithFactory` returns).
- unsafe {
- binder_rpc_unstable_bindgen::RunVsockRpcServerWithFactory(
- Some(factory_wrapper),
- context,
- port,
- )
- }
-}
-
-unsafe extern "C" fn factory_wrapper(cid: u32, context: *mut raw::c_void) -> *mut AIBinder {
- // SAFETY: `context` was created from an `&mut RpcServerFactoryRef` by
- // `run_vsock_rpc_server_with_factory`, and we are still within the lifetime of the value it is
- // pointing to.
- let factory_ptr = context as *mut RpcServerFactoryRef;
- let factory = factory_ptr.as_mut().unwrap();
-
- if let Some(mut service) = factory(cid) {
- service.as_native_mut()
- } else {
- null_mut()
- }
-}
diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs
new file mode 100644
index 0000000..62fedb1
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/session.rs
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+use binder::unstable_api::new_spibinder;
+use binder::{FromIBinder, SpIBinder, StatusCode, Strong};
+use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::ffi::CString;
+use std::os::{
+ raw::{c_int, c_void},
+ unix::io::{AsRawFd, BorrowedFd, RawFd},
+};
+
+pub use binder_rpc_unstable_bindgen::ARpcSession_FileDescriptorTransportMode as FileDescriptorTransportMode;
+
+foreign_type! {
+ type CType = binder_rpc_unstable_bindgen::ARpcSession;
+ fn drop = binder_rpc_unstable_bindgen::ARpcSession_free;
+
+ /// A type that represents a foreign instance of RpcSession.
+ #[derive(Debug)]
+ pub struct RpcSession;
+ /// A borrowed RpcSession.
+ pub struct RpcSessionRef;
+}
+
+/// SAFETY - The opaque handle can be cloned freely.
+unsafe impl Send for RpcSession {}
+/// SAFETY - The underlying C++ RpcSession class is thread-safe.
+unsafe impl Sync for RpcSession {}
+
+impl RpcSession {
+ /// Allocates a new RpcSession object.
+ pub fn new() -> RpcSession {
+ // SAFETY - Takes ownership of the returned handle, which has correct refcount.
+ unsafe { RpcSession::from_ptr(binder_rpc_unstable_bindgen::ARpcSession_new()) }
+ }
+}
+
+impl Default for RpcSession {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl RpcSessionRef {
+ /// Sets the file descriptor transport mode for this session.
+ pub fn set_file_descriptor_transport_mode(&self, mode: FileDescriptorTransportMode) {
+ // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ unsafe {
+ binder_rpc_unstable_bindgen::ARpcSession_setFileDescriptorTransportMode(
+ self.as_ptr(),
+ mode,
+ )
+ };
+ }
+
+ /// Sets the maximum number of incoming threads.
+ pub fn set_max_incoming_threads(&self, threads: usize) {
+ // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ unsafe {
+ binder_rpc_unstable_bindgen::ARpcSession_setMaxIncomingThreads(self.as_ptr(), threads)
+ };
+ }
+
+ /// Sets the maximum number of outgoing threads.
+ pub fn set_max_outgoing_threads(&self, threads: usize) {
+ // SAFETY - Only passes the 'self' pointer as an opaque handle.
+ unsafe {
+ binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads)
+ };
+ }
+
+ /// Connects to an RPC Binder server over vsock for a particular interface.
+ pub fn setup_vsock_client<T: FromIBinder + ?Sized>(
+ &self,
+ cid: u32,
+ port: u32,
+ ) -> Result<Strong<T>, StatusCode> {
+ // SAFETY: AIBinder returned by ARpcSession_setupVsockClient has correct
+ // reference count, and the ownership can safely be taken by new_spibinder.
+ let service = unsafe {
+ new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupVsockClient(
+ self.as_ptr(),
+ cid,
+ port,
+ ))
+ };
+ Self::get_interface(service)
+ }
+
+ /// Connects to an RPC Binder server over a names Unix Domain Socket for
+ /// a particular interface.
+ pub fn setup_unix_domain_client<T: FromIBinder + ?Sized>(
+ &self,
+ socket_name: &str,
+ ) -> Result<Strong<T>, StatusCode> {
+ let socket_name = match CString::new(socket_name) {
+ Ok(s) => s,
+ Err(e) => {
+ log::error!("Cannot convert {} to CString. Error: {:?}", socket_name, e);
+ return Err(StatusCode::NAME_NOT_FOUND);
+ }
+ };
+
+ // SAFETY: AIBinder returned by ARpcSession_setupUnixDomainClient has correct
+ // reference count, and the ownership can safely be taken by new_spibinder.
+ let service = unsafe {
+ new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainClient(
+ self.as_ptr(),
+ socket_name.as_ptr(),
+ ))
+ };
+ Self::get_interface(service)
+ }
+
+ /// Connects to an RPC Binder server over a bootstrap Unix Domain Socket
+ /// for a particular interface.
+ pub fn setup_unix_domain_bootstrap_client<T: FromIBinder + ?Sized>(
+ &self,
+ bootstrap_fd: BorrowedFd,
+ ) -> Result<Strong<T>, StatusCode> {
+ // SAFETY: ARpcSession_setupUnixDomainBootstrapClient does not take
+ // ownership of bootstrap_fd. The returned AIBinder has correct
+ // reference count, and the ownership can safely be taken by new_spibinder.
+ let service = unsafe {
+ new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupUnixDomainBootstrapClient(
+ self.as_ptr(),
+ bootstrap_fd.as_raw_fd(),
+ ))
+ };
+ Self::get_interface(service)
+ }
+
+ /// Connects to an RPC Binder server, using the given callback to get (and
+ /// take ownership of) file descriptors already connected to it.
+ pub fn setup_preconnected_client<T: FromIBinder + ?Sized>(
+ &self,
+ mut request_fd: impl FnMut() -> Option<RawFd>,
+ ) -> Result<Strong<T>, StatusCode> {
+ // Double reference the factory because trait objects aren't FFI safe.
+ let mut request_fd_ref: RequestFd = &mut request_fd;
+ let param = &mut request_fd_ref as *mut RequestFd as *mut c_void;
+
+ // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and the
+ // ownership can be safely taken by new_spibinder. RpcPreconnectedClient does not take ownership
+ // of param, only passing it to request_fd_wrapper.
+ let service = unsafe {
+ new_spibinder(binder_rpc_unstable_bindgen::ARpcSession_setupPreconnectedClient(
+ self.as_ptr(),
+ Some(request_fd_wrapper),
+ param,
+ ))
+ };
+ Self::get_interface(service)
+ }
+
+ fn get_interface<T: FromIBinder + ?Sized>(
+ service: Option<SpIBinder>,
+ ) -> Result<Strong<T>, StatusCode> {
+ if let Some(service) = service {
+ FromIBinder::try_from(service)
+ } else {
+ Err(StatusCode::NAME_NOT_FOUND)
+ }
+ }
+}
+
+type RequestFd<'a> = &'a mut dyn FnMut() -> Option<RawFd>;
+
+unsafe extern "C" fn request_fd_wrapper(param: *mut c_void) -> c_int {
+ // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
+ // BinderFdFactory reference, with param being a properly aligned non-null pointer to an
+ // initialized instance.
+ let request_fd_ptr = param as *mut RequestFd;
+ let request_fd = request_fd_ptr.as_mut().unwrap();
+ request_fd().unwrap_or(-1)
+}
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index dee05d0..6f686fb 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -22,7 +22,6 @@
use crate::proxy::SpIBinder;
use crate::sys;
-use lazy_static::lazy_static;
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fs::File;
@@ -508,10 +507,8 @@
_private: (),
}
-lazy_static! {
- // Count of how many LazyServiceGuard objects are in existence.
- static ref GUARD_COUNT: Mutex<u64> = Mutex::new(0);
-}
+// Count of how many LazyServiceGuard objects are in existence.
+static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
impl LazyServiceGuard {
/// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index 28e0200..df8a2af 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -21,5 +21,7 @@
"waghpawan@google.com",
"smoreland@google.com",
],
+ // hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 43e407c..5cb406a 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -29,5 +29,7 @@
"waghpawan@google.com",
"smoreland@google.com",
],
+ // hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
index d2bfde1..a2d48b6 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -16,9 +16,9 @@
use binder::binder_impl::BorrowedParcel;
use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
-use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable;
-use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable;
-use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable;
+use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable;
macro_rules! read_parcel_interface {
($data_type:ty) => {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 25b524f..f7498c4 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -120,6 +120,7 @@
BINDER_LIB_TEST_CAN_GET_SID,
BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+ BINDER_LIB_TEST_IS_THREADPOOL_STARTED,
BINDER_LIB_TEST_LOCK_UNLOCK,
BINDER_LIB_TEST_PROCESS_LOCK,
BINDER_LIB_TEST_UNLOCK_AFTER_MS,
@@ -1383,6 +1384,14 @@
EXPECT_EQ(replyi, kKernelThreads + 1);
}
+TEST_F(BinderLibTest, ThreadPoolStarted) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR);
+ EXPECT_TRUE(reply.readBool());
+}
+
size_t epochMillis() {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
@@ -1849,6 +1858,10 @@
reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
return NO_ERROR;
}
+ case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: {
+ reply->writeBool(ProcessState::self()->isThreadPoolStarted());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_PROCESS_LOCK: {
m_blockMutex.lock();
return NO_ERROR;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 68a827b..739c217 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -913,6 +913,33 @@
EXPECT_EQ(status.transactionError(), BAD_VALUE) << status;
}
+TEST_P(BinderRpc, AppendInvalidFd) {
+ auto proc = createRpcTestSocketServerProcess({
+ .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
+ .serverSupportedFileDescriptorTransportModes =
+ {RpcSession::FileDescriptorTransportMode::UNIX},
+ });
+
+ int badFd = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 0);
+ ASSERT_NE(badFd, -1);
+
+ // Close the file descriptor so it becomes invalid for dup
+ close(badFd);
+
+ Parcel p1;
+ p1.markForBinder(proc.rootBinder);
+ p1.writeInt32(3);
+ EXPECT_EQ(OK, p1.writeFileDescriptor(badFd, false));
+
+ Parcel pRaw;
+ pRaw.markForBinder(proc.rootBinder);
+ EXPECT_EQ(OK, pRaw.appendFrom(&p1, 0, p1.dataSize()));
+
+ pRaw.setDataPosition(0);
+ EXPECT_EQ(3, pRaw.readInt32());
+ ASSERT_EQ(-1, pRaw.readFileDescriptor());
+}
+
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
if constexpr (!kEnableSharedLibs) {
GTEST_SKIP() << "Test disabled because Binder was built as a static library";
@@ -1315,7 +1342,7 @@
} break;
case SocketType::VSOCK: {
auto port = allocateVsockPort();
- auto status = rpcServer->setupVsockServer(port);
+ auto status = rpcServer->setupVsockServer(VMADDR_CID_LOCAL, port);
if (status != OK) {
return AssertionFailure() << "setupVsockServer: " << statusToString(status);
}
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 995e761..cc9726b 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -58,7 +58,7 @@
CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
break;
case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
+ CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort));
break;
case SocketType::INET: {
CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 61a2412..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -12,13 +12,14 @@
host_supported: true,
unstable: true,
srcs: [
- "EmptyParcelable.aidl",
- "SingleDataParcelable.aidl",
- "GenericDataParcelable.aidl",
+ "parcelables/EmptyParcelable.aidl",
+ "parcelables/SingleDataParcelable.aidl",
+ "parcelables/GenericDataParcelable.aidl",
],
backend: {
java: {
- enabled: false,
+ enabled: true,
+ platform_apis: true,
},
rust: {
enabled: true,
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
deleted file mode 100644
index 96d6223..0000000
--- a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
+++ /dev/null
@@ -1,18 +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.
- */
-
-parcelable EmptyParcelable{
-}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 9dac2c9..768fbe1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder"
#include "binder.h"
-#include "EmptyParcelable.h"
-#include "GenericDataParcelable.h"
-#include "SingleDataParcelable.h"
+#include "parcelables/EmptyParcelable.h"
+#include "parcelables/GenericDataParcelable.h"
+#include "parcelables/SingleDataParcelable.h"
#include "util.h"
#include <android-base/hex.h>
@@ -359,19 +359,19 @@
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
- EmptyParcelable emptyParcelable{};
+ parcelables::EmptyParcelable emptyParcelable{};
status_t status = emptyParcelable.readFromParcel(&p);
FUZZ_LOG() << " status: " << status;
},
[] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
- SingleDataParcelable singleDataParcelable;
+ parcelables::SingleDataParcelable singleDataParcelable;
status_t status = singleDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
- GenericDataParcelable genericDataParcelable;
+ parcelables::GenericDataParcelable genericDataParcelable;
status_t status = genericDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index af773a0..53e7de4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder_ndk"
#include "binder_ndk.h"
-#include "aidl/EmptyParcelable.h"
-#include "aidl/GenericDataParcelable.h"
-#include "aidl/SingleDataParcelable.h"
+#include "aidl/parcelables/EmptyParcelable.h"
+#include "aidl/parcelables/GenericDataParcelable.h"
+#include "aidl/parcelables/SingleDataParcelable.h"
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
@@ -183,19 +183,19 @@
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
- aidl::EmptyParcelable emptyParcelable;
+ aidl::parcelables::EmptyParcelable emptyParcelable;
binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
- aidl::SingleDataParcelable singleDataParcelable;
+ aidl::parcelables::SingleDataParcelable singleDataParcelable;
binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
- aidl::GenericDataParcelable genericDataParcelable;
+ aidl::parcelables::GenericDataParcelable genericDataParcelable;
binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 25f6096..8bef33f 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -18,6 +18,7 @@
#include <fuzzbinder/random_parcel.h>
#include <android-base/logging.h>
+#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
namespace android {
@@ -30,10 +31,19 @@
.extraFds = {},
};
+ if (provider.ConsumeBool()) {
+ // set calling uid
+ IPCThreadState::self()->restoreCallingIdentity(provider.ConsumeIntegral<int64_t>());
+ }
+
while (provider.remaining_bytes() > 0) {
- uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ // Most of the AIDL services will have small set of transaction codes.
+ uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+ : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
+ // for increased fuzz coverage
+ data.setEnforceNoDataAvail(provider.ConsumeBool());
sp<IBinder> target = options.extraBinders.at(
provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
@@ -50,6 +60,8 @@
fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
Parcel reply;
+ // for increased fuzz coverage
+ reply.setEnforceNoDataAvail(provider.ConsumeBool());
(void)target->transact(code, data, &reply, flags);
// feed back in binders and fds that are returned from the service, so that
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
similarity index 92%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
index d62891b..1216250 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-parcelable SingleDataParcelable{
- int data;
+package parcelables;
+parcelable EmptyParcelable {
}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
similarity index 97%
rename from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index fc2542b..f1079e9 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable GenericDataParcelable {
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
index d62891b..0187168 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable SingleDataParcelable{
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index edc695f..f0beed2 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -73,6 +73,11 @@
1));
CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
} else {
+ // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion
+ if (options->extraFds.size() > 1000) {
+ return;
+ }
+
std::vector<base::unique_fd> fds = getRandomFds(&provider);
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 58bfe71..d249b2e 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -239,6 +239,12 @@
}
if (!(uevt.event & IPC_HANDLE_POLL_MSG)) {
/* No message, terminate here and leave mHaveMessage false */
+ if (uevt.event & IPC_HANDLE_POLL_HUP) {
+ // Peer closed the connection. We need to preserve the order
+ // between MSG and HUP from FdTrigger.cpp, which means that
+ // getting MSG&HUP should return OK instead of DEAD_OBJECT.
+ return DEAD_OBJECT;
+ }
return OK;
}
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index bf877a3..de84617 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -120,3 +120,9 @@
do { \
TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
} while (0)
+
+// Override the definition of __assert from binder_status.h
+#ifndef __BIONIC__
+#undef __assert
+#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
+#endif // __BIONIC__
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
index 480ec79..1109ad8 100644
--- a/libs/fakeservicemanager/ServiceManager.cpp
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -36,6 +36,9 @@
status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
bool /*allowIsolated*/,
int /*dumpsysFlags*/) {
+ if (service == nullptr) {
+ return UNEXPECTED_NULL;
+ }
mNameToService[name] = service;
return NO_ERROR;
}
@@ -103,4 +106,8 @@
std::vector<IServiceManager::ServiceDebugInfo> ret;
return ret;
}
+
+void ServiceManager::clear() {
+ mNameToService.clear();
+}
} // namespace android
diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
index ee0637e..ba6bb7d 100644
--- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
+++ b/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h
@@ -64,6 +64,9 @@
std::vector<IServiceManager::ServiceDebugInfo> getServiceDebugInfo() override;
+ // Clear all of the registered services
+ void clear();
+
private:
std::map<String16, sp<IBinder>> mNameToService;
};
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 71e5abe..8682c1c 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -50,6 +50,12 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
+TEST(AddService, SadNullBinder) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL);
+}
+
TEST(AddService, HappyOverExistingService) {
auto sm = new ServiceManager();
EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
@@ -58,6 +64,15 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
}
+TEST(AddService, HappyClearAddedService) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+ EXPECT_NE(sm->getService(String16("foo")), nullptr);
+ sm->clear();
+ EXPECT_EQ(sm->getService(String16("foo")), nullptr);
+}
+
TEST(GetService, HappyHappy) {
auto sm = new ServiceManager();
sp<IBinder> service = getBinder();
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index df0b271..8e57152 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -22,6 +22,7 @@
"flags_test.cpp",
"future_test.cpp",
"match_test.cpp",
+ "mixins_test.cpp",
"non_null_test.cpp",
"optional_test.cpp",
"shared_mutex_test.cpp",
diff --git a/libs/ftl/mixins_test.cpp b/libs/ftl/mixins_test.cpp
new file mode 100644
index 0000000..2c9f9df
--- /dev/null
+++ b/libs/ftl/mixins_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ftl/mixins.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace android::test {
+namespace {
+
+// Keep in sync with example usage in header file.
+
+struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
+ using Constructible::Constructible;
+};
+
+static_assert(!std::is_default_constructible_v<Id>);
+
+struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
+ ftl::Equatable<Color>,
+ ftl::Orderable<Color> {
+ using DefaultConstructible::DefaultConstructible;
+};
+
+static_assert(Color() == Color(0u));
+static_assert(ftl::to_underlying(Color(-1)) == 255u);
+static_assert(Color(1u) < Color(2u));
+
+struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
+ ftl::Equatable<Sequence>,
+ ftl::Orderable<Sequence>,
+ ftl::Incrementable<Sequence> {
+ using DefaultConstructible::DefaultConstructible;
+};
+
+static_assert(Sequence() == Sequence(-1));
+
+struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
+ ftl::Equatable<Timeout>,
+ ftl::Addable<Timeout> {
+ using DefaultConstructible::DefaultConstructible;
+};
+
+using namespace std::chrono_literals;
+static_assert(Timeout() + Timeout(5s) == Timeout(15s));
+
+// Construction.
+constexpr Id kId{1234};
+constexpr Sequence kSequence;
+
+// Underlying value.
+static_assert(ftl::to_underlying(Id(-42)) == -42);
+static_assert(ftl::to_underlying(kSequence) == -1);
+
+// Casting.
+static_assert(static_cast<std::int32_t>(Id(-1)) == -1);
+static_assert(static_cast<std::int8_t>(kSequence) == -1);
+
+static_assert(!std::is_convertible_v<std::int32_t, Id>);
+static_assert(!std::is_convertible_v<Id, std::int32_t>);
+
+// Equality.
+static_assert(kId == Id(1234));
+static_assert(kId != Id(123));
+static_assert(kSequence == Sequence(-1));
+
+// Ordering.
+static_assert(Sequence(1) < Sequence(2));
+static_assert(Sequence(2) > Sequence(1));
+static_assert(Sequence(3) <= Sequence(4));
+static_assert(Sequence(4) >= Sequence(3));
+static_assert(Sequence(5) <= Sequence(5));
+static_assert(Sequence(6) >= Sequence(6));
+
+// Incrementing.
+template <typename Op, typename T, typename... Ts>
+constexpr auto mutable_op(Op op, T lhs, Ts... rhs) {
+ const T result = op(lhs, rhs...);
+ return std::make_pair(lhs, result);
+}
+
+static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Sequence()) ==
+ std::make_pair(Sequence(0), Sequence(0)));
+
+static_assert(mutable_op([](auto& lhs) { return lhs++; }, Sequence()) ==
+ std::make_pair(Sequence(0), Sequence(-1)));
+
+// Addition.
+
+// `Addable` implies `Incrementable`.
+static_assert(mutable_op([](auto& lhs) { return ++lhs; }, Timeout()) ==
+ std::make_pair(Timeout(11s), Timeout(11s)));
+
+static_assert(mutable_op([](auto& lhs) { return lhs++; }, Timeout()) ==
+ std::make_pair(Timeout(11s), Timeout(10s)));
+
+static_assert(Timeout(5s) + Timeout(6s) == Timeout(11s));
+
+static_assert(mutable_op([](auto& lhs, const auto& rhs) { return lhs += rhs; }, Timeout(7s),
+ Timeout(8s)) == std::make_pair(Timeout(15s), Timeout(15s)));
+
+// Type safety.
+
+namespace traits {
+
+template <typename, typename = void>
+struct is_incrementable : std::false_type {};
+
+template <typename T>
+struct is_incrementable<T, std::void_t<decltype(++std::declval<T&>())>> : std::true_type {};
+
+template <typename T>
+constexpr bool is_incrementable_v = is_incrementable<T>{};
+
+template <typename, typename, typename, typename = void>
+struct has_binary_op : std::false_type {};
+
+template <typename Op, typename T, typename U>
+struct has_binary_op<Op, T, U, std::void_t<decltype(Op{}(std::declval<T&>(), std::declval<U&>()))>>
+ : std::true_type {};
+
+template <typename T, typename U>
+constexpr bool is_equatable_v =
+ has_binary_op<std::equal_to<void>, T, U>{} && has_binary_op<std::not_equal_to<void>, T, U>{};
+
+template <typename T, typename U>
+constexpr bool is_orderable_v =
+ has_binary_op<std::less<void>, T, U>{} && has_binary_op<std::less_equal<void>, T, U>{} &&
+ has_binary_op<std::greater<void>, T, U>{} && has_binary_op<std::greater_equal<void>, T, U>{};
+
+template <typename T, typename U>
+constexpr bool is_addable_v = has_binary_op<std::plus<void>, T, U>{};
+
+} // namespace traits
+
+struct Real : ftl::Constructible<Real, float> {
+ using Constructible::Constructible;
+};
+
+static_assert(traits::is_equatable_v<Id, Id>);
+static_assert(!traits::is_equatable_v<Real, Real>);
+static_assert(!traits::is_equatable_v<Id, Color>);
+static_assert(!traits::is_equatable_v<Sequence, Id>);
+static_assert(!traits::is_equatable_v<Id, std::int32_t>);
+static_assert(!traits::is_equatable_v<std::chrono::seconds, Timeout>);
+
+static_assert(traits::is_orderable_v<Color, Color>);
+static_assert(!traits::is_orderable_v<Id, Id>);
+static_assert(!traits::is_orderable_v<Real, Real>);
+static_assert(!traits::is_orderable_v<Color, Sequence>);
+static_assert(!traits::is_orderable_v<Color, std::uint8_t>);
+static_assert(!traits::is_orderable_v<std::chrono::seconds, Timeout>);
+
+static_assert(traits::is_incrementable_v<Sequence>);
+static_assert(traits::is_incrementable_v<Timeout>);
+static_assert(!traits::is_incrementable_v<Id>);
+static_assert(!traits::is_incrementable_v<Color>);
+static_assert(!traits::is_incrementable_v<Real>);
+
+static_assert(traits::is_addable_v<Timeout, Timeout>);
+static_assert(!traits::is_addable_v<Id, Id>);
+static_assert(!traits::is_addable_v<Real, Real>);
+static_assert(!traits::is_addable_v<Sequence, Sequence>);
+static_assert(!traits::is_addable_v<Timeout, Sequence>);
+static_assert(!traits::is_addable_v<Color, Timeout>);
+
+} // namespace
+} // namespace android::test
diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
index f7410c2..6b3b6c4 100644
--- a/libs/ftl/optional_test.cpp
+++ b/libs/ftl/optional_test.cpp
@@ -164,4 +164,35 @@
}));
}
+// Comparison.
+namespace {
+
+constexpr Optional<int> kOptional1 = 1;
+constexpr Optional<int> kAnotherOptional1 = 1;
+constexpr Optional<int> kOptional2 = 2;
+constexpr Optional<int> kOptionalEmpty, kAnotherOptionalEmpty;
+
+constexpr std::optional<int> kStdOptional1 = 1;
+
+static_assert(kOptional1 == kAnotherOptional1);
+
+static_assert(kOptional1 != kOptional2);
+static_assert(kOptional2 != kOptional1);
+
+static_assert(kOptional1 != kOptionalEmpty);
+static_assert(kOptionalEmpty != kOptional1);
+
+static_assert(kOptionalEmpty == kAnotherOptionalEmpty);
+
+static_assert(kOptional1 == kStdOptional1);
+static_assert(kStdOptional1 == kOptional1);
+
+static_assert(kOptional2 != kStdOptional1);
+static_assert(kStdOptional1 != kOptional2);
+
+static_assert(kOptional2 != kOptionalEmpty);
+static_assert(kOptionalEmpty != kOptional2);
+
+} // namespace
+
} // namespace android::test
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 858739c..7b74214 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -89,6 +89,14 @@
if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
if ((status = parcel->writeBool(angleInUse)) != OK) return status;
+ if ((status = parcel->writeBool(createdGlesContext)) != OK) return status;
+ if ((status = parcel->writeBool(createdVulkanDevice)) != OK) return status;
+ if ((status = parcel->writeBool(createdVulkanSwapchain)) != OK) return status;
+ if ((status = parcel->writeUint32(vulkanApiVersion)) != OK) return status;
+ if ((status = parcel->writeUint64(vulkanDeviceFeaturesEnabled)) != OK) return status;
+ if ((status = parcel->writeInt32Vector(vulkanInstanceExtensions)) != OK) return status;
+ if ((status = parcel->writeInt32Vector(vulkanDeviceExtensions)) != OK) return status;
+
return OK;
}
@@ -103,6 +111,14 @@
if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
if ((status = parcel->readBool(&angleInUse)) != OK) return status;
+ if ((status = parcel->readBool(&createdGlesContext)) != OK) return status;
+ if ((status = parcel->readBool(&createdVulkanDevice)) != OK) return status;
+ if ((status = parcel->readBool(&createdVulkanSwapchain)) != OK) return status;
+ if ((status = parcel->readUint32(&vulkanApiVersion)) != OK) return status;
+ if ((status = parcel->readUint64(&vulkanDeviceFeaturesEnabled)) != OK) return status;
+ if ((status = parcel->readInt32Vector(&vulkanInstanceExtensions)) != OK) return status;
+ if ((status = parcel->readInt32Vector(&vulkanDeviceExtensions)) != OK) return status;
+
return OK;
}
@@ -114,6 +130,12 @@
StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
StringAppendF(&result, "angleInUse = %d\n", angleInUse);
+ StringAppendF(&result, "createdGlesContext = %d\n", createdGlesContext);
+ StringAppendF(&result, "createdVulkanDevice = %d\n", createdVulkanDevice);
+ StringAppendF(&result, "createdVulkanSwapchain = %d\n", createdVulkanSwapchain);
+ StringAppendF(&result, "vulkanApiVersion = 0x%" PRIx32 "\n", vulkanApiVersion);
+ StringAppendF(&result, "vulkanDeviceFeaturesEnabled = 0x%" PRIx64 "\n",
+ vulkanDeviceFeaturesEnabled);
result.append("glDriverLoadingTime:");
for (int32_t loadingTime : glDriverLoadingTime) {
StringAppendF(&result, " %d", loadingTime);
@@ -129,6 +151,16 @@
StringAppendF(&result, " %d", loadingTime);
}
result.append("\n");
+ result.append("vulkanInstanceExtensions:");
+ for (int32_t extension : vulkanInstanceExtensions) {
+ StringAppendF(&result, " 0x%x", extension);
+ }
+ result.append("\n");
+ result.append("vulkanDeviceExtensions:");
+ for (int32_t extension : vulkanDeviceExtensions) {
+ StringAppendF(&result, " 0x%x", extension);
+ }
+ result.append("\n");
return result;
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 5f5f85a..46dd62d 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -259,6 +259,57 @@
sendGpuStatsLocked(api, isDriverLoaded, driverLoadingTime);
}
+// Hash function to calculate hash for null-terminated Vulkan extension names
+// We store hash values of the extensions, rather than the actual names or
+// indices to be able to support new extensions easily, avoid creating
+// a table of 'known' extensions inside Android and reduce the runtime overhead.
+static uint64_t calculateExtensionHash(const char* word) {
+ if (!word) {
+ return 0;
+ }
+ const size_t wordLen = strlen(word);
+ const uint32_t seed = 167;
+ uint64_t hash = 0;
+ for (size_t i = 0; i < wordLen; i++) {
+ hash = (hash * seed) + word[i];
+ }
+ return hash;
+}
+
+void GraphicsEnv::setVulkanInstanceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames) {
+ ATRACE_CALL();
+ if (enabledExtensionCount == 0 || ppEnabledExtensionNames == nullptr) {
+ return;
+ }
+
+ const uint32_t maxNumStats = android::GpuStatsAppInfo::MAX_NUM_EXTENSIONS;
+ uint64_t extensionHashes[maxNumStats];
+ const uint32_t numStats = std::min(enabledExtensionCount, maxNumStats);
+ for(uint32_t i = 0; i < numStats; i++) {
+ extensionHashes[i] = calculateExtensionHash(ppEnabledExtensionNames[i]);
+ }
+ setTargetStatsArray(android::GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ extensionHashes, numStats);
+}
+
+void GraphicsEnv::setVulkanDeviceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames) {
+ ATRACE_CALL();
+ if (enabledExtensionCount == 0 || ppEnabledExtensionNames == nullptr) {
+ return;
+ }
+
+ const uint32_t maxNumStats = android::GpuStatsAppInfo::MAX_NUM_EXTENSIONS;
+ uint64_t extensionHashes[maxNumStats];
+ const uint32_t numStats = std::min(enabledExtensionCount, maxNumStats);
+ for(uint32_t i = 0; i < numStats; i++) {
+ extensionHashes[i] = calculateExtensionHash(ppEnabledExtensionNames[i]);
+ }
+ setTargetStatsArray(android::GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ extensionHashes, numStats);
+}
+
static sp<IGpuService> getGpuService() {
static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
if (!binder) {
@@ -276,6 +327,11 @@
}
void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) {
+ return setTargetStatsArray(stats, &value, 1);
+}
+
+void GraphicsEnv::setTargetStatsArray(const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
@@ -283,8 +339,8 @@
const sp<IGpuService> gpuService = getGpuService();
if (gpuService) {
- gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats,
- value);
+ gpuService->setTargetStatsArray(mGpuStats.appPackageName, mGpuStats.driverVersionCode,
+ stats, values, valueCount);
}
}
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index fa25c55..ceb52f7 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -61,6 +61,14 @@
remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
+ 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]);
+ }
+ }
+
void setUpdatableDriverPath(const std::string& driverPath) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 5b513d2..47607a0 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -58,6 +58,9 @@
*/
class GpuStatsAppInfo : public Parcelable {
public:
+ // This limits the worst case number of extensions to be tracked.
+ static const uint32_t MAX_NUM_EXTENSIONS = 100;
+
GpuStatsAppInfo() = default;
GpuStatsAppInfo(const GpuStatsAppInfo&) = default;
virtual ~GpuStatsAppInfo() = default;
@@ -74,6 +77,13 @@
bool falsePrerotation = false;
bool gles1InUse = false;
bool angleInUse = false;
+ bool createdGlesContext = false;
+ bool createdVulkanDevice = false;
+ bool createdVulkanSwapchain = false;
+ uint32_t vulkanApiVersion = 0;
+ uint64_t vulkanDeviceFeaturesEnabled = 0;
+ std::vector<int32_t> vulkanInstanceExtensions = {};
+ std::vector<int32_t> vulkanDeviceExtensions = {};
std::chrono::time_point<std::chrono::system_clock> lastAccessTime;
};
@@ -101,6 +111,13 @@
CPU_VULKAN_IN_USE = 0,
FALSE_PREROTATION = 1,
GLES_1_IN_USE = 2,
+ CREATED_GLES_CONTEXT = 3,
+ CREATED_VULKAN_API_VERSION = 4,
+ CREATED_VULKAN_DEVICE = 5,
+ CREATED_VULKAN_SWAPCHAIN = 6,
+ VULKAN_DEVICE_FEATURES_ENABLED = 7,
+ VULKAN_INSTANCE_EXTENSION = 8,
+ VULKAN_DEVICE_EXTENSION = 9,
};
GpuStatsInfo() = default;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 73d3196..b58a6d9 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -71,10 +71,19 @@
const std::string& appPackageName, const int32_t vulkanVersion);
// Set stats for target GpuStatsInfo::Stats type.
void setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value = 0);
+ // Set array of stats for target GpuStatsInfo::Stats type.
+ void setTargetStatsArray(const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount);
// Set which driver is intended to load.
void setDriverToLoad(GpuStatsInfo::Driver driver);
// Set which driver is actually loaded.
void setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+ // Set which instance extensions are enabled for the app.
+ void setVulkanInstanceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames);
+ // Set which device extensions are enabled for the app.
+ void setVulkanDeviceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames);
/*
* Api for Vk/GL layer injection. Presently, drivers enable certain
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index 2d59fa0..b708b0f 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -42,6 +42,10 @@
// set target stats.
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
+ virtual void setTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode,
+ const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount) = 0;
// setter and getter for updatable driver path.
virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index a988e39..6c9c28a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -192,6 +192,7 @@
"BitTube.cpp",
"BLASTBufferQueue.cpp",
"BufferItemConsumer.cpp",
+ "Choreographer.cpp",
"CompositorTiming.cpp",
"ConsumerBase.cpp",
"CpuConsumer.cpp",
@@ -234,6 +235,7 @@
export_header_lib_headers: [
"libgui_aidl_headers",
+ "jni_headers",
],
aidl: {
@@ -241,6 +243,7 @@
},
header_libs: [
+ "jni_headers",
"libdvr_headers",
"libgui_aidl_headers",
"libpdx_headers",
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
new file mode 100644
index 0000000..6b25b26
--- /dev/null
+++ b/libs/gui/Choreographer.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+
+#include <gui/Choreographer.h>
+#include <jni.h>
+
+#undef LOG_TAG
+#define LOG_TAG "AChoreographer"
+
+namespace {
+struct {
+ // Global JVM that is provided by zygote
+ JavaVM* jvm = nullptr;
+ struct {
+ jclass clazz;
+ jmethodID getInstance;
+ jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+ jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+ } displayManagerGlobal;
+} gJni;
+
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+ if (gJni.jvm == nullptr) {
+ ALOGW("AChoreographer: No JVM provided!");
+ return nullptr;
+ }
+
+ JNIEnv* env = nullptr;
+ if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGD("Attaching thread to JVM for AChoreographer");
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+ jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+ if (attachResult != JNI_OK) {
+ ALOGE("Unable to attach thread. Error: %d", attachResult);
+ return nullptr;
+ }
+ }
+ if (env == nullptr) {
+ ALOGW("AChoreographer: No JNI env available!");
+ }
+ return env;
+}
+
+inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+} // namespace
+
+namespace android {
+
+Choreographer::Context Choreographer::gChoreographers;
+
+static thread_local Choreographer* gChoreographer;
+
+void Choreographer::initJVM(JNIEnv* env) {
+ env->GetJavaVM(&gJni.jvm);
+ // Now we need to find the java classes.
+ jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+ gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+ gJni.displayManagerGlobal.getInstance =
+ env->GetStaticMethodID(dmgClass, "getInstance",
+ "()Landroid/hardware/display/DisplayManagerGlobal;");
+ gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+ gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+ env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+ "()V");
+}
+
+Choreographer* Choreographer::getForThread() {
+ if (gChoreographer == nullptr) {
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ ALOGW("No looper prepared for thread");
+ return nullptr;
+ }
+ gChoreographer = new Choreographer(looper);
+ status_t result = gChoreographer->initialize();
+ if (result != OK) {
+ ALOGW("Failed to initialize");
+ return nullptr;
+ }
+ }
+ return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper)
+ : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
+ mLooper(looper),
+ mThreadId(std::this_thread::get_id()) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+ gChoreographers.ptrs.end(),
+ [=](Choreographer* c) { return c == this; }),
+ gChoreographers.ptrs.end());
+ // Only poke DisplayManagerGlobal to unregister if we previously registered
+ // callbacks.
+ if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ gChoreographers.registeredToDisplayManager = false;
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .unregisterNativeChoreographerForRefreshRateCallbacks);
+ env->DeleteLocalRef(dmg);
+ }
+ }
+}
+
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_vsyncCallback vsyncCallback, void* data,
+ nsecs_t delay) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ mFrameCallbacks.push(callback);
+ }
+ if (callback.dueTime <= now) {
+ if (std::this_thread::get_id() != mThreadId) {
+ if (mLooper != nullptr) {
+ Message m{MSG_SCHEDULE_VSYNC};
+ mLooper->sendMessage(this, m);
+ } else {
+ scheduleVsync();
+ }
+ } else {
+ scheduleVsync();
+ }
+ } else {
+ if (mLooper != nullptr) {
+ Message m{MSG_SCHEDULE_CALLBACKS};
+ mLooper->sendMessageDelayed(delay, this, m);
+ } else {
+ scheduleCallbacks();
+ }
+ }
+}
+
+void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (const auto& callback : mRefreshRateCallbacks) {
+ // Don't re-add callbacks.
+ if (cb == callback.callback && data == callback.data) {
+ return;
+ }
+ }
+ mRefreshRateCallbacks.emplace_back(
+ RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+ bool needsRegistration = false;
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ needsRegistration = !gChoreographers.registeredToDisplayManager;
+ }
+ if (needsRegistration) {
+ JNIEnv* env = getJniEnv();
+ if (env == nullptr) {
+ ALOGW("JNI environment is unavailable, skipping registration");
+ return;
+ }
+ jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+ gJni.displayManagerGlobal.getInstance);
+ if (dmg == nullptr) {
+ ALOGW("DMS is not initialized yet: skipping registration");
+ return;
+ } else {
+ env->CallVoidMethod(dmg,
+ gJni.displayManagerGlobal
+ .registerNativeChoreographerForRefreshRateCallbacks,
+ reinterpret_cast<int64_t>(this));
+ env->DeleteLocalRef(dmg);
+ {
+ std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+ gChoreographers.registeredToDisplayManager = true;
+ }
+ }
+ } else {
+ scheduleLatestConfigRequest();
+ }
+}
+
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+ void* data) {
+ std::lock_guard<std::mutex> _l{mLock};
+ mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+ mRefreshRateCallbacks.end(),
+ [&](const RefreshRateCallback& callback) {
+ return cb == callback.callback &&
+ data == callback.data;
+ }),
+ mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+ if (mLooper != nullptr) {
+ Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+ mLooper->sendMessage(this, m);
+ } else {
+ // If the looper thread is detached from Choreographer, then refresh rate
+ // changes will be handled in AChoreographer_handlePendingEvents, so we
+ // need to wake up the looper thread by writing to the write-end of the
+ // socket the looper is listening on.
+ // Fortunately, these events are small so sending packets across the
+ // socket should be atomic across processes.
+ DisplayEventReceiver::Event event;
+ event.header =
+ DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ PhysicalDisplayId::fromPort(0), systemTime()};
+ injectEvent(event);
+ }
+}
+
+void Choreographer::scheduleCallbacks() {
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t dueTime;
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ // If there are no pending callbacks then don't schedule a vsync
+ if (mFrameCallbacks.empty()) {
+ return;
+ }
+ dueTime = mFrameCallbacks.top().dueTime;
+ }
+
+ if (dueTime <= now) {
+ ALOGV("choreographer %p ~ scheduling vsync", this);
+ scheduleVsync();
+ return;
+ }
+}
+
+void Choreographer::handleRefreshRateUpdates() {
+ std::vector<RefreshRateCallback> callbacks{};
+ const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+ const nsecs_t lastPeriod = mLatestVsyncPeriod;
+ if (pendingPeriod > 0) {
+ mLatestVsyncPeriod = pendingPeriod;
+ }
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ for (auto& cb : mRefreshRateCallbacks) {
+ callbacks.push_back(cb);
+ cb.firstCallbackFired = true;
+ }
+ }
+
+ for (auto& cb : callbacks) {
+ if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+ cb.callback(pendingPeriod, cb.data);
+ }
+ }
+}
+
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+ VsyncEventData vsyncEventData) {
+ std::vector<FrameCallback> callbacks{};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+ callbacks.push_back(mFrameCallbacks.top());
+ mFrameCallbacks.pop();
+ }
+ }
+ mLastVsyncEventData = vsyncEventData;
+ for (const auto& cb : callbacks) {
+ if (cb.vsyncCallback != nullptr) {
+ const ChoreographerFrameCallbackDataImpl frameCallbackData =
+ createFrameCallbackData(timestamp);
+ registerStartTime();
+ mInCallback = true;
+ cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+ &frameCallbackData),
+ cb.data);
+ mInCallback = false;
+ } else if (cb.callback64 != nullptr) {
+ cb.callback64(timestamp, cb.data);
+ } else if (cb.callback != nullptr) {
+ cb.callback(timestamp, cb.data);
+ }
+ }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+ to_string(displayId).c_str(), toString(connected));
+}
+
+void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
+ LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
+}
+
+void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
+ std::vector<FrameRateOverride>) {
+ LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
+}
+
+void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
+ ALOGV("choreographer %p ~ received null event.", this);
+ handleRefreshRateUpdates();
+}
+
+void Choreographer::handleMessage(const Message& message) {
+ switch (message.what) {
+ case MSG_SCHEDULE_CALLBACKS:
+ scheduleCallbacks();
+ break;
+ case MSG_SCHEDULE_VSYNC:
+ scheduleVsync();
+ break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
+ }
+}
+
+int64_t Choreographer::getFrameInterval() const {
+ return mLastVsyncEventData.frameInterval;
+}
+
+bool Choreographer::inCallback() const {
+ return mInCallback;
+}
+
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+ return {.frameTimeNanos = timestamp,
+ .vsyncEventData = mLastVsyncEventData,
+ .choreographer = this};
+}
+
+void Choreographer::registerStartTime() const {
+ std::scoped_lock _l(gChoreographers.lock);
+ for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
+ while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
+ gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
+ }
+ gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+}
+
+void Choreographer::signalRefreshRateCallbacks(nsecs_t vsyncPeriod) {
+ std::lock_guard<std::mutex> _l(gChoreographers.lock);
+ gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+ for (auto c : gChoreographers.ptrs) {
+ c->scheduleLatestConfigRequest();
+ }
+}
+
+int64_t Choreographer::getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
+ std::scoped_lock _l(gChoreographers.lock);
+ const auto iter = gChoreographers.startTimes.find(vsyncId);
+ if (iter == gChoreographers.startTimes.end()) {
+ ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
+ return 0;
+ }
+ return iter->second;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..a0e75ff 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -42,6 +42,7 @@
namespace android {
+using gui::CallbackId;
using gui::DisplayCaptureArgs;
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2b25b61..23d7d50 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -21,22 +21,11 @@
#include <optional>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
#include <private/gui/ParcelUtils.h>
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
- ON_RELEASE_BUFFER,
- ON_TRANSACTION_QUEUE_STALLED,
- LAST = ON_TRANSACTION_QUEUE_STALLED,
-};
-
-} // Anonymous namespace
+namespace android::gui {
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t err = output->writeUint64(frameNumber);
@@ -274,60 +263,6 @@
return listenerStats;
}
-class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
-public:
- explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
- : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
- }
-
- ~BpTransactionCompletedListener() override;
-
- void onTransactionCompleted(ListenerStats stats) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
- stats);
- }
-
- void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId,
- releaseFence,
- currentMaxAcquiredBufferCount);
- }
-
- void onTransactionQueueStalled(const String8& reason) override {
- callRemoteAsync<
- decltype(&ITransactionCompletedListener::
- onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
- reason);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
-
-IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
-
-status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_TRANSACTION_COMPLETED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionCompleted);
- case Tag::ON_RELEASE_BUFFER:
- return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
- case Tag::ON_TRANSACTION_QUEUE_STALLED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionQueueStalled);
- }
-}
-
ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
std::vector<CallbackId> filteredCallbackIds;
for (const auto& callbackId : callbackIds) {
@@ -366,4 +301,4 @@
const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
-}; // namespace android
+}; // namespace android::gui
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 924e65e..0d1a69b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -24,12 +24,34 @@
#include <binder/Parcel.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/LayerState.h>
+#include <gui/SurfaceControl.h>
#include <private/gui/ParcelUtils.h>
#include <system/window.h>
#include <utils/Errors.h>
+#define CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD) \
+ { \
+ if ((OTHER.what & CHANGE_FLAG) && (FIELD != OTHER.FIELD)) { \
+ DIFF_RESULT |= CHANGE_FLAG; \
+ } \
+ }
+
+#define CHECK_DIFF2(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2) \
+ { \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \
+ }
+
+#define CHECK_DIFF3(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1, FIELD2, FIELD3) \
+ { \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD1) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD2) \
+ CHECK_DIFF(DIFF_RESULT, CHANGE_FLAG, OTHER, FIELD3) \
+ }
+
namespace android {
+using gui::CallbackId;
using gui::FocusRequest;
using gui::WindowInfoHandle;
@@ -367,6 +389,27 @@
}
}
+void DisplayState::sanitize(int32_t permissions) {
+ if (what & DisplayState::eLayerStackChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eLayerStackChanged;
+ ALOGE("Stripped attempt to set eLayerStackChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eDisplayProjectionChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eDisplayProjectionChanged;
+ ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eSurfaceChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eSurfaceChanged;
+ ALOGE("Stripped attempt to set eSurfaceChanged in sanitize");
+ }
+ }
+}
+
void layer_state_t::sanitize(int32_t permissions) {
// TODO: b/109894387
//
@@ -626,6 +669,74 @@
}
}
+uint64_t layer_state_t::diff(const layer_state_t& other) const {
+ uint64_t diff = 0;
+ CHECK_DIFF2(diff, ePositionChanged, other, x, y);
+ if (other.what & eLayerChanged) {
+ diff |= eLayerChanged;
+ diff &= ~eRelativeLayerChanged;
+ }
+ CHECK_DIFF(diff, eAlphaChanged, other, color.a);
+ CHECK_DIFF(diff, eMatrixChanged, other, matrix);
+ if (other.what & eTransparentRegionChanged &&
+ (!transparentRegion.hasSameRects(other.transparentRegion))) {
+ diff |= eTransparentRegionChanged;
+ }
+ if (other.what & eFlagsChanged) {
+ uint64_t changedFlags = (flags & other.mask) ^ (other.flags & other.mask);
+ if (changedFlags) diff |= eFlagsChanged;
+ }
+ CHECK_DIFF(diff, eLayerStackChanged, other, layerStack);
+ CHECK_DIFF(diff, eCornerRadiusChanged, other, cornerRadius);
+ CHECK_DIFF(diff, eBackgroundBlurRadiusChanged, other, backgroundBlurRadius);
+ if (other.what & eBlurRegionsChanged) diff |= eBlurRegionsChanged;
+ if (other.what & eRelativeLayerChanged) {
+ diff |= eRelativeLayerChanged;
+ diff &= ~eLayerChanged;
+ }
+ if (other.what & eReparent &&
+ !SurfaceControl::isSameSurface(parentSurfaceControlForChild,
+ other.parentSurfaceControlForChild)) {
+ diff |= eReparent;
+ }
+ CHECK_DIFF(diff, eBufferTransformChanged, other, bufferTransform);
+ CHECK_DIFF(diff, eTransformToDisplayInverseChanged, other, transformToDisplayInverse);
+ CHECK_DIFF(diff, eCropChanged, other, crop);
+ if (other.what & eBufferChanged) diff |= eBufferChanged;
+ CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
+ CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
+ if (other.what & eSurfaceDamageRegionChanged &&
+ (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
+ diff |= eSurfaceDamageRegionChanged;
+ }
+ CHECK_DIFF(diff, eApiChanged, other, api);
+ if (other.what & eSidebandStreamChanged) diff |= eSidebandStreamChanged;
+ CHECK_DIFF(diff, eApiChanged, other, api);
+ CHECK_DIFF(diff, eColorTransformChanged, other, colorTransform);
+ if (other.what & eHasListenerCallbacksChanged) diff |= eHasListenerCallbacksChanged;
+ if (other.what & eInputInfoChanged) diff |= eInputInfoChanged;
+ CHECK_DIFF3(diff, eBackgroundColorChanged, other, color.rgb, bgColorAlpha, bgColorDataspace);
+ if (other.what & eMetadataChanged) diff |= eMetadataChanged;
+ CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
+ CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor);
+ CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
+ CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
+ CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
+ changeFrameRateStrategy);
+ CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
+ CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
+ CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
+ CHECK_DIFF(diff, eStretchChanged, other, stretchEffect);
+ CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop);
+ CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame);
+ if (other.what & eProducerDisconnect) diff |= eProducerDisconnect;
+ CHECK_DIFF(diff, eDropInputModeChanged, other, dropInputMode);
+ CHECK_DIFF(diff, eColorChanged, other, color.rgb);
+ CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
+ CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
+ return diff;
+}
+
bool layer_state_t::hasBufferChanges() const {
return what & layer_state_t::eBufferChanged;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c4fb1cf..edb18a8 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1108,9 +1108,12 @@
ATRACE_CALL();
auto& mapper = GraphicBufferMapper::get();
mapper.setDataspace(buffer->handle, static_cast<ui::Dataspace>(queueBufferInput.dataSpace));
- mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086());
- mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613());
- mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus());
+ if (mHdrMetadataIsSet & HdrMetadata::SMPTE2086)
+ mapper.setSmpte2086(buffer->handle, queueBufferInput.getHdrMetadata().getSmpte2086());
+ if (mHdrMetadataIsSet & HdrMetadata::CTA861_3)
+ mapper.setCta861_3(buffer->handle, queueBufferInput.getHdrMetadata().getCta8613());
+ if (mHdrMetadataIsSet & HdrMetadata::HDR10PLUS)
+ mapper.setSmpte2094_40(buffer->handle, queueBufferInput.getHdrMetadata().getHdr10Plus());
}
void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence,
@@ -2252,6 +2255,7 @@
int Surface::setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata) {
ALOGV("Surface::setBuffersSmpte2086Metadata");
Mutex::Autolock lock(mMutex);
+ mHdrMetadataIsSet |= HdrMetadata::SMPTE2086;
if (metadata) {
mHdrMetadata.smpte2086 = *metadata;
mHdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
@@ -2264,6 +2268,7 @@
int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata) {
ALOGV("Surface::setBuffersCta8613Metadata");
Mutex::Autolock lock(mMutex);
+ mHdrMetadataIsSet |= HdrMetadata::CTA861_3;
if (metadata) {
mHdrMetadata.cta8613 = *metadata;
mHdrMetadata.validTypes |= HdrMetadata::CTA861_3;
@@ -2276,6 +2281,7 @@
int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
ALOGV("Surface::setBuffersBlobMetadata");
Mutex::Autolock lock(mMutex);
+ mHdrMetadataIsSet |= HdrMetadata::HDR10PLUS;
if (size > 0) {
mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d18d22d..a2ed8aa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -314,7 +314,8 @@
}
}
-void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+binder::Status TransactionCompletedListener::onTransactionCompleted(
+ const ListenerStats& listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
@@ -454,9 +455,10 @@
}
}
}
+ return binder::Status::ok();
}
-void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) {
+binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) {
std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -465,6 +467,7 @@
for (auto const& it : callbackCopy) {
it.second(reason.c_str());
}
+ return binder::Status::ok();
}
void TransactionCompletedListener::addQueueStallListener(
@@ -478,9 +481,12 @@
mQueueStallListeners.erase(id);
}
-void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
- sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) {
+binder::Status TransactionCompletedListener::onReleaseBuffer(
+ const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) {
+ sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get()))
+ : Fence::NO_FENCE);
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -489,13 +495,14 @@
if (!callback) {
ALOGE("Could not call release buffer callback, buffer not found %s",
callbackId.to_string().c_str());
- return;
+ return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
}
std::optional<uint32_t> optionalMaxAcquiredBufferCount =
- currentMaxAcquiredBufferCount == UINT_MAX
+ static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX
? std::nullopt
: std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
+ return binder::Status::ok();
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -825,7 +832,11 @@
->mReleaseCallbackThread
.addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
} else {
- listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (fence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get())));
+ }
+ listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX);
}
}
@@ -971,14 +982,16 @@
class SyncCallback {
public:
- static void function(void* callbackContext, nsecs_t /* latchTime */,
- const sp<Fence>& /* presentFence */,
- const std::vector<SurfaceControlStats>& /* stats */) {
- if (!callbackContext) {
- ALOGE("failed to get callback context for SyncCallback");
- }
- SyncCallback* helper = static_cast<SyncCallback*>(callbackContext);
- LOG_ALWAYS_FATAL_IF(sem_post(&helper->mSemaphore), "sem_post failed");
+ static auto getCallback(std::shared_ptr<SyncCallback>& callbackContext) {
+ return [callbackContext](void* /* unused context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context for SyncCallback");
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(sem_post(&callbackContext->mSemaphore), "sem_post failed");
+ };
}
~SyncCallback() {
if (mInitialized) {
@@ -1013,10 +1026,11 @@
return mStatus;
}
- SyncCallback syncCallback;
+ std::shared_ptr<SyncCallback> syncCallback = std::make_shared<SyncCallback>();
if (synchronous) {
- syncCallback.init();
- addTransactionCommittedCallback(syncCallback.function, syncCallback.getContext());
+ syncCallback->init();
+ addTransactionCommittedCallback(SyncCallback::getCallback(syncCallback),
+ /*callbackContext=*/nullptr);
}
bool hasListenerCallbacks = !mListenerCallbacks.empty();
@@ -1092,7 +1106,7 @@
clear();
if (synchronous) {
- syncCallback.wait();
+ syncCallback->wait();
}
mStatus = NO_ERROR;
@@ -1250,6 +1264,7 @@
if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
(mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
(mask & layer_state_t::eEnableBackpressure) ||
+ (mask & layer_state_t::eIgnoreDestinationFrame) ||
(mask & layer_state_t::eLayerIsDisplayDecoration)) {
s->what |= layer_state_t::eFlagsChanged;
}
@@ -2143,6 +2158,12 @@
mStatus = NO_INIT;
}
+status_t SurfaceComposerClient::bootFinished() {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+ binder::Status status = sf->bootFinished();
+ return statusTFromBinderStatus(status);
+}
+
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, int32_t flags,
const sp<IBinder>& parentHandle,
@@ -2259,12 +2280,12 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display,
+status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId,
ui::StaticDisplayInfo* outInfo) {
using Tag = android::gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
gui::StaticDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(displayId, &ginfo);
if (status.isOk()) {
// convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
@@ -2308,52 +2329,74 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display,
- ui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo) {
+ // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
+ for (const auto& mode : ginfo.supportedDisplayModes) {
+ ui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const int32_t& value) { return static_cast<ui::Hdr>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
+ outInfo->renderFrameRate = ginfo.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
+ for (const auto& cmode : ginfo.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
+
+ std::vector<ui::Hdr> types;
+ types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
+ for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
+ types.push_back(static_cast<ui::Hdr>(hdr));
+ }
+ outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
+ ginfo.hdrCapabilities.maxAverageLuminance,
+ ginfo.hdrCapabilities.minLuminance);
+
+ outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+}
+
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
+ ui::DynamicDisplayInfo* outInfo) {
gui::DynamicDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromId(displayId,
+ &ginfo);
if (status.isOk()) {
- // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
- for (const auto& mode : ginfo.supportedDisplayModes) {
- ui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
+ }
+ return statusTFromBinderStatus(status);
+}
- outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
- for (const auto& cmode : ginfo.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
-
- std::vector<ui::Hdr> types;
- types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
- for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
- types.push_back(static_cast<ui::Hdr>(hdr));
- }
- outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
- ginfo.hdrCapabilities.maxAverageLuminance,
- ginfo.hdrCapabilities.minLuminance);
-
- outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo* outInfo) {
+ gui::DynamicDisplayInfo ginfo;
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromToken(display,
+ &ginfo);
+ if (status.isOk()) {
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
}
return statusTFromBinderStatus(status);
}
@@ -2361,7 +2404,8 @@
status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
ui::DisplayMode* mode) {
ui::DynamicDisplayInfo info;
- status_t result = getDynamicDisplayInfo(display, &info);
+
+ status_t result = getDynamicDisplayInfoFromToken(display, &info);
if (result != NO_ERROR) {
return result;
}
@@ -2537,7 +2581,7 @@
gui::PullAtomData pad;
binder::Status status = ComposerServiceAIDL::getComposerService()->onPullAtom(atomId, &pad);
if (status.isOk()) {
- outData->assign((const char*)pad.data.data(), pad.data.size());
+ outData->assign(pad.data.begin(), pad.data.end());
*success = pad.success;
}
return statusTFromBinderStatus(status);
@@ -2822,7 +2866,11 @@
while (!callbackInfos.empty()) {
auto [callbackId, releaseFence] = callbackInfos.front();
- listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX);
callbackInfos.pop();
}
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
index 1c43530..9415035 100644
--- a/libs/gui/TEST_MAPPING
+++ b/libs/gui/TEST_MAPPING
@@ -3,5 +3,11 @@
{
"path": "frameworks/native/libs/nativewindow"
}
+ ],
+ "postsubmit": [
+ {
+ // TODO(257123981): move this to presubmit after dealing with existing breakages.
+ "name": "libgui_test"
+ }
]
}
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index 3cd77f8..ce30426 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -27,6 +27,7 @@
Size resolution;
float xDpi = 0.0f;
float yDpi = 0.0f;
+ int[] supportedHdrTypes;
float refreshRate = 0.0f;
long appVsyncOffset = 0;
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 57e6081..3114929 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -27,6 +27,7 @@
List<DisplayMode> supportedDisplayModes;
int activeDisplayModeId;
+ float renderFrameRate;
int[] supportedColorModes;
int activeColorMode;
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 40410fb..a0b613c 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -62,8 +62,6 @@
* Signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
*/
- // Note this must be the 1st method, so IBinder::FIRST_CALL_TRANSACTION
- // is assigned, as it is called from Java by ActivityManagerService.
void bootFinished();
/**
@@ -126,12 +124,14 @@
/**
* Gets immutable information about given physical display.
*/
- StaticDisplayInfo getStaticDisplayInfo(IBinder display);
+ StaticDisplayInfo getStaticDisplayInfo(long displayId);
/**
* Gets dynamic information about given physical display.
*/
- DynamicDisplayInfo getDynamicDisplayInfo(IBinder display);
+ DynamicDisplayInfo getDynamicDisplayInfoFromId(long displayId);
+
+ DynamicDisplayInfo getDynamicDisplayInfoFromToken(IBinder display);
DisplayPrimaries getDisplayNativePrimaries(IBinder display);
diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
similarity index 60%
copy from libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
copy to libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
index a8bc994..dde4d38 100644
--- a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
+++ b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
@@ -16,8 +16,16 @@
package android.gui;
+import android.gui.ListenerStats;
+import android.gui.ReleaseCallbackId;
+
/** @hide */
-parcelable SupportedBufferCombinations {
- int[] pixelFormats;
- int[] dataspaces;
+oneway interface ITransactionCompletedListener {
+ void onTransactionCompleted(in ListenerStats stats);
+
+ void onReleaseBuffer(in ReleaseCallbackId callbackId,
+ in @nullable ParcelFileDescriptor releaseFenceFd,
+ int currentMaxAcquiredBufferCount);
+
+ void onTransactionQueueStalled(@utf8InCpp String name);
}
diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl
similarity index 85%
rename from libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
rename to libs/gui/aidl/android/gui/ListenerStats.aidl
index a8bc994..63248b2 100644
--- a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
+++ b/libs/gui/aidl/android/gui/ListenerStats.aidl
@@ -16,8 +16,4 @@
package android.gui;
-/** @hide */
-parcelable SupportedBufferCombinations {
- int[] pixelFormats;
- int[] dataspaces;
-}
+parcelable ListenerStats cpp_header "gui/ListenerStats.h";
diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
index 80d5ced..75cea15 100644
--- a/libs/gui/aidl/android/gui/OverlayProperties.aidl
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -16,9 +16,11 @@
package android.gui;
-import android.gui.SupportedBufferCombinations;
-
/** @hide */
parcelable OverlayProperties {
+ parcelable SupportedBufferCombinations {
+ int[] pixelFormats;
+ int[] dataspaces;
+ }
SupportedBufferCombinations[] combinations;
}
diff --git a/libs/gui/aidl/android/gui/PullAtomData.aidl b/libs/gui/aidl/android/gui/PullAtomData.aidl
index 14d33c6..c307cef 100644
--- a/libs/gui/aidl/android/gui/PullAtomData.aidl
+++ b/libs/gui/aidl/android/gui/PullAtomData.aidl
@@ -18,6 +18,6 @@
/** @hide */
parcelable PullAtomData {
- @utf8InCpp String data;
+ byte[] data;
boolean success;
}
diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
similarity index 85%
copy from libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
copy to libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
index a8bc994..c86de34 100644
--- a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
+++ b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
@@ -16,8 +16,4 @@
package android.gui;
-/** @hide */
-parcelable SupportedBufferCombinations {
- int[] pixelFormats;
- int[] dataspaces;
-}
+parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 1c61d6b..82e1b5a 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -62,7 +62,6 @@
"libutils",
"libnativewindow",
"libvndksupport",
- "libbufferhubqueue",
],
header_libs: [
"libdvr_headers",
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 9d1ee8f..8810e4e 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -79,9 +79,11 @@
(override));
MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
(override));
- MOCK_METHOD(binder::Status, getStaticDisplayInfo, (const sp<IBinder>&, gui::StaticDisplayInfo*),
+ MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*),
(override));
- MOCK_METHOD(binder::Status, getDynamicDisplayInfo,
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*),
+ (override));
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken,
(const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
(const sp<IBinder>&, gui::DisplayPrimaries*), (override));
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
new file mode 100644
index 0000000..89a7058
--- /dev/null
+++ b/libs/gui/include/gui/Choreographer.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/choreographer.h>
+#include <gui/DisplayEventDispatcher.h>
+#include <jni.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace android {
+using gui::VsyncEventData;
+
+struct FrameCallback {
+ AChoreographer_frameCallback callback;
+ AChoreographer_frameCallback64 callback64;
+ AChoreographer_vsyncCallback vsyncCallback;
+ void* data;
+ nsecs_t dueTime;
+
+ inline bool operator<(const FrameCallback& rhs) const {
+ // Note that this is intentionally flipped because we want callbacks due sooner to be at
+ // the head of the queue
+ return dueTime > rhs.dueTime;
+ }
+};
+
+struct RefreshRateCallback {
+ AChoreographer_refreshRateCallback callback;
+ void* data;
+ bool firstCallbackFired = false;
+};
+
+class Choreographer;
+
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+ int64_t frameTimeNanos{0};
+
+ VsyncEventData vsyncEventData;
+
+ const Choreographer* choreographer;
+};
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+ struct Context {
+ std::mutex lock;
+ std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+ std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
+ bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+ std::atomic<nsecs_t> mLastKnownVsync = -1;
+ };
+ static Context gChoreographers;
+
+ explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
+ void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_vsyncCallback vsyncCallback, void* data,
+ nsecs_t delay);
+ void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+ EXCLUDES(gChoreographers.lock);
+ void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+ // Drains the queue of pending vsync periods and dispatches refresh rate
+ // updates to callbacks.
+ // The assumption is that this method is only called on a single
+ // processing thread, either by looper or by AChoreographer_handleEvents
+ void handleRefreshRateUpdates();
+ void scheduleLatestConfigRequest();
+
+ enum {
+ MSG_SCHEDULE_CALLBACKS = 0,
+ MSG_SCHEDULE_VSYNC = 1,
+ MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
+ };
+ virtual void handleMessage(const Message& message) override;
+
+ static void initJVM(JNIEnv* env);
+ static Choreographer* getForThread();
+ static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
+ static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
+ virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
+ int64_t getFrameInterval() const;
+ bool inCallback() const;
+
+private:
+ Choreographer(const Choreographer&) = delete;
+
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ VsyncEventData vsyncEventData) override;
+ void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+ nsecs_t vsyncPeriod) override;
+ void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
+ void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
+ std::vector<FrameRateOverride> overrides) override;
+
+ void scheduleCallbacks();
+
+ ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+ void registerStartTime() const;
+
+ std::mutex mLock;
+ // Protected by mLock
+ std::priority_queue<FrameCallback> mFrameCallbacks;
+ std::vector<RefreshRateCallback> mRefreshRateCallbacks;
+
+ nsecs_t mLatestVsyncPeriod = -1;
+ VsyncEventData mLastVsyncEventData;
+ bool mInCallback = false;
+
+ const sp<Looper> mLooper;
+ const std::thread::id mThreadId;
+
+ // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
+ static constexpr size_t kMaxStartTimes = 250;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d517e99..06a246e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,11 +23,11 @@
#include <android/gui/IHdrLayerInfoListener.h>
#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/SpHash.h>
#include <math/vec4.h>
#include <stdint.h>
@@ -66,6 +66,7 @@
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
+using gui::ListenerCallbacks;
using gui::SpHash;
namespace gui {
@@ -102,7 +103,7 @@
// (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
// in the early configuration until it receives eEarlyWakeupEnd. These flags are
// expected to be used by WindowManager only and are guarded by
- // android.permission.ACCESS_SURFACE_FLINGER
+ // android.permission.WAKEUP_SURFACE_FLINGER
eEarlyWakeupStart = 0x08,
eEarlyWakeupEnd = 0x10,
eOneWay = 0x20
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index e16f89c..9cf62bc 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -66,8 +66,9 @@
Standard = 1,
Performance = 2,
Battery = 3,
+ Custom = 4,
- ftl_last = Battery
+ ftl_last = Custom
};
} // namespace android::gui
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 09f171d..c5fdf82 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,10 +21,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#include <android/gui/DropInputMode.h>
@@ -35,6 +35,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfo.h>
@@ -56,6 +57,9 @@
using gui::ISurfaceComposerClient;
using gui::LayerMetadata;
+using gui::ITransactionCompletedListener;
+using gui::ReleaseCallbackId;
+
struct client_cache_t {
wp<IBinder> token = nullptr;
uint64_t id;
@@ -201,7 +205,32 @@
void merge(const layer_state_t& other);
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ // Compares two layer_state_t structs and returns a set of change flags describing all the
+ // states that are different.
+ uint64_t diff(const layer_state_t& other) const;
bool hasBufferChanges() const;
+
+ // Changes to the tree structure.
+ static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent |
+ layer_state_t::eBackgroundColorChanged;
+ // Content updates.
+ static constexpr uint64_t CONTENT_CHANGES = layer_state_t::eAlphaChanged |
+ layer_state_t::eTransparentRegionChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eRenderBorderChanged | layer_state_t::eColorChanged |
+ layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
+ layer_state_t::eApiChanged | layer_state_t::eSidebandStreamChanged |
+ layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eBackgroundColorChanged | layer_state_t::eColorSpaceAgnosticChanged |
+ layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eAutoRefreshChanged | layer_state_t::eStretchChanged;
+ // Changes to content or children size.
+ static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::ePositionChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::eTransparentRegionChanged |
+ layer_state_t::eBufferCropChanged | layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
+ layer_state_t::eDestinationFrameChanged;
+
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -212,6 +241,11 @@
float dsdy{0};
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
+ inline bool operator==(const matrix22_t& other) const {
+ return std::tie(dsdx, dtdx, dtdy, dsdy) ==
+ std::tie(other.dsdx, other.dtdx, other.dtdy, other.dsdy);
+ }
+ inline bool operator!=(const matrix22_t& other) const { return !(*this == other); }
};
sp<IBinder> surface;
int32_t layerId;
@@ -329,6 +363,7 @@
DisplayState();
void merge(const DisplayState& other);
+ void sanitize(int32_t permissions);
uint32_t what = 0;
uint32_t flags = 0;
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ListenerStats.h
similarity index 81%
rename from libs/gui/include/gui/ITransactionCompletedListener.h
rename to libs/gui/include/gui/ListenerStats.h
index 453e8f3..3a12802 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ListenerStats.h
@@ -24,6 +24,8 @@
#include <binder/SafeInterface.h>
#include <gui/FrameTimestamps.h>
+#include <gui/ReleaseCallbackId.h>
+
#include <ui/Fence.h>
#include <utils/Timers.h>
@@ -32,10 +34,7 @@
#include <unordered_set>
#include <variant>
-namespace android {
-
-class ITransactionCompletedListener;
-class ListenerCallbacks;
+namespace android::gui {
class CallbackId : public Parcelable {
public:
@@ -54,30 +53,6 @@
std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
};
-class ReleaseCallbackId : public Parcelable {
-public:
- static const ReleaseCallbackId INVALID_ID;
-
- uint64_t bufferId;
- uint64_t framenumber;
- ReleaseCallbackId() {}
- ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
- : bufferId(bufferId), framenumber(framenumber) {}
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-
- bool operator==(const ReleaseCallbackId& rhs) const {
- return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
- }
- bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
- std::string to_string() const {
- if (*this == INVALID_ID) return "INVALID_ID";
-
- return "bufferId:" + std::to_string(bufferId) +
- " framenumber:" + std::to_string(framenumber);
- }
-};
-
struct ReleaseBufferCallbackIdHash {
std::size_t operator()(const ReleaseCallbackId& key) const {
return std::hash<uint64_t>()(key.bufferId);
@@ -186,27 +161,6 @@
std::vector<TransactionStats> transactionStats;
};
-class ITransactionCompletedListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(TransactionCompletedListener)
-
- virtual void onTransactionCompleted(ListenerStats stats) = 0;
-
- virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) = 0;
-
- virtual void onTransactionQueueStalled(const String8& name) = 0;
-};
-
-class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
-public:
- BnTransactionCompletedListener()
- : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
class ListenerCallbacks {
public:
ListenerCallbacks(const sp<IBinder>& listener,
@@ -268,4 +222,4 @@
}
};
-} // namespace android
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h
new file mode 100644
index 0000000..142ee5a
--- /dev/null
+++ b/libs/gui/include/gui/ReleaseCallbackId.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <cstdint>
+
+namespace android::gui {
+
+class ReleaseCallbackId : public Parcelable {
+public:
+ static const ReleaseCallbackId INVALID_ID;
+
+ uint64_t bufferId;
+ uint64_t framenumber;
+ ReleaseCallbackId() {}
+ ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+ : bufferId(bufferId), framenumber(framenumber) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const ReleaseCallbackId& rhs) const {
+ return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+ }
+ bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+ std::string to_string() const {
+ if (*this == INVALID_ID) return "INVALID_ID";
+
+ return "bufferId:" + std::to_string(bufferId) +
+ " framenumber:" + std::to_string(framenumber);
+ }
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 7aec0bf..b9ccdc9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -486,6 +486,11 @@
// queue operation. There is no HDR metadata by default.
HdrMetadata mHdrMetadata;
+ // mHdrMetadataIsSet is a bitfield to track which HDR metadata has been set.
+ // Prevent Surface from resetting HDR metadata that was set on a bufer when
+ // HDR metadata is not set on this Surface.
+ uint32_t mHdrMetadataIsSet{0};
+
// mCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mCrop;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2038f14..cc459c5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,10 +42,13 @@
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/BnTransactionCompletedListener.h>
+
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -59,11 +62,21 @@
class ITunnelModeEnabledListener;
class Region;
+using gui::BnTransactionCompletedListener;
+using gui::CallbackId;
+using gui::CallbackIdHash;
using gui::DisplayCaptureArgs;
+using gui::FrameEventHistoryStats;
using gui::IRegionSamplingListener;
using gui::ISurfaceComposerClient;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
using gui::LayerCaptureArgs;
using gui::LayerMetadata;
+using gui::ListenerStats;
+using gui::ReleaseBufferCallbackIdHash;
+using gui::ReleaseCallbackId;
+using gui::SurfaceStats;
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
@@ -145,14 +158,17 @@
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
void* cookie = nullptr, uint32_t flags = 0);
+ // Notify the SurfaceComposerClient that the boot procedure has completed
+ static status_t bootFinished();
+
// Get transactional state of given display.
static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
// Get immutable information about given physical display.
- static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*);
+ static status_t getStaticDisplayInfo(int64_t, ui::StaticDisplayInfo*);
- // Get dynamic information about given physical display.
- static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*);
+ // Get dynamic information about given physical display from display id
+ static status_t getDynamicDisplayInfoFromId(int64_t, ui::DynamicDisplayInfo*);
// Shorthand for the active display mode from getDynamicDisplayInfo().
// TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function.
@@ -714,6 +730,12 @@
ReleaseCallbackThread mReleaseCallbackThread;
private:
+ // Get dynamic information about given physical display from token
+ static status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo*);
+
+ static void getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo);
virtual void onFirstRef();
mutable Mutex mLock;
@@ -819,17 +841,17 @@
void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
// BnTransactionCompletedListener overrides
- void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionCompleted(const ListenerStats& stats) override;
+ binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionQueueStalled(const std::string& reason) override;
void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
- void onTransactionQueueStalled(const String8& reason) override;
-
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index ac74c8a..b01a3db 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -272,6 +272,7 @@
WindowInfoHandle(const WindowInfo& other);
inline const WindowInfo* getInfo() const { return &mInfo; }
+ inline WindowInfo* editInfo() { return &mInfo; }
sp<IBinder> getToken() const;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6d3b425..55242df 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -782,13 +782,18 @@
return binder::Status::ok();
}
- binder::Status getStaticDisplayInfo(const sp<IBinder>& /*display*/,
+ binder::Status getStaticDisplayInfo(int64_t /*displayId*/,
gui::StaticDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& /*display*/,
- gui::DynamicDisplayInfo* /*outInfo*/) override {
+ binder::Status getDynamicDisplayInfoFromId(int64_t /*displayId*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& /*display*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 3685f54..9e8ebf3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -21,6 +21,7 @@
#include <cutils/compiler.h>
#include <inttypes.h>
#include <string.h>
+#include <optional>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -552,21 +553,21 @@
&pointerCoords[getPointerCount()]);
}
-int MotionEvent::getSurfaceRotation() const {
+std::optional<ui::Rotation> MotionEvent::getSurfaceRotation() const {
// The surface rotation is the rotation from the window's coordinate space to that of the
// display. Since the event's transform takes display space coordinates to window space, the
// returned surface rotation is the inverse of the rotation for the surface.
switch (mTransform.getOrientation()) {
case ui::Transform::ROT_0:
- return DISPLAY_ORIENTATION_0;
+ return ui::ROTATION_0;
case ui::Transform::ROT_90:
- return DISPLAY_ORIENTATION_270;
+ return ui::ROTATION_270;
case ui::Transform::ROT_180:
- return DISPLAY_ORIENTATION_180;
+ return ui::ROTATION_180;
case ui::Transform::ROT_270:
- return DISPLAY_ORIENTATION_90;
+ return ui::ROTATION_90;
default:
- return -1;
+ return std::nullopt;
}
}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 4751a7d..fb6c590 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -179,6 +179,7 @@
mIsExternal(other.mIsExternal),
mHasMic(other.mHasMic),
mCountryCode(other.mCountryCode),
+ mKeyboardLayoutInfo(other.mKeyboardLayoutInfo),
mSources(other.mSources),
mKeyboardType(other.mKeyboardType),
mKeyCharacterMap(other.mKeyCharacterMap),
@@ -270,6 +271,10 @@
mKeyboardType = std::max(mKeyboardType, keyboardType);
}
+void InputDeviceInfo::setKeyboardLayoutInfo(KeyboardLayoutInfo layoutInfo) {
+ mKeyboardLayoutInfo = std::move(layoutInfo);
+}
+
std::vector<InputDeviceSensorInfo> InputDeviceInfo::getSensors() {
std::vector<InputDeviceSensorInfo> infos;
infos.reserve(mSensors.size());
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index b78fae3..dd7cbb5 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -339,7 +339,8 @@
DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \
DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \
DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \
- DEFINE_KEYCODE(STYLUS_BUTTON_TAIL)
+ DEFINE_KEYCODE(STYLUS_BUTTON_TAIL), \
+ DEFINE_KEYCODE(RECENT_APPS)
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 422e6e0..fa5c41f 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -43,7 +43,6 @@
// Enables debug output for mapping.
#define DEBUG_MAPPING 0
-
namespace android {
static const char* WHITESPACE = " \t\r";
@@ -93,6 +92,7 @@
: mType(other.mType),
mLoadFileName(other.mLoadFileName),
mLayoutOverlayApplied(other.mLayoutOverlayApplied),
+ mKeyRemapping(other.mKeyRemapping),
mKeysByScanCode(other.mKeysByScanCode),
mKeysByUsageCode(other.mKeysByUsageCode) {
for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -114,7 +114,7 @@
if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
return false;
}
- if (mKeys.size() != other.mKeys.size() ||
+ if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() ||
mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
return false;
@@ -131,22 +131,9 @@
}
}
- for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
- if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
- return false;
- }
- if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
- return false;
- }
- }
-
- for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
- if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
- return false;
- }
- if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
- return false;
- }
+ if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode ||
+ mKeysByUsageCode != other.mKeysByUsageCode) {
+ return false;
}
return true;
@@ -258,14 +245,12 @@
}
}
- for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
- mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
- overlay.mKeysByScanCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByScanCode) {
+ mKeysByScanCode.insert_or_assign(it.first, it.second);
}
- for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
- mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
- overlay.mKeysByUsageCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByUsageCode) {
+ mKeysByUsageCode.insert_or_assign(it.first, it.second);
}
mLayoutOverlayApplied = true;
}
@@ -400,11 +385,26 @@
return true;
}
+void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ if (fromKeyCode == toKeyCode) {
+ mKeyRemapping.erase(fromKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
+#endif
+ return;
+ }
+ mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
+ toKeyCode);
+#endif
+}
+
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- *outKeyCode = mKeysByUsageCode.valueAt(index);
+ const auto it = mKeysByUsageCode.find(usageCode);
+ if (it != mKeysByUsageCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -413,9 +413,9 @@
}
}
if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- *outKeyCode = mKeysByScanCode.valueAt(index);
+ const auto it = mKeysByScanCode.find(scanCode);
+ if (it != mKeysByScanCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -431,45 +431,59 @@
return NAME_NOT_FOUND;
}
-void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState,
- int32_t *outKeyCode, int32_t *outMetaState) const {
- *outKeyCode = keyCode;
- *outMetaState = metaState;
+int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const {
+ int32_t toKeyCode = fromKeyCode;
- const Behavior* behavior = getKeyBehavior(keyCode, metaState);
+ const auto it = mKeyRemapping.find(fromKeyCode);
+ if (it != mKeyRemapping.end()) {
+ toKeyCode = it->second;
+ }
+#if DEBUG_MAPPING
+ ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode);
+#endif
+ return toKeyCode;
+}
+
+std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
+ int32_t fromMetaState) const {
+ int32_t toKeyCode = fromKeyCode;
+ int32_t toMetaState = fromMetaState;
+
+ const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState);
if (behavior != nullptr) {
if (behavior->replacementKeyCode) {
- *outKeyCode = behavior->replacementKeyCode;
- int32_t newMetaState = metaState & ~behavior->metaState;
+ toKeyCode = behavior->replacementKeyCode;
+ toMetaState = fromMetaState & ~behavior->metaState;
// Reset dependent meta states.
if (behavior->metaState & AMETA_ALT_ON) {
- newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
+ toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- newMetaState &= ~AMETA_ALT_ON;
+ toMetaState &= ~AMETA_ALT_ON;
}
if (behavior->metaState & AMETA_CTRL_ON) {
- newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
+ toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
}
if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- newMetaState &= ~AMETA_CTRL_ON;
+ toMetaState &= ~AMETA_CTRL_ON;
}
if (behavior->metaState & AMETA_SHIFT_ON) {
- newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
+ toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
- newMetaState &= ~AMETA_SHIFT_ON;
+ toMetaState &= ~AMETA_SHIFT_ON;
}
// ... and put universal bits back if needed
- *outMetaState = normalizeMetaState(newMetaState);
+ toMetaState = normalizeMetaState(toMetaState);
}
}
#if DEBUG_MAPPING
- ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ "
- "replacement keyCode=%d, replacement metaState=0x%08x.",
- keyCode, metaState, *outKeyCode, *outMetaState);
+ ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ "
+ "replacement keyCode=%d, replacement metaState=0x%08x.",
+ fromKeyCode, fromMetaState, toKeyCode, toMetaState);
#endif
+ return std::make_pair(toKeyCode, toMetaState);
}
bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
@@ -720,6 +734,18 @@
return nullptr;
}
}
+ size_t numKeyRemapping = parcel->readInt32();
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < numKeyRemapping; i++) {
+ int32_t key = parcel->readInt32();
+ int32_t value = parcel->readInt32();
+ map->mKeyRemapping.insert_or_assign(key, value);
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ }
size_t numKeysByScanCode = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -727,7 +753,7 @@
for (size_t i = 0; i < numKeysByScanCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByScanCode.add(key, value);
+ map->mKeysByScanCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -739,7 +765,7 @@
for (size_t i = 0; i < numKeysByUsageCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByUsageCode.add(key, value);
+ map->mKeysByUsageCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -773,17 +799,23 @@
}
parcel->writeInt32(0);
}
+ size_t numKeyRemapping = mKeyRemapping.size();
+ parcel->writeInt32(numKeyRemapping);
+ for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) {
+ parcel->writeInt32(fromAndroidKeyCode);
+ parcel->writeInt32(toAndroidKeyCode);
+ }
size_t numKeysByScanCode = mKeysByScanCode.size();
parcel->writeInt32(numKeysByScanCode);
- for (size_t i = 0; i < numKeysByScanCode; i++) {
- parcel->writeInt32(mKeysByScanCode.keyAt(i));
- parcel->writeInt32(mKeysByScanCode.valueAt(i));
+ for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) {
+ parcel->writeInt32(fromScanCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
size_t numKeysByUsageCode = mKeysByUsageCode.size();
parcel->writeInt32(numKeysByUsageCode);
- for (size_t i = 0; i < numKeysByUsageCode; i++) {
- parcel->writeInt32(mKeysByUsageCode.keyAt(i));
- parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+ for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) {
+ parcel->writeInt32(fromUsageCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
}
#endif // __linux__
@@ -950,9 +982,9 @@
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
- KeyedVector<int32_t, int32_t>& map =
- mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+ const auto it = map.find(code);
+ if (it != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -971,7 +1003,7 @@
ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
mapUsage ? "usage" : "scan code", code, keyCode);
#endif
- map.add(code, keyCode);
+ map.insert_or_assign(code, keyCode);
return NO_ERROR;
}
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 16ffa10..ed9ac9f 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -116,25 +116,24 @@
Tokenizer* rawTokenizer;
status_t status = Tokenizer::open(String8(filename), &rawTokenizer);
- std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
if (status) {
- ALOGE("Error %d opening property file %s.", status, filename);
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(outMap.get(), tokenizer.get());
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (status) {
- return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
- }
+ return android::base::Error(-status) << "Could not open file: " << filename;
}
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+ std::unique_ptr<Tokenizer> tokenizer(rawTokenizer);
+ Parser parser(outMap.get(), tokenizer.get());
+ status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+ nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+ ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(),
+ tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+#endif
+ if (status) {
+ return android::base::Error(BAD_VALUE) << "Could not parse " << filename;
+ }
+
return std::move(outMap);
}
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index c62e098..c9393f4 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -40,17 +40,20 @@
const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }
-void TouchVideoFrame::rotate(int32_t orientation) {
+void TouchVideoFrame::rotate(ui::Rotation orientation) {
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
rotateQuarterTurn(false /*clockwise*/);
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
rotate180();
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
rotateQuarterTurn(true /*clockwise*/);
break;
+ case ui::ROTATION_0:
+ // No need to rotate if there's no rotation.
+ break;
}
}
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 654b236..081a995 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -73,38 +73,38 @@
TEST(TouchVideoFrame, Rotate90_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2_4times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
- frame.rotate(DISPLAY_ORIENTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
+ frame.rotate(ui::ROTATION_90);
ASSERT_EQ(frame, frameOriginal);
}
@@ -113,43 +113,43 @@
TEST(TouchVideoFrame, Rotate180_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate180_3x2_2times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameOriginal);
}
TEST(TouchVideoFrame, Rotate180_3x3) {
TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_180);
+ frame.rotate(ui::ROTATION_180);
ASSERT_EQ(frame, frameRotated);
}
@@ -158,38 +158,38 @@
TEST(TouchVideoFrame, Rotate270_0x0) {
TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_1x1) {
TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2_4times) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
- frame.rotate(DISPLAY_ORIENTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
+ frame.rotate(ui::ROTATION_270);
ASSERT_EQ(frame, frameOriginal);
}
diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp
index 0375915..54af7c9 100644
--- a/libs/jpegrecoverymap/Android.bp
+++ b/libs/jpegrecoverymap/Android.bp
@@ -31,6 +31,7 @@
srcs: [
"recoverymap.cpp",
"recoverymapmath.cpp",
+ "recoverymaputils.cpp",
],
shared_libs: [
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
index df24b10..39c79c9 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -47,7 +47,7 @@
*/
void* getDecompressedImagePtr();
/*
- * Returns the decompressed raw image buffer size. This method must be called only after
+ * Returns the decompressed raw image buffer size. This mgit ethod must be called only after
* calling decompressImage().
*/
size_t getDecompressedImageSize();
@@ -61,6 +61,42 @@
* decompressImage().
*/
size_t getDecompressedImageHeight();
+ /*
+ * Returns the XMP data from the image.
+ */
+ void* getXMPPtr();
+ /*
+ * Returns the decompressed XMP buffer size. This method must be called only after
+ * calling decompressImage() or getCompressedImageParameters().
+ */
+ size_t getXMPSize();
+ /*
+ * Returns the EXIF data from the image.
+ */
+ void* getEXIFPtr();
+ /*
+ * Returns the decompressed EXIF buffer size. This method must be called only after
+ * calling decompressImage() or getCompressedImageParameters().
+ */
+ size_t getEXIFSize();
+ /*
+ * Returns the position offset of EXIF package
+ * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>),
+ * or -1 if no EXIF exists.
+ */
+ int getEXIFPos() { return mExifPos; }
+ /*
+ * Decompresses metadata of the image.
+ */
+ bool getCompressedImageParameters(const void* image, int length,
+ size_t* pWidth, size_t* pHeight,
+ std::vector<uint8_t>* &iccData,
+ std::vector<uint8_t>* &exifData);
+ /*
+ * Extracts EXIF package and updates the EXIF position / length without decoding the image.
+ */
+ bool extractEXIF(const void* image, int length);
+
private:
bool decode(const void* image, int length);
// Returns false if errors occur.
@@ -72,9 +108,16 @@
static const int kCompressBatchSize = 16;
// The buffer that holds the decompressed result.
std::vector<JOCTET> mResultBuffer;
+ // The buffer that holds XMP Data.
+ std::vector<JOCTET> mXMPBuffer;
+ // The buffer that holds EXIF Data.
+ std::vector<JOCTET> mEXIFBuffer;
+
// Resolution of the decompressed image.
size_t mWidth;
size_t mHeight;
+ // Position of EXIF package, default value is -1 which means no EXIF package appears.
+ size_t mExifPos;
};
} /* namespace android */
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index 194cd2f..6995762 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -37,12 +37,14 @@
ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2,
ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3,
ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
+ ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
JPEGR_RUNTIME_ERROR_BASE = -20000,
ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
+ ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index b2ca481..905bf16 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -22,11 +22,25 @@
namespace android::recoverymap {
typedef enum {
- JPEGR_COLORSPACE_UNSPECIFIED,
- JPEGR_COLORSPACE_BT709,
- JPEGR_COLORSPACE_P3,
- JPEGR_COLORSPACE_BT2100,
-} jpegr_color_space;
+ JPEGR_COLORGAMUT_UNSPECIFIED,
+ JPEGR_COLORGAMUT_BT709,
+ JPEGR_COLORGAMUT_P3,
+ JPEGR_COLORGAMUT_BT2100,
+} jpegr_color_gamut;
+
+// Transfer functions as defined for XMP metadata
+typedef enum {
+ JPEGR_TF_LINEAR = 0,
+ JPEGR_TF_HLG = 1,
+ JPEGR_TF_PQ = 2,
+} jpegr_transfer_function;
+
+struct jpegr_info_struct {
+ size_t width;
+ size_t height;
+ std::vector<uint8_t>* iccData;
+ std::vector<uint8_t>* exifData;
+};
/*
* Holds information for uncompressed image or recovery map.
@@ -38,8 +52,8 @@
int width;
// Height of the recovery map or image in pixels.
int height;
- // Color space.
- jpegr_color_space colorSpace;
+ // Color gamut.
+ jpegr_color_gamut colorGamut;
};
/*
@@ -48,10 +62,12 @@
struct jpegr_compressed_struct {
// Pointer to the data location.
void* data;
- // Data length.
+ // Used data length in bytes.
int length;
- // Color space.
- jpegr_color_space colorSpace;
+ // Maximum available data length in bytes.
+ int maxLength;
+ // Color gamut.
+ jpegr_color_gamut colorGamut;
};
/*
@@ -64,20 +80,86 @@
int length;
};
+struct chromaticity_coord {
+ float x;
+ float y;
+};
+
+
+struct st2086_metadata {
+ // xy chromaticity coordinate of the red primary of the mastering display
+ chromaticity_coord redPrimary;
+ // xy chromaticity coordinate of the green primary of the mastering display
+ chromaticity_coord greenPrimary;
+ // xy chromaticity coordinate of the blue primary of the mastering display
+ chromaticity_coord bluePrimary;
+ // xy chromaticity coordinate of the white point of the mastering display
+ chromaticity_coord whitePoint;
+ // Maximum luminance in nits of the mastering display
+ uint32_t maxLuminance;
+ // Minimum luminance in nits of the mastering display
+ float minLuminance;
+};
+
+struct hdr10_metadata {
+ // Mastering display color volume
+ st2086_metadata st2086Metadata;
+ // Max frame average light level in nits
+ float maxFALL;
+ // Max content light level in nits
+ float maxCLL;
+};
+
+struct jpegr_metadata {
+ // JPEG/R version
+ uint32_t version;
+ // Range scaling factor for the map
+ float rangeScalingFactor;
+ // The transfer function for decoding the HDR representation of the image
+ jpegr_transfer_function transferFunction;
+ // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ
+ hdr10_metadata hdr10Metadata;
+};
+
typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
typedef struct jpegr_compressed_struct* jr_compressed_ptr;
typedef struct jpegr_exif_struct* jr_exif_ptr;
+typedef struct jpegr_metadata* jr_metadata_ptr;
+typedef struct jpegr_info_struct* jr_info_ptr;
class RecoveryMap {
public:
/*
+ * Encode API-0
+ * Compress JPEGR image from 10-bit HDR YUV.
+ *
+ * Tonemap the HDR input to a SDR image, generate recovery map from the HDR and SDR images,
+ * compress SDR YUV to 8-bit JPEG and append the recovery map to the end of the compressed
+ * JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif);
+
+ /*
+ * Encode API-1
* Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
*
* Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
* the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same
- * resolution and color space.
+ * resolution.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
* @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
* the highest quality
@@ -86,11 +168,13 @@
*/
status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest,
int quality,
jr_exif_ptr exif);
/*
+ * Encode API-2
* Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
*
* This method requires HAL Hardware JPEG encoder.
@@ -99,43 +183,50 @@
* compressed JPEG. HDR and SDR inputs must be the same resolution and color space.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * Note: the SDR image must be the decoded version of the JPEG
+ * input
* @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest);
/*
+ * Encode API-3
* Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
*
* This method requires HAL Hardware JPEG encoder.
*
* Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input
* and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR
- * and SDR inputs must be the same resolution and color space.
+ * and SDR inputs must be the same resolution.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest);
/*
+ * Decode API
* Decompress JPEGR image.
*
+ * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
* @param compressed_jpegr_image compressed JPEGR image
* @param dest destination of the uncompressed JPEGR image
- * @param exif destination of the decoded EXIF metadata. Default value is nullptr where EXIF
- * metadata will not be decoded.
- * @param request_sdr flag that request SDR output, default to false (request HDR output). If
- * set to true, decoder will only decode the primary image which is SDR.
- * Setting of request_sdr and input source (HDR or SDR) can be found in
- * the table below:
+ * @param exif destination of the decoded EXIF metadata.
+ * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
+ * the primary image which is SDR. Setting of request_sdr and input source
+ * (HDR or SDR) can be found in the table below:
* | input source | request_sdr | output of decoding |
* | HDR | true | SDR |
* | HDR | false | HDR |
@@ -147,18 +238,19 @@
jr_uncompressed_ptr dest,
jr_exif_ptr exif = nullptr,
bool request_sdr = false);
-private:
- /*
- * This method is called in the decoding pipeline. It will decode the recovery map.
- *
- * @param compressed_recovery_map compressed recovery map
- * @param dest decoded recover map
- * @return NO_ERROR if decoding succeeds, error code if error occurs.
- */
- status_t decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map,
- jr_uncompressed_ptr dest);
/*
+ * Gets Info from JPEGR file without decoding it.
+ *
+ * The output is filled jpegr_info structure
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param jpegr_info pointer to output JPEGR info
+ * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
+ */
+ status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+ jr_info_ptr jpegr_info);
+private:
+ /*
* This method is called in the encoding pipeline. It will encode the recovery map.
*
* @param uncompressed_recovery_map uncompressed recovery map
@@ -176,29 +268,46 @@
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param dest recovery map; caller responsible for memory of data
- * @param hdr_ratio HDR ratio will be updated in this method
+ * @param metadata metadata provides the transfer function for the HDR
+ * image; range_scaling_factor and hdr10 FALL and CLL will
+ * be updated.
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr dest,
- float &hdr_ratio);
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest);
/*
* This method is called in the decoding pipeline. It will take the uncompressed (decoded)
- * 8-bit yuv image and the uncompressed (decoded) recovery map as input, and calculate the
- * 10-bit recovered image (in p010 color format).
+ * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as
+ * input, and calculate the 10-bit recovered image. The recovered output image is the same
+ * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata,
+ * and is in RGBA1010102 data format.
*
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_recovery_map uncompressed recovery map
+ * @param metadata JPEG/R metadata extracted from XMP.
* @param dest reconstructed HDR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
jr_uncompressed_ptr dest);
/*
+ * This methoud is called to separate primary image and recovery map image from JPEGR
+ *
+ * @param compressed_jpegr_image compressed JPEGR image
+ * @param primary_image destination of primary image
+ * @param recovery_map destination of compressed recovery map
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
+ */
+ status_t extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map);
+ /*
* This method is called in the decoding pipeline. It will read XMP metadata to find the start
* position of the compressed recovery map, and will extract the compressed recovery map.
*
@@ -206,7 +315,8 @@
* @param dest destination of compressed recovery map
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr dest);
+ status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr dest);
/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
@@ -215,55 +325,26 @@
*
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param compress_recovery_map compressed recover map
- * @param hdr_ratio HDR ratio
+ * @param exif EXIF package
+ * @param metadata JPEG/R metadata to encode in XMP of the jpeg
* @param dest compressed JPEGR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
jr_compressed_ptr compressed_recovery_map,
- float hdr_ratio,
+ jr_exif_ptr exif,
+ jr_metadata_ptr metadata,
jr_compressed_ptr dest);
/*
- * This method generates XMP metadata.
+ * This method will tone map a HDR image to an SDR image.
*
- * below is an example of the XMP metadata that this function generates where
- * secondary_image_length = 1000
- * hdr_ratio = 1.25
- *
- * <x:xmpmeta
- * xmlns:x="adobe:ns:meta/"
- * x:xmptk="Adobe XMP Core 5.1.2">
- * <rdf:RDF
- * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- * <rdf:Description
- * xmlns:GContainer="http://ns.google.com/photos/1.0/container/">
- * <GContainer:Version>1</GContainer:Version>
- * <GContainer:HdrRatio>1.25</GContainer:HdrRatio>
- * <GContainer:Directory>
- * <rdf:Seq>
- * <rdf:li>
- * <GContainer:Item
- * Item:Semantic="Primary"
- * Item:Mime="image/jpeg"/>
- * </rdf:li>
- * <rdf:li>
- * <GContainer:Item
- * Item:Semantic="RecoveryMap"
- * Item:Mime="image/jpeg"
- * Item:Length="1000"/>
- * </rdf:li>
- * </rdf:Seq>
- * </GContainer:Directory>
- * </rdf:Description>
- * </rdf:RDF>
- * </x:xmpmeta>
- *
- * @param secondary_image_length length of secondary image
- * @param hdr_ratio hdr ratio
- * @return XMP metadata in type of string
+ * @param uncompressed_p010_image (input) uncompressed P010 image
+ * @param dest (output) tone mapping result as a YUV_420 image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- std::string generateXmp(int secondary_image_length, float hdr_ratio);
+ status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest);
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index ef9112e..0fb64d3 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -23,7 +23,12 @@
namespace android::recoverymap {
+////////////////////////////////////////////////////////////////////////////////
+// Framework
+
const float kSdrWhiteNits = 100.0f;
+const float kHlgMaxNits = 1000.0f;
+const float kPqMaxNits = 10000.0f;
struct Color {
union {
@@ -40,35 +45,202 @@
};
};
-/*
- * Convert from OETF'd bt.2100 RGB to YUV, according to BT.2100
- */
-Color bt2100RgbToYuv(Color e);
+typedef Color (*ColorTransformFn)(Color);
+typedef float (*ColorCalculationFn)(Color);
+
+inline Color operator+=(Color& lhs, const Color& rhs) {
+ lhs.r += rhs.r;
+ lhs.g += rhs.g;
+ lhs.b += rhs.b;
+ return lhs;
+}
+inline Color operator-=(Color& lhs, const Color& rhs) {
+ lhs.r -= rhs.r;
+ lhs.g -= rhs.g;
+ lhs.b -= rhs.b;
+ return lhs;
+}
+
+inline Color operator+(const Color& lhs, const Color& rhs) {
+ Color temp = lhs;
+ return temp += rhs;
+}
+inline Color operator-(const Color& lhs, const Color& rhs) {
+ Color temp = lhs;
+ return temp -= rhs;
+}
+
+inline Color operator+=(Color& lhs, const float rhs) {
+ lhs.r += rhs;
+ lhs.g += rhs;
+ lhs.b += rhs;
+ return lhs;
+}
+inline Color operator-=(Color& lhs, const float rhs) {
+ lhs.r -= rhs;
+ lhs.g -= rhs;
+ lhs.b -= rhs;
+ return lhs;
+}
+inline Color operator*=(Color& lhs, const float rhs) {
+ lhs.r *= rhs;
+ lhs.g *= rhs;
+ lhs.b *= rhs;
+ return lhs;
+}
+inline Color operator/=(Color& lhs, const float rhs) {
+ lhs.r /= rhs;
+ lhs.g /= rhs;
+ lhs.b /= rhs;
+ return lhs;
+}
+
+inline Color operator+(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp += rhs;
+}
+inline Color operator-(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp -= rhs;
+}
+inline Color operator*(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp *= rhs;
+}
+inline Color operator/(const Color& lhs, const float rhs) {
+ Color temp = lhs;
+ return temp /= rhs;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// sRGB transformations
+// NOTE: sRGB has the same color primaries as BT.709, but different transfer
+// function. For this reason, all sRGB transformations here apply to BT.709,
+// except for those concerning transfer functions.
/*
- * Convert srgb YUV to RGB, according to ECMA TR/98.
- */
-Color srgbYuvToRgb(Color e);
-
-/*
- * TODO: better source for srgb transfer function
- * Convert from srgb to linear, according to https://en.wikipedia.org/wiki/SRGB.
+ * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1.
+ *
* [0.0, 1.0] range in and out.
*/
-float srgbInvOetf(float e);
-Color srgbInvOetf(Color e);
+float srgbLuminance(Color e);
/*
- * Convert from HLG to scene luminance in nits, according to BT.2100.
+ * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98.
*/
-float hlgInvOetf(float e);
+Color srgbYuvToRgb(Color e_gamma);
/*
- * Convert from scene luminance in nits to HLG, according to BT.2100.
+ * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98.
*/
+Color srgbRgbToYuv(Color e_gamma);
+
+/*
+ * Convert from srgb to linear, according to IEC 61966-2-1.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float srgbInvOetf(float e_gamma);
+Color srgbInvOetf(Color e_gamma);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Display-P3 transformations
+
+/*
+ * Calculated the luminance of a linear RGB P3 pixel, according to SMPTE EG 432-1.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float p3Luminance(Color e);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// BT.2100 transformations - according to ITU-R BT.2100-2
+
+/*
+ * Calculate the luminance of a linear RGB BT.2100 pixel.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float bt2100Luminance(Color e);
+
+/*
+ * Convert from OETF'd BT.2100 RGB to YUV.
+ */
+Color bt2100RgbToYuv(Color e_gamma);
+
+/*
+ * Convert from OETF'd BT.2100 YUV to RGB.
+ */
+Color bt2100YuvToRgb(Color e_gamma);
+
+/*
+ * Convert from scene luminance to HLG.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float hlgOetf(float e);
Color hlgOetf(Color e);
/*
+ * Convert from HLG to scene luminance.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float hlgInvOetf(float e_gamma);
+Color hlgInvOetf(Color e_gamma);
+
+/*
+ * Convert from scene luminance to PQ.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float pqOetf(float e);
+Color pqOetf(Color e);
+
+/*
+ * Convert from PQ to scene luminance in nits.
+ *
+ * [0.0, 1.0] range in and out.
+ */
+float pqInvOetf(float e_gamma);
+Color pqInvOetf(Color e_gamma);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Color space conversions
+
+/*
+ * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1.
+ *
+ * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the
+ * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is
+ * always the inverse of the RGB gamut to XYZ matrix.
+ */
+Color bt709ToP3(Color e);
+Color bt709ToBt2100(Color e);
+Color p3ToBt709(Color e);
+Color p3ToBt2100(Color e);
+Color bt2100ToBt709(Color e);
+Color bt2100ToP3(Color e);
+
+/*
+ * Identity conversion.
+ */
+inline Color identityConversion(Color e) { return e; }
+
+/*
+ * Get the conversion to apply to the HDR image for recovery map generation
+ */
+ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut);
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Recovery map calculations
+
+/*
* Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR
* luminances in linear space, and the hdr ratio to encode against.
*/
@@ -81,24 +253,43 @@
Color applyRecovery(Color e, float recovery, float hdr_ratio);
/*
- * Helper for sampling from images.
+ * Helper for sampling from YUV 420 images.
*/
Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
/*
+ * Helper for sampling from P010 images.
+ *
+ * Expect narrow-range image data for P010.
+ */
+Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
+
+/*
+ * Sample the image at the provided location, with a weighting based on nearby
+ * pixels and the map scale factor.
+ */
+Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
+ * Sample the image at the provided location, with a weighting based on nearby
+ * pixels and the map scale factor.
+ *
+ * Expect narrow-range image data for P010.
+ */
+Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+
+/*
* Sample the recovery value for the map from a given x,y coordinate on a scale
* that is map scale factor larger than the map size.
*/
float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
/*
- * Sample the image Y value at the provided location, with a weighting based on nearby pixels
- * and the map scale factor.
+ * Convert from Color to RGBA1010102.
*
- * Expect narrow-range image data for P010.
+ * Alpha always set to 1.0.
*/
-float sampleYuv420Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
-float sampleP010Y(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+uint32_t colorToRgba1010102(Color e_gamma);
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
new file mode 100644
index 0000000..e61d0c4
--- /dev/null
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+
+#include <sstream>
+#include <stdint.h>
+#include <string>
+#include <cstdio>
+
+namespace android::recoverymap {
+
+struct jpegr_metadata;
+
+/*
+ * Parses XMP packet and fills metadata with data from XMP
+ *
+ * @param xmp_data pointer to XMP packet
+ * @param xmp_size size of XMP packet
+ * @param metadata place to store HDR metadata values
+ * @return true if metadata is successfully retrieved, false otherwise
+*/
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+
+/*
+ * This method generates XMP metadata.
+ *
+ * below is an example of the XMP metadata that this function generates where
+ * secondary_image_length = 1000
+ * range_scaling_factor = 1.25
+ *
+ * <x:xmpmeta
+ * xmlns:x="adobe:ns:meta/"
+ * x:xmptk="Adobe XMP Core 5.1.2">
+ * <rdf:RDF
+ * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ * <rdf:Description
+ * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"
+ * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
+ * <GContainer:Version>1</GContainer:Version>
+ * <GContainer:Directory>
+ * <rdf:Seq>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="Primary"
+ * Item:Mime="image/jpeg"
+ * RecoveryMap:Version=”1”
+ * RecoveryMap:RangeScalingFactor=”1.25”
+ * RecoveryMap:TransferFunction=”2”/>
+ * <RecoveryMap:HDR10Metadata
+ * // some attributes
+ * // some elements
+ * </RecoveryMap:HDR10Metadata>
+ * </rdf:li>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="RecoveryMap"
+ * Item:Mime="image/jpeg"
+ * Item:Length="1000"/>
+ * </rdf:li>
+ * </rdf:Seq>
+ * </GContainer:Directory>
+ * </rdf:Description>
+ * </rdf:RDF>
+ * </x:xmpmeta>
+ *
+ * @param secondary_image_length length of secondary image
+ * @param metadata JPEG/R metadata to encode as XMP
+ * @return XMP metadata in type of string
+ */
+std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+}
+
+#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
index c1fb6c3..c2a8f45 100644
--- a/libs/jpegrecoverymap/jpegdecoder.cpp
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -20,8 +20,19 @@
#include <errno.h>
#include <setjmp.h>
+#include <string>
+
+using namespace std;
namespace android::recoverymap {
+
+const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
+const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
+const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
+
+const std::string kXmpNameSpace = "http://ns.adobe.com/xap/1.0/";
+const std::string kExifIdCode = "Exif";
+
struct jpegr_source_mgr : jpeg_source_mgr {
jpegr_source_mgr(const uint8_t* ptr, int len);
~jpegr_source_mgr();
@@ -76,6 +87,7 @@
}
JpegDecoder::JpegDecoder() {
+ mExifPos = 0;
}
JpegDecoder::~JpegDecoder() {
@@ -88,6 +100,7 @@
}
mResultBuffer.clear();
+ mXMPBuffer.clear();
if (!decode(image, length)) {
return false;
}
@@ -103,6 +116,22 @@
return mResultBuffer.size();
}
+void* JpegDecoder::getXMPPtr() {
+ return mXMPBuffer.data();
+}
+
+size_t JpegDecoder::getXMPSize() {
+ return mXMPBuffer.size();
+}
+
+void* JpegDecoder::getEXIFPtr() {
+ return mEXIFBuffer.data();
+}
+
+size_t JpegDecoder::getEXIFSize() {
+ return mEXIFBuffer.size();
+}
+
size_t JpegDecoder::getDecompressedImageWidth() {
return mWidth;
}
@@ -115,6 +144,7 @@
jpeg_decompress_struct cinfo;
jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
jpegrerror_mgr myerr;
+
cinfo.err = jpeg_std_error(&myerr.pub);
myerr.pub.error_exit = jpegrerror_exit;
@@ -124,9 +154,59 @@
}
jpeg_create_decompress(&cinfo);
+ jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+
cinfo.src = &mgr;
jpeg_read_header(&cinfo, TRUE);
+ // Save XMP data and EXIF data.
+ // Here we only handle the first XMP / EXIF package.
+ // The parameter pos is used for capturing start offset of EXIF, which is hacky, but working...
+ // We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
+ // two bytes of package length which is stored in marker->original_length, and the real data
+ // which is stored in marker->data. The pos is adding up all previous package lengths (
+ // 4 bytes marker and length, marker->original_length) before EXIF appears. Note that here we
+ // we are using marker->original_length instead of marker->data_length because in case the real
+ // package length is larger than the limitation, jpeg-turbo will only copy the data within the
+ // limitation (represented by data_length) and this may vary from original_length / real offset.
+ // A better solution is making jpeg_marker_struct holding the offset, but currently it doesn't.
+ bool exifAppears = false;
+ bool xmpAppears = false;
+ size_t pos = 2; // position after SOI
+ for (jpeg_marker_struct* marker = cinfo.marker_list;
+ marker && !(exifAppears && xmpAppears);
+ marker = marker->next) {
+
+ pos += 4;
+ pos += marker->original_length;
+
+ if (marker->marker != kAPP1Marker) {
+ continue;
+ }
+
+ const unsigned int len = marker->data_length;
+ if (!xmpAppears &&
+ len > kXmpNameSpace.size() &&
+ !strncmp(reinterpret_cast<const char*>(marker->data),
+ kXmpNameSpace.c_str(),
+ kXmpNameSpace.size())) {
+ mXMPBuffer.resize(len+1, 0);
+ memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
+ xmpAppears = true;
+ } else if (!exifAppears &&
+ len > kExifIdCode.size() &&
+ !strncmp(reinterpret_cast<const char*>(marker->data),
+ kExifIdCode.c_str(),
+ kExifIdCode.size())) {
+ mEXIFBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
+ exifAppears = true;
+ mExifPos = pos - marker->original_length;
+ }
+ }
+
mWidth = cinfo.image_width;
mHeight = cinfo.image_height;
@@ -153,6 +233,60 @@
return true;
}
+// TODO (Fyodor/Dichen): merge this method with getCompressedImageParameters() since they have
+// similar functionality. Yet Dichen is not familiar with who's calling
+// getCompressedImageParameters(), looks like it's used by some pending CLs.
+bool JpegDecoder::extractEXIF(const void* image, int length) {
+ jpeg_decompress_struct cinfo;
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ jpegrerror_mgr myerr;
+
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+
+ cinfo.src = &mgr;
+ jpeg_read_header(&cinfo, TRUE);
+
+ bool exifAppears = false;
+ size_t pos = 2; // position after SOI
+ for (jpeg_marker_struct* marker = cinfo.marker_list;
+ marker && !exifAppears;
+ marker = marker->next) {
+
+ pos += 4;
+ pos += marker->original_length;
+
+ if (marker->marker != kAPP1Marker) {
+ continue;
+ }
+
+ const unsigned int len = marker->data_length;
+ if (!exifAppears &&
+ len > kExifIdCode.size() &&
+ !strncmp(reinterpret_cast<const char*>(marker->data),
+ kExifIdCode.c_str(),
+ kExifIdCode.size())) {
+ mEXIFBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
+ exifAppears = true;
+ mExifPos = pos - marker->original_length;
+ }
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+ return true;
+}
+
bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
bool isSingleChannel) {
if (isSingleChannel) {
@@ -161,6 +295,43 @@
return decompressYUV(cinfo, dest);
}
+bool JpegDecoder::getCompressedImageParameters(const void* image, int length,
+ size_t *pWidth, size_t *pHeight,
+ std::vector<uint8_t> *&iccData , std::vector<uint8_t> *&exifData) {
+ jpeg_decompress_struct cinfo;
+ jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
+ jpegrerror_mgr myerr;
+ cinfo.err = jpeg_std_error(&myerr.pub);
+ myerr.pub.error_exit = jpegrerror_exit;
+
+ if (setjmp(myerr.setjmp_buffer)) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
+ jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
+
+ cinfo.src = &mgr;
+ if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
+ jpeg_destroy_decompress(&cinfo);
+ return false;
+ }
+
+ *pWidth = cinfo.image_width;
+ *pHeight = cinfo.image_height;
+
+ //TODO: Parse iccProfile and exifData
+ (void)iccData;
+ (void)exifData;
+
+
+ jpeg_destroy_decompress(&cinfo);
+ return true;
+}
+
+
bool JpegDecoder::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
JSAMPROW y[kCompressBatchSize];
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index bd16a68..53fa8ce 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -14,24 +14,26 @@
* limitations under the License.
*/
-// TODO: need to clean up handling around hdr_ratio and passing it around
-// TODO: need to handle color space information; currently we assume everything
-// is srgb in.
-// TODO: handle PQ encode/decode (currently only HLG)
-
#include <jpegrecoverymap/recoverymap.h>
#include <jpegrecoverymap/jpegencoder.h>
#include <jpegrecoverymap/jpegdecoder.h>
#include <jpegrecoverymap/recoverymapmath.h>
+#include <jpegrecoverymap/recoverymaputils.h>
#include <image_io/jpeg/jpeg_marker.h>
-#include <image_io/xml/xml_writer.h>
+#include <image_io/jpeg/jpeg_info.h>
+#include <image_io/jpeg/jpeg_scanner.h>
+#include <image_io/jpeg/jpeg_info_builder.h>
+#include <image_io/base/data_segment_data_source.h>
+#include <utils/Log.h>
#include <memory>
#include <sstream>
#include <string>
+#include <cmath>
using namespace std;
+using namespace photos_editing_formats::image_io;
namespace android::recoverymap {
@@ -43,22 +45,23 @@
} \
}
+// The current JPEGR version that we encode to
+static const uint32_t kJpegrVersion = 1;
+
// Map is quarter res / sixteenth size
static const size_t kMapDimensionScaleFactor = 4;
+// JPEG compress quality (0 ~ 100) for recovery map
+static const int kMapCompressQuality = 85;
-
-/*
- * Helper function used for generating XMP metadata.
- *
- * @param prefix The prefix part of the name.
- * @param suffix The suffix part of the name.
- * @return A name of the form "prefix:suffix".
- */
-string Name(const string &prefix, const string &suffix) {
- std::stringstream ss;
- ss << prefix << ":" << suffix;
- return ss.str();
-}
+// TODO: fill in st2086 metadata
+static const st2086_metadata kSt2086Metadata = {
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ 0,
+ 1.0f,
+};
/*
* Helper function used for writing data to destination.
@@ -70,7 +73,7 @@
* @return status of succeed or error code.
*/
status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
- if (position + length > destination->length) {
+ if (position + length > destination->maxLength) {
return ERROR_JPEGR_BUFFER_TOO_SMALL;
}
@@ -79,11 +82,186 @@
return NO_ERROR;
}
+status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position) {
+ memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
+ position += length;
+ return NO_ERROR;
+}
+
+// If the EXIF package doesn't exist in the input JPEG, we'll create one with one entry
+// where the length is represented by this value.
+const size_t PSEUDO_EXIF_PACKAGE_LENGTH = 28;
+// If the EXIF package exists in the input JPEG, we'll add an "JR" entry where the length is
+// represented by this value.
+const size_t EXIF_J_R_ENTRY_LENGTH = 12;
+
+/*
+ * Helper function
+ * Add J R entry to existing exif, or create a new one with J R entry if it's null.
+ * EXIF syntax / change:
+ * ori:
+ * FF E1 - APP1
+ * 01 FC - size of APP1 (to be calculated)
+ * -----------------------------------------------------
+ * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
+ * 49 49 2A 00 - TIFF Header
+ * 08 00 00 00 - offset to the IFD (image file directory)
+ * 06 00 - 6 entries
+ * 00 01 - Width Tag
+ * 03 00 - 'Short' type
+ * 01 00 00 00 - one entry
+ * 00 05 00 00 - image with 0x500
+ *--------------------------------------------------------------------------
+ * new:
+ * FF E1 - APP1
+ * 02 08 - new size, equals to old size + EXIF_J_R_ENTRY_LENGTH (12)
+ *-----------------------------------------------------
+ * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
+ * 49 49 2A 00 - TIFF Header
+ * 08 00 00 00 - offset to the IFD (image file directory)
+ * 07 00 - +1 entry
+ * 4A 52 Custom ('J''R') Tag
+ * 07 00 - Unknown type
+ * 01 00 00 00 - one element
+ * 00 00 00 00 - empty data
+ * 00 01 - Width Tag
+ * 03 00 - 'Short' type
+ * 01 00 00 00 - one entry
+ * 00 05 00 00 - image with 0x500
+ */
+status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {
+ if (exif == nullptr || exif->data == nullptr) {
+ uint8_t data[PSEUDO_EXIF_PACKAGE_LENGTH] = {
+ 0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
+ 0x49, 0x49, 0x2A, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00,
+ 0x4A, 0x52,
+ 0x07, 0x00,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ int pos = 0;
+ Write(dest, data, PSEUDO_EXIF_PACKAGE_LENGTH, pos);
+ return NO_ERROR;
+ }
+
+ int num_entry = 0;
+ uint8_t num_entry_low = 0;
+ uint8_t num_entry_high = 0;
+ bool use_big_endian = false;
+ if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4949) {
+ num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[14];
+ num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[15];
+ } else if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4d4d) {
+ use_big_endian = true;
+ num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[14];
+ num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[15];
+ } else {
+ return ERROR_JPEGR_METADATA_ERROR;
+ }
+ num_entry = (num_entry_high << 8) | num_entry_low;
+ num_entry += 1;
+ num_entry_low = num_entry & 0xff;
+ num_entry_high = (num_entry << 8) & 0xff;
+
+ int pos = 0;
+ Write(dest, (uint8_t*)exif->data, 14, pos);
+
+ if (use_big_endian) {
+ Write(dest, &num_entry_high, 1, pos);
+ Write(dest, &num_entry_low, 1, pos);
+ uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
+ 0x4A, 0x52,
+ 0x07, 0x00,
+ 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
+ } else {
+ Write(dest, &num_entry_low, 1, pos);
+ Write(dest, &num_entry_high, 1, pos);
+ uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
+ 0x4A, 0x52,
+ 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00};
+ Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
+ }
+
+ Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);
+
+ return NO_ERROR;
+}
+
+/* Encode API-0 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest,
int quality,
- jr_exif_ptr /* exif */) {
+ jr_exif_ptr exif) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (quality < 0 || quality > 100) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JpegEncoder jpeg_encoder;
+ // TODO: determine ICC data based on color gamut information
+ if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
+ uncompressed_yuv_420_image.width,
+ uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
+ return ERROR_JPEGR_ENCODE_ERROR;
+ }
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_encoder.getCompressedImagePtr();
+ jpeg.length = jpeg_encoder.getCompressedImageSize();
+
+ jpegr_exif_struct new_exif;
+ if (exif->data == nullptr) {
+ new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
+ } else {
+ new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
+ }
+ new_exif.data = new uint8_t[new_exif.length];
+ std::unique_ptr<uint8_t[]> new_exif_data;
+ new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
+ JPEGR_CHECK(updateExif(exif, &new_exif));
+
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+/* Encode API-1 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
|| dest == nullptr) {
@@ -99,21 +277,27 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
jpegr_uncompressed_struct map;
- float hdr_ratio = 0.0f;
JPEGR_CHECK(generateRecoveryMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio));
+ uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
jpegr_compressed_struct compressed_map;
- std::unique_ptr<uint8_t[]> compressed_map_data =
- std::make_unique<uint8_t[]>(map.width * map.height);
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
JpegEncoder jpeg_encoder;
- // TODO: ICC data - need color space information
+ // TODO: determine ICC data based on color gamut information
if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
uncompressed_yuv_420_image->width,
uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
@@ -123,14 +307,28 @@
jpeg.data = jpeg_encoder.getCompressedImagePtr();
jpeg.length = jpeg_encoder.getCompressedImageSize();
- JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, hdr_ratio, dest));
+ jpegr_exif_struct new_exif;
+ if (exif == nullptr || exif->data == nullptr) {
+ new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
+ } else {
+ new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
+ }
+
+ new_exif.data = new uint8_t[new_exif.length];
+ std::unique_ptr<uint8_t[]> new_exif_data;
+ new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
+ JPEGR_CHECK(updateExif(exif, &new_exif));
+
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
return NO_ERROR;
}
+/* Encode API-2 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
@@ -144,26 +342,68 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
jpegr_uncompressed_struct map;
- float hdr_ratio = 0.0f;
JPEGR_CHECK(generateRecoveryMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio));
+ uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
jpegr_compressed_struct compressed_map;
- std::unique_ptr<uint8_t[]> compressed_map_data =
- std::make_unique<uint8_t[]>(map.width * map.height);
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest));
+ // Extract EXIF from JPEG without decoding.
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ jpegr_exif_struct exif;
+ exif.data = nullptr;
+ exif.length = 0;
+ // Delete EXIF package if it appears, and update exif.
+ if (jpeg_decoder.getEXIFPos() != 0) {
+ int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
+ memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
+ (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
+ + jpeg_decoder.getEXIFSize(),
+ compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
+ compressed_jpeg_image->length = new_length;
+ exif.data = jpeg_decoder.getEXIFPtr();
+ exif.length = jpeg_decoder.getEXIFSize();
+ }
+
+ jpegr_exif_struct new_exif;
+ if (exif.data == nullptr) {
+ new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
+ } else {
+ new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
+ }
+
+ new_exif.data = new uint8_t[new_exif.length];
+ std::unique_ptr<uint8_t[]> new_exif_data;
+ new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
+ JPEGR_CHECK(updateExif(&exif, &new_exif));
+
+ JPEGR_CHECK(appendRecoveryMap(
+ compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
return NO_ERROR;
}
+/* Encode API-3 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| compressed_jpeg_image == nullptr
@@ -179,74 +419,136 @@
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
+ uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
+
+ jpegr_exif_struct exif;
+ exif.data = nullptr;
+ exif.length = 0;
+ // Delete EXIF package if it appears, and update exif.
+ if (jpeg_decoder.getEXIFPos() != 0) {
+ int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
+ memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
+ (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
+ + jpeg_decoder.getEXIFSize(),
+ compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
+ compressed_jpeg_image->length = new_length;
+ exif.data = jpeg_decoder.getEXIFPtr();
+ exif.length = jpeg_decoder.getEXIFSize();
+ }
+
+ jpegr_exif_struct new_exif;
+ if (exif.data == nullptr) {
+ new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
+ } else {
+ new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
+ }
+ new_exif.data = new uint8_t[new_exif.length];
+ std::unique_ptr<uint8_t[]> new_exif_data;
+ new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
+ JPEGR_CHECK(updateExif(&exif, &new_exif));
if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
|| uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
jpegr_uncompressed_struct map;
- float hdr_ratio = 0.0f;
JPEGR_CHECK(generateRecoveryMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio));
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
jpegr_compressed_struct compressed_map;
- std::unique_ptr<uint8_t[]> compressed_map_data =
- std::make_unique<uint8_t[]>(map.width * map.height);
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest));
+ JPEGR_CHECK(appendRecoveryMap(
+ compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
return NO_ERROR;
}
+status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
+ jr_info_ptr jpegr_info) {
+ if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ jpegr_compressed_struct primary_image, recovery_map;
+ JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
+ &primary_image, &recovery_map));
+
+ JpegDecoder jpeg_decoder;
+ if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
+ &jpegr_info->width, &jpegr_info->height,
+ jpegr_info->iccData, jpegr_info->exifData)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+/* Decode API */
status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
- jr_exif_ptr /* exif */,
- bool /* request_sdr */) {
+ jr_exif_ptr exif,
+ bool request_sdr) {
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
+ // TODO: fill EXIF data
+ (void) exif;
+
jpegr_compressed_struct compressed_map;
+ jpegr_metadata metadata;
JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
- jpegr_uncompressed_struct map;
- JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map));
JpegDecoder jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
+ JpegDecoder recovery_map_decoder;
+ if (!recovery_map_decoder.decompressImage(compressed_map.data,
+ compressed_map.length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ jpegr_uncompressed_struct map;
+ map.data = recovery_map_decoder.getDecompressedImagePtr();
+ map.width = recovery_map_decoder.getDecompressedImageWidth();
+ map.height = recovery_map_decoder.getDecompressedImageHeight();
+
+
jpegr_uncompressed_struct uncompressed_yuv_420_image;
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
- JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, dest));
-
- return NO_ERROR;
-}
-
-status_t RecoveryMap::decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map,
- jr_uncompressed_ptr dest) {
- if (compressed_recovery_map == nullptr || dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- JpegDecoder jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_recovery_map->data,
- compressed_recovery_map->length)) {
+ if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
+ jpeg_decoder.getXMPSize(), &metadata)) {
return ERROR_JPEGR_DECODE_ERROR;
}
- dest->data = jpeg_decoder.getDecompressedImagePtr();
- dest->width = jpeg_decoder.getDecompressedImageWidth();
- dest->height = jpeg_decoder.getDecompressedImageHeight();
+ if (request_sdr) {
+ memcpy(dest->data, uncompressed_yuv_420_image.data,
+ uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
+ dest->width = uncompressed_yuv_420_image.width;
+ dest->height = uncompressed_yuv_420_image.height;
+ } else {
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+ }
return NO_ERROR;
}
@@ -257,30 +559,36 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TODO: should we have ICC data?
+ // TODO: should we have ICC data for the map?
JpegEncoder jpeg_encoder;
- if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data, uncompressed_recovery_map->width,
- uncompressed_recovery_map->height, 85, nullptr, 0,
+ if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
+ uncompressed_recovery_map->width,
+ uncompressed_recovery_map->height,
+ kMapCompressQuality,
+ nullptr,
+ 0,
true /* isSingleChannel */)) {
return ERROR_JPEGR_ENCODE_ERROR;
}
- if (dest->length < jpeg_encoder.getCompressedImageSize()) {
+ if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
return ERROR_JPEGR_BUFFER_TOO_SMALL;
}
memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
dest->length = jpeg_encoder.getCompressedImageSize();
+ dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
return NO_ERROR;
}
status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr dest,
- float &hdr_ratio) {
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_p010_image == nullptr
+ || metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -290,6 +598,11 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
+ || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
size_t image_width = uncompressed_yuv_420_image->width;
size_t image_height = uncompressed_yuv_420_image->height;
size_t map_width = image_width / kMapDimensionScaleFactor;
@@ -297,35 +610,87 @@
dest->width = map_width;
dest->height = map_height;
+ dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
dest->data = new uint8_t[map_width * map_height];
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
- uint16_t yp_hdr_max = 0;
+ ColorTransformFn hdrInvOetf = nullptr;
+ float hdr_white_nits = 0.0f;
+ switch (metadata->transferFunction) {
+ case JPEGR_TF_LINEAR:
+ hdrInvOetf = identityConversion;
+ break;
+ case JPEGR_TF_HLG:
+ hdrInvOetf = hlgInvOetf;
+ hdr_white_nits = kHlgMaxNits;
+ break;
+ case JPEGR_TF_PQ:
+ hdrInvOetf = pqInvOetf;
+ hdr_white_nits = kPqMaxNits;
+ break;
+ }
+
+ ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
+ uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
+
+ ColorCalculationFn luminanceFn = nullptr;
+ switch (uncompressed_yuv_420_image->colorGamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ luminanceFn = srgbLuminance;
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ luminanceFn = p3Luminance;
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ luminanceFn = bt2100Luminance;
+ break;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ // Should be impossible to hit after input validation.
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
+ float hdr_y_nits_max = 0.0f;
+ double hdr_y_nits_avg = 0.0f;
for (size_t y = 0; y < image_height; ++y) {
for (size_t x = 0; x < image_width; ++x) {
- size_t pixel_idx = x + y * image_width;
- uint16_t yp_hdr = reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->data)[pixel_idx];
- if (yp_hdr > yp_hdr_max) {
- yp_hdr_max = yp_hdr;
+ Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
+ Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
+ hdr_rgb = hdrGamutConversionFn(hdr_rgb);
+ float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
+
+ hdr_y_nits_avg += hdr_y_nits;
+ if (hdr_y_nits > hdr_y_nits_max) {
+ hdr_y_nits_max = hdr_y_nits;
}
}
}
+ hdr_y_nits_avg /= image_width * image_height;
- float y_hdr_max_nits = hlgInvOetf(yp_hdr_max);
- hdr_ratio = y_hdr_max_nits / kSdrWhiteNits;
+ metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
+ if (metadata->transferFunction == JPEGR_TF_PQ) {
+ metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
+ metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
+ }
for (size_t y = 0; y < map_height; ++y) {
for (size_t x = 0; x < map_width; ++x) {
- float yp_sdr = sampleYuv420Y(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
- float yp_hdr = sampleP010Y(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
+ Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
+ kMapDimensionScaleFactor, x, y);
+ Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
+ Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
+ float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
- float y_sdr_nits = srgbInvOetf(yp_sdr);
- float y_hdr_nits = hlgInvOetf(yp_hdr);
+ Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
+ Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
+ Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
+ hdr_rgb = hdrGamutConversionFn(hdr_rgb);
+ float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
size_t pixel_idx = x + y * map_width;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeRecovery(y_sdr_nits, y_hdr_nits, hdr_ratio);
+ encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
}
}
@@ -335,17 +700,15 @@
status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_recovery_map == nullptr
+ || metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TODO: need to get this from the XMP; should probably be a function
- // parameter
- float hdr_ratio = 4.0f;
-
size_t width = uncompressed_yuv_420_image->width;
size_t height = uncompressed_yuv_420_image->height;
@@ -353,134 +716,193 @@
dest->height = height;
size_t pixel_count = width * height;
+ ColorTransformFn hdrOetf = nullptr;
+ switch (metadata->transferFunction) {
+ case JPEGR_TF_LINEAR:
+ hdrOetf = identityConversion;
+ break;
+ case JPEGR_TF_HLG:
+ hdrOetf = hlgOetf;
+ break;
+ case JPEGR_TF_PQ:
+ hdrOetf = pqOetf;
+ break;
+ }
+
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
- size_t pixel_y_idx = x + y * width;
+ Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
+ Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
+ Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
- size_t pixel_uv_idx = x / 2 + (y / 2) * (width / 2);
-
- Color ypuv_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
- Color rgbp_sdr = srgbYuvToRgb(ypuv_sdr);
- Color rgb_sdr = srgbInvOetf(rgbp_sdr);
-
+ // TODO: determine map scaling factor based on actual map dims
float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
- Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
+ Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
- Color rgbp_hdr = hlgOetf(rgb_hdr);
- Color ypuv_hdr = bt2100RgbToYuv(rgbp_hdr);
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
+ uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
- reinterpret_cast<uint16_t*>(dest->data)[pixel_y_idx] = ypuv_hdr.r;
- reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx] = ypuv_hdr.g;
- reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx + 1] = ypuv_hdr.b;
+ size_t pixel_idx = x + y * width;
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
}
}
+ return NO_ERROR;
+}
+
+status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr primary_image,
+ jr_compressed_ptr recovery_map) {
+ if (compressed_jpegr_image == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ MessageHandler msg_handler;
+ std::shared_ptr<DataSegment> seg =
+ DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
+ static_cast<const uint8_t*>(compressed_jpegr_image->data),
+ DataSegment::BufferDispositionPolicy::kDontDelete);
+ DataSegmentDataSource data_source(seg);
+ JpegInfoBuilder jpeg_info_builder;
+ jpeg_info_builder.SetImageLimit(2);
+ JpegScanner jpeg_scanner(&msg_handler);
+ jpeg_scanner.Run(&data_source, &jpeg_info_builder);
+ data_source.Reset();
+
+ if (jpeg_scanner.HasError()) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ const auto& jpeg_info = jpeg_info_builder.GetInfo();
+ const auto& image_ranges = jpeg_info.GetImageRanges();
+ if (image_ranges.empty()) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (image_ranges.size() != 2) {
+ // Must be 2 JPEG Images
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ if (primary_image != nullptr) {
+ primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+ image_ranges[0].GetBegin();
+ primary_image->length = image_ranges[0].GetLength();
+ }
+
+ if (recovery_map != nullptr) {
+ recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+ image_ranges[1].GetBegin();
+ recovery_map->length = image_ranges[1].GetLength();
+ }
return NO_ERROR;
}
+
status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
jr_compressed_ptr dest) {
if (compressed_jpegr_image == nullptr || dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TBD
- return NO_ERROR;
+ return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
}
+// JPEG/R structure:
+// SOI (ff d8)
+// APP1 (ff e1)
+// 2 bytes of length (2 + length of exif package)
+// EXIF package (this includes the first two bytes representing the package length)
+// APP1 (ff e1)
+// 2 bytes of length (2 + 29 + length of xmp package)
+// name space ("http://ns.adobe.com/xap/1.0/\0")
+// xmp
+// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
+// secondary image (the recovery map)
+//
+// Metadata versions we are using:
+// ECMA TR-98 for JFIF marker
+// Exif 2.2 spec for EXIF marker
+// Adobe XMP spec part 3 for XMP marker
+// ICC v4.3 spec for ICC
status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
jr_compressed_ptr compressed_recovery_map,
- float hdr_ratio,
+ jr_exif_ptr exif,
+ jr_metadata_ptr metadata,
jr_compressed_ptr dest) {
if (compressed_jpeg_image == nullptr
|| compressed_recovery_map == nullptr
+ || exif == nullptr
+ || metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- string xmp = generateXmp(compressed_recovery_map->length, hdr_ratio);
- string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
-
- // 2 bytes: APP1 sign (ff e1)
- // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0"
- // x bytes: length of xmp packet
- int length = 2 + nameSpace.size() + xmp.size();
- uint8_t lengthH = ((length >> 8) & 0xff);
- uint8_t lengthL = (length & 0xff);
-
int pos = 0;
- // JPEG/R structure:
- // SOI (ff d8)
- // APP1 (ff e1)
- // 2 bytes of length (2 + 29 + length of xmp packet)
- // name space ("http://ns.adobe.com/xap/1.0/\0")
- // xmp
- // primary image (without the first two bytes, the SOI sign)
- // secondary image (the recovery map)
+ // Write SOI
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpace.size(), pos));
- JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
+
+ // Write EXIF
+ {
+ const int length = 2 + exif->length;
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, exif->data, exif->length, pos));
+ }
+
+ // Prepare and write XMP
+ {
+ const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
+ const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+ const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
+ // 2 bytes: representing the length of the package
+ // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
+ // x bytes: length of xmp packet
+ const int length = 3 + nameSpaceLength + xmp.size();
+ const uint8_t lengthH = ((length >> 8) & 0xff);
+ const uint8_t lengthL = (length & 0xff);
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+ JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+ JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+ JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
+ JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
+ }
+
+ // Write primary image
JPEGR_CHECK(Write(dest,
(uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
+
+ // Write secondary image
JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
+
+ // Set back length
dest->length = pos;
+ // Done!
return NO_ERROR;
}
-string RecoveryMap::generateXmp(int secondary_image_length, float hdr_ratio) {
- const string kContainerPrefix = "GContainer";
- const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
- const string kItemPrefix = "Item";
- const string kRecoveryMap = "RecoveryMap";
- const string kDirectory = "Directory";
- const string kImageJpeg = "image/jpeg";
- const string kItem = "Item";
- const string kLength = "Length";
- const string kMime = "Mime";
- const string kPrimary = "Primary";
- const string kSemantic = "Semantic";
- const string kVersion = "Version";
- const int kVersionValue = 1;
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
- const string kConDir = Name(kContainerPrefix, kDirectory);
- const string kContainerItem = Name(kContainerPrefix, kItem);
- const string kItemLength = Name(kItemPrefix, kLength);
- const string kItemMime = Name(kItemPrefix, kMime);
- const string kItemSemantic = Name(kItemPrefix, kSemantic);
+ dest->width = uncompressed_p010_image->width;
+ dest->height = uncompressed_p010_image->height;
+ unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
+ dest->data = dest_data.get();
- const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
- const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+ // TODO: Tone map algorighm here.
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kContainerPrefix, kContainerUri);
- writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kVersionValue);
- writer.WriteElementAndContent(Name(kContainerPrefix, "HdrRatio"), hdr_ratio);
- writer.StartWritingElements(kConDirSeq);
- size_t item_depth = writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
- writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
- writer.FinishWritingElementsToDepth(item_depth);
- writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
- writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
- writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
- writer.FinishWriting();
-
- return ss.str();
+ return NO_ERROR;
}
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 368783f..9ed2949 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -20,43 +20,117 @@
namespace android::recoverymap {
-static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
-static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
+////////////////////////////////////////////////////////////////////////////////
+// sRGB transformations
-Color bt2100RgbToYuv(Color e) {
- float yp = kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
- return {{{yp, (e.b - yp) / kBt2100Cb, (e.r - yp) / kBt2100Cr }}};
+// See IEC 61966-2-1, Equation F.7.
+static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
+
+float srgbLuminance(Color e) {
+ return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
}
+// See ECMA TR/98, Section 7.
static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f;
-Color srgbYuvToRgb(Color e) {
- return {{{ e.y + kSrgbRCr * e.v, e.y - kSrgbGCb * e.u - kSrgbGCr * e.v, e.y + kSrgbBCb * e.u }}};
+Color srgbYuvToRgb(Color e_gamma) {
+ return {{{ e_gamma.y + kSrgbRCr * e_gamma.v,
+ e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v,
+ e_gamma.y + kSrgbBCb * e_gamma.u }}};
}
-float srgbInvOetf(float e) {
- if (e <= 0.04045f) {
- return e / 12.92f;
+// See ECMA TR/98, Section 7.
+static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f;
+static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f;
+static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f;
+
+Color srgbRgbToYuv(Color e_gamma) {
+ return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b,
+ kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b,
+ kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}};
+}
+
+// See IEC 61966-2-1, Equations F.5 and F.6.
+float srgbInvOetf(float e_gamma) {
+ if (e_gamma <= 0.04045f) {
+ return e_gamma / 12.92f;
} else {
- return pow((e + 0.055f) / 1.055f, 2.4);
+ return pow((e_gamma + 0.055f) / 1.055f, 2.4);
}
}
-Color srgbInvOetf(Color e) {
- return {{{ srgbInvOetf(e.r), srgbInvOetf(e.g), srgbInvOetf(e.b) }}};
+Color srgbInvOetf(Color e_gamma) {
+ return {{{ srgbInvOetf(e_gamma.r),
+ srgbInvOetf(e_gamma.g),
+ srgbInvOetf(e_gamma.b) }}};
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Display-P3 transformations
+
+// See SMPTE EG 432-1, Table 7-2.
+static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
+
+float p3Luminance(Color e) {
+ return kP3R * e.r + kP3G * e.g + kP3B * e.b;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// BT.2100 transformations - according to ITU-R BT.2100-2
+
+// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
+static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
+
+float bt2100Luminance(Color e) {
+ return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
+}
+
+// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
+static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
+
+Color bt2100RgbToYuv(Color e_gamma) {
+ float y_gamma = bt2100Luminance(e_gamma);
+ return {{{ y_gamma,
+ (e_gamma.b - y_gamma) / kBt2100Cb,
+ (e_gamma.r - y_gamma) / kBt2100Cr }}};
+}
+
+// Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty
+// straight forward; we just invert the formulas for U and V above. But deriving
+// the formula for G is a bit more complicated:
+//
+// Start with equation for luminance:
+// Y = kBt2100R * R + kBt2100G * G + kBt2100B * B
+// Solve for G:
+// G = (Y - kBt2100R * R - kBt2100B * B) / kBt2100B
+// Substitute equations for R and B in terms YUV:
+// G = (Y - kBt2100R * (Y + kBt2100Cr * V) - kBt2100B * (Y + kBt2100Cb * U)) / kBt2100B
+// Simplify:
+// G = Y * ((1 - kBt2100R - kBt2100B) / kBt2100G)
+// + U * (kBt2100B * kBt2100Cb / kBt2100G)
+// + V * (kBt2100R * kBt2100Cr / kBt2100G)
+//
+// We then get the following coeficients for calculating G from YUV:
+//
+// Coef for Y = (1 - kBt2100R - kBt2100B) / kBt2100G = 1
+// Coef for U = kBt2100B * kBt2100Cb / kBt2100G = kBt2100GCb = ~0.1645
+// Coef for V = kBt2100R * kBt2100Cr / kBt2100G = kBt2100GCr = ~0.5713
+
+static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G;
+static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G;
+
+Color bt2100YuvToRgb(Color e_gamma) {
+ return {{{ e_gamma.y + kBt2100Cr * e_gamma.v,
+ e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v,
+ e_gamma.y + kBt2100Cb * e_gamma.u }}};
+}
+
+// See ITU-R BT.2100-2, Table 5, HLG Reference OETF.
static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
-float hlgInvOetf(float e) {
- if (e <= 0.5f) {
- return pow(e, 2.0f) / 3.0f;
- } else {
- return (exp((e - kHlgC) / kHlgA) + kHlgB) / 12.0f;
- }
-}
-
-static float hlgOetf(float e) {
+float hlgOetf(float e) {
if (e <= 1.0f/12.0f) {
return sqrt(3.0f * e);
} else {
@@ -68,19 +142,160 @@
return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
}
+// See ITU-R BT.2100-2, Table 5, HLG Reference EOTF.
+float hlgInvOetf(float e_gamma) {
+ if (e_gamma <= 0.5f) {
+ return pow(e_gamma, 2.0f) / 3.0f;
+ } else {
+ return (exp((e_gamma - kHlgC) / kHlgA) + kHlgB) / 12.0f;
+ }
+}
+
+Color hlgInvOetf(Color e_gamma) {
+ return {{{ hlgInvOetf(e_gamma.r),
+ hlgInvOetf(e_gamma.g),
+ hlgInvOetf(e_gamma.b) }}};
+}
+
+// See ITU-R BT.2100-2, Table 4, Reference PQ OETF.
+static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
+static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
+ kPqC3 = 2392.0f / 4096.0f * 32.0f;
+
+float pqOetf(float e) {
+ if (e <= 0.0f) return 0.0f;
+ return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)),
+ kPqM2);
+}
+
+Color pqOetf(Color e) {
+ return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
+}
+
+// Derived from the inverse of the Reference PQ OETF.
+static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f,
+ kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f;
+
+float pqInvOetf(float e_gamma) {
+ // This equation blows up if e_gamma is 0.0, and checking on <= 0.0 doesn't
+ // always catch 0.0. So, check on 0.0001, since anything this small will
+ // effectively be crushed to zero anyways.
+ if (e_gamma <= 0.0001f) return 0.0f;
+ return pow((kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB)
+ / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)),
+ kPqInvE);
+}
+
+Color pqInvOetf(Color e_gamma) {
+ return {{{ pqInvOetf(e_gamma.r),
+ pqInvOetf(e_gamma.g),
+ pqInvOetf(e_gamma.b) }}};
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Color conversions
+
+Color bt709ToP3(Color e) {
+ return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
+ 0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
+ 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}};
+}
+
+Color bt709ToBt2100(Color e) {
+ return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
+ 0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
+ 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}};
+}
+
+Color p3ToBt709(Color e) {
+ return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
+ -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
+ -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}};
+}
+
+Color p3ToBt2100(Color e) {
+ return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
+ 0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
+ -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}};
+}
+
+Color bt2100ToBt709(Color e) {
+ return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
+ -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
+ -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}};
+}
+
+Color bt2100ToP3(Color e) {
+ return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
+ -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
+ 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b
+ }}};
+}
+
+// TODO: confirm we always want to convert like this before calculating
+// luminance.
+ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) {
+ switch (sdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_P3:
+ return p3ToBt709;
+ case JPEGR_COLORGAMUT_BT2100:
+ return bt2100ToBt709;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return bt709ToP3;
+ case JPEGR_COLORGAMUT_P3:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_BT2100:
+ return bt2100ToP3;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return bt709ToBt2100;
+ case JPEGR_COLORGAMUT_P3:
+ return p3ToBt2100;
+ case JPEGR_COLORGAMUT_BT2100:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Recovery map calculations
+
uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) {
float gain = 1.0f;
if (y_sdr > 0.0f) {
gain = y_hdr / y_sdr;
}
- if (gain < -hdr_ratio) gain = -hdr_ratio;
+ if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio;
if (gain > hdr_ratio) gain = hdr_ratio;
return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f + 127.5f);
}
static float applyRecovery(float e, float recovery, float hdr_ratio) {
+ if (e <= 0.0f) return 0.0f;
return exp2(log2(e) + recovery * log2(hdr_ratio));
}
@@ -90,36 +305,6 @@
applyRecovery(e.b, recovery, hdr_ratio) }}};
}
-// TODO: do we need something more clever for filtering either the map or images
-// to generate the map?
-
-static float mapUintToFloat(uint8_t map_uint) {
- return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
-}
-
-float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
- float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
- float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
-
- size_t x_lower = static_cast<size_t>(floor(x_map));
- size_t x_upper = x_lower + 1;
- size_t y_lower = static_cast<size_t>(floor(y_map));
- size_t y_upper = y_lower + 1;
-
- float x_influence = x_map - static_cast<float>(x_lower);
- float y_influence = y_map - static_cast<float>(y_lower);
-
- float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
- float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
- float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
- float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
-
- return e1 * (x_influence + y_influence) / 2.0f
- + e2 * (x_influence + 1.0f - y_influence) / 2.0f
- + e3 * (1.0f - x_influence + y_influence) / 2.0f
- + e4 * (1.0f - x_influence + 1.0f - y_influence) / 2.0f;
-}
-
Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
size_t pixel_count = image->width * image->height;
@@ -137,40 +322,116 @@
(static_cast<float>(v_uint) - 128.0f) / 255.0f }}};
}
-typedef float (*sampleComponentFn)(jr_uncompressed_ptr, size_t, size_t);
+Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
+ size_t pixel_count = image->width * image->height;
-static float sampleComponent(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
- sampleComponentFn sample_fn) {
- float e = 0.0f;
+ size_t pixel_y_idx = x + y * image->width;
+ size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
+
+ uint16_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_y_idx]
+ >> 6;
+ uint16_t u_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2]
+ >> 6;
+ uint16_t v_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_count + pixel_uv_idx * 2 + 1]
+ >> 6;
+
+ // Conversions include taking narrow-range into account.
+ return {{{ (static_cast<float>(y_uint) - 64.0f) / 876.0f,
+ (static_cast<float>(u_uint) - 64.0f) / 896.0f - 0.5f,
+ (static_cast<float>(v_uint) - 64.0f) / 896.0f - 0.5f }}};
+}
+
+typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t);
+
+static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
+ getPixelFn get_pixel_fn) {
+ Color e = {{{ 0.0f, 0.0f, 0.0f }}};
for (size_t dy = 0; dy < map_scale_factor; ++dy) {
for (size_t dx = 0; dx < map_scale_factor; ++dx) {
- e += sample_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
+ e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
}
}
return e / static_cast<float>(map_scale_factor * map_scale_factor);
}
-static float getYuv420Y(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t pixel_idx = x + y * image->width;
- uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_idx];
- return static_cast<float>(y_uint) / 255.0f;
+Color sampleYuv420(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
+ return samplePixels(image, map_scale_factor, x, y, getYuv420Pixel);
}
-
-float sampleYuv420Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
- return sampleComponent(image, map_scale_factor, x, y, getYuv420Y);
+Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
+ return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
}
-static float getP010Y(jr_uncompressed_ptr image, size_t x, size_t y) {
- size_t pixel_idx = x + y * image->width;
- uint8_t y_uint = reinterpret_cast<uint16_t*>(image->data)[pixel_idx];
- // Expecting narrow range input
- return (static_cast<float>(y_uint) - 64.0f) / 960.0f;
+// TODO: do we need something more clever for filtering either the map or images
+// to generate the map?
+
+static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
+ return val < low ? low : (high < val ? high : val);
}
-float sampleP010Y(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
- return sampleComponent(image, map_scale_factor, x, y, getP010Y);
+static float mapUintToFloat(uint8_t map_uint) {
+ return (static_cast<float>(map_uint) - 127.5f) / 127.5f;
+}
+
+static float pythDistance(float x_diff, float y_diff) {
+ return sqrt(pow(x_diff, 2.0f) + pow(y_diff, 2.0f));
+}
+
+float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) {
+ float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor);
+ float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor);
+
+ size_t x_lower = static_cast<size_t>(floor(x_map));
+ size_t x_upper = x_lower + 1;
+ size_t y_lower = static_cast<size_t>(floor(y_map));
+ size_t y_upper = y_lower + 1;
+
+ x_lower = clamp(x_lower, 0, map->width - 1);
+ x_upper = clamp(x_upper, 0, map->width - 1);
+ y_lower = clamp(y_lower, 0, map->height - 1);
+ y_upper = clamp(y_upper, 0, map->height - 1);
+
+ // Use Shepard's method for inverse distance weighting. For more information:
+ // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
+
+ float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
+ float e1_dist = pythDistance(x_map - static_cast<float>(x_lower),
+ y_map - static_cast<float>(y_lower));
+ if (e1_dist == 0.0f) return e1;
+
+ float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
+ float e2_dist = pythDistance(x_map - static_cast<float>(x_lower),
+ y_map - static_cast<float>(y_upper));
+ if (e2_dist == 0.0f) return e2;
+
+ float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
+ float e3_dist = pythDistance(x_map - static_cast<float>(x_upper),
+ y_map - static_cast<float>(y_lower));
+ if (e3_dist == 0.0f) return e3;
+
+ float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
+ float e4_dist = pythDistance(x_map - static_cast<float>(x_upper),
+ y_map - static_cast<float>(y_upper));
+ if (e4_dist == 0.0f) return e2;
+
+ float e1_weight = 1.0f / e1_dist;
+ float e2_weight = 1.0f / e2_dist;
+ float e3_weight = 1.0f / e3_dist;
+ float e4_weight = 1.0f / e4_dist;
+ float total_weight = e1_weight + e2_weight + e3_weight + e4_weight;
+
+ return e1 * (e1_weight / total_weight)
+ + e2 * (e2_weight / total_weight)
+ + e3 * (e3_weight / total_weight)
+ + e4 * (e4_weight / total_weight);
+}
+
+uint32_t colorToRgba1010102(Color e_gamma) {
+ return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
+ | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
+ | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20)
+ | (0x3 << 30); // Set alpha to 1.0
}
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
new file mode 100644
index 0000000..63b25f7
--- /dev/null
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jpegrecoverymap/recoverymaputils.h>
+#include <jpegrecoverymap/recoverymap.h>
+#include <image_io/xml/xml_reader.h>
+#include <image_io/xml/xml_writer.h>
+#include <image_io/base/message_handler.h>
+#include <image_io/xml/xml_element_rules.h>
+#include <image_io/xml/xml_handler.h>
+#include <image_io/xml/xml_rule.h>
+
+using namespace photos_editing_formats::image_io;
+using namespace std;
+
+namespace android::recoverymap {
+
+/*
+ * Helper function used for generating XMP metadata.
+ *
+ * @param prefix The prefix part of the name.
+ * @param suffix The suffix part of the name.
+ * @return A name of the form "prefix:suffix".
+ */
+string Name(const string &prefix, const string &suffix) {
+ std::stringstream ss;
+ ss << prefix << ":" << suffix;
+ return ss.str();
+}
+
+// Extremely simple XML Handler - just searches for interesting elements
+class XMPXmlHandler : public XmlHandler {
+public:
+
+ XMPXmlHandler() : XmlHandler() {
+ gContainerItemState = NotStrarted;
+ }
+
+ enum ParseState {
+ NotStrarted,
+ Started,
+ Done
+ };
+
+ virtual DataMatchResult StartElement(const XmlTokenContext& context) {
+ string val;
+ if (context.BuildTokenValue(&val)) {
+ if (!val.compare(gContainerItemName)) {
+ gContainerItemState = Started;
+ } else {
+ if (gContainerItemState != Done) {
+ gContainerItemState = NotStrarted;
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
+ if (gContainerItemState == Started) {
+ gContainerItemState = Done;
+ lastAttributeName = "";
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
+ string val;
+ if (gContainerItemState == Started) {
+ if (context.BuildTokenValue(&val)) {
+ if (!val.compare(rangeScalingFactorAttrName)) {
+ lastAttributeName = rangeScalingFactorAttrName;
+ } else if (!val.compare(transferFunctionAttrName)) {
+ lastAttributeName = transferFunctionAttrName;
+ } else {
+ lastAttributeName = "";
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
+ string val;
+ if (gContainerItemState == Started) {
+ if (context.BuildTokenValue(&val, true)) {
+ if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
+ rangeScalingFactorStr = val;
+ } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
+ transferFunctionStr = val;
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ bool getRangeScalingFactor(float* scaling_factor) {
+ if (gContainerItemState == Done) {
+ stringstream ss(rangeScalingFactorStr);
+ float val;
+ if (ss >> val) {
+ *scaling_factor = val;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ bool getTransferFunction(jpegr_transfer_function* transfer_function) {
+ if (gContainerItemState == Done) {
+ stringstream ss(transferFunctionStr);
+ int val;
+ if (ss >> val) {
+ *transfer_function = static_cast<jpegr_transfer_function>(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+private:
+ static const string gContainerItemName;
+ static const string rangeScalingFactorAttrName;
+ static const string transferFunctionAttrName;
+ string rangeScalingFactorStr;
+ string transferFunctionStr;
+ string lastAttributeName;
+ ParseState gContainerItemState;
+};
+
+const string XMPXmlHandler::gContainerItemName = "GContainer:Item";
+const string XMPXmlHandler::rangeScalingFactorAttrName = "RecoveryMap:RangeScalingFactor";
+const string XMPXmlHandler::transferFunctionAttrName = "RecoveryMap:TransferFunction";
+
+
+const string kContainerPrefix = "GContainer";
+const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
+const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
+const string kItemPrefix = "Item";
+const string kRecoveryMap = "RecoveryMap";
+const string kDirectory = "Directory";
+const string kImageJpeg = "image/jpeg";
+const string kItem = "Item";
+const string kLength = "Length";
+const string kMime = "Mime";
+const string kPrimary = "Primary";
+const string kSemantic = "Semantic";
+const string kVersion = "Version";
+const string kHdr10Metadata = "HDR10Metadata";
+const string kSt2086Metadata = "ST2086Metadata";
+const string kSt2086Coordinate = "ST2086Coordinate";
+const string kSt2086CoordinateX = "ST2086CoordinateX";
+const string kSt2086CoordinateY = "ST2086CoordinateY";
+const string kSt2086Primary = "ST2086Primary";
+const int kSt2086PrimaryRed = 0;
+const int kSt2086PrimaryGreen = 1;
+const int kSt2086PrimaryBlue = 2;
+const int kSt2086PrimaryWhite = 3;
+const int kGContainerVersion = 1;
+
+const string kConDir = Name(kContainerPrefix, kDirectory);
+const string kContainerItem = Name(kContainerPrefix, kItem);
+const string kItemLength = Name(kItemPrefix, kLength);
+const string kItemMime = Name(kItemPrefix, kMime);
+const string kItemSemantic = Name(kItemPrefix, kSemantic);
+
+bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
+ string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+
+ if (xmp_size < nameSpace.size()+2) {
+ // Data too short
+ return false;
+ }
+
+ if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
+ // Not correct namespace
+ return false;
+ }
+
+ // Position the pointers to the start of XMP XML portion
+ xmp_data += nameSpace.size()+1;
+ xmp_size -= nameSpace.size()+1;
+ XMPXmlHandler handler;
+
+ // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
+ while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
+ xmp_size--;
+ }
+
+ string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
+ MessageHandler msg_handler;
+ unique_ptr<XmlRule> rule(new XmlElementRule);
+ XmlReader reader(&handler, &msg_handler);
+ reader.StartParse(std::move(rule));
+ reader.Parse(str);
+ reader.FinishParse();
+ if (reader.HasErrors()) {
+ // Parse error
+ return false;
+ }
+
+ if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
+ return false;
+ }
+
+ if (!handler.getTransferFunction(&metadata->transferFunction)) {
+ return false;
+ }
+ return true;
+}
+
+string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+ const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kContainerPrefix, kContainerUri);
+ writer.WriteXmlns(kRecoveryMap, kRecoveryMapUri);
+ writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kGContainerVersion);
+ writer.StartWritingElements(kConDirSeq);
+ size_t item_depth = writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kVersion), metadata.version);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "RangeScalingFactor"), metadata.rangeScalingFactor);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "TransferFunction"), metadata.transferFunction);
+ if (metadata.transferFunction == JPEGR_TF_PQ) {
+ writer.StartWritingElement(Name(kRecoveryMap, kHdr10Metadata));
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "HDR10MaxFALL"), metadata.hdr10Metadata.maxFALL);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "HDR10MaxCLL"), metadata.hdr10Metadata.maxCLL);
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Metadata));
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "ST2086MaxLuminance"),
+ metadata.hdr10Metadata.st2086Metadata.maxLuminance);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "ST2086MinLuminance"),
+ metadata.hdr10Metadata.st2086Metadata.minLuminance);
+
+ // red
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryRed);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.redPrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.redPrimary.y);
+ writer.FinishWritingElement();
+
+ // green
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryGreen);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.greenPrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.greenPrimary.y);
+ writer.FinishWritingElement();
+
+ // blue
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryBlue);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.bluePrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.bluePrimary.y);
+ writer.FinishWritingElement();
+
+ // white
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryWhite);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.whitePoint.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.whitePoint.y);
+ writer.FinishWritingElement();
+ }
+ writer.FinishWritingElementsToDepth(item_depth);
+ writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
+} // namespace android::recoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp
index 41af991..b509478 100644
--- a/libs/jpegrecoverymap/tests/Android.bp
+++ b/libs/jpegrecoverymap/tests/Android.bp
@@ -26,8 +26,18 @@
test_suites: ["device-tests"],
srcs: [
"recoverymap_test.cpp",
+ "recoverymapmath_test.cpp",
+ ],
+ shared_libs: [
+ "libjpeg",
+ "liblog",
],
static_libs: [
+ "libimage_io",
+ "libgmock",
+ "libgtest",
+ "libjpegdecoder",
+ "libjpegencoder",
"libjpegrecoverymap",
],
}
diff --git a/libs/jpegrecoverymap/tests/data/jpeg_image.jpg b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg
new file mode 100644
index 0000000..e285742
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/jpeg_image.jpg
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_p010_image.p010 b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010
new file mode 100644
index 0000000..01673bf
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_p010_image.p010
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420 b/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
new file mode 100644
index 0000000..c043da6
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index c436138..c3c6fd4 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -15,8 +15,340 @@
*/
#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/recoverymaputils.h>
+#include <fcntl.h>
+#include <fstream>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
-namespace android {
+#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
+#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
+#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
+#define TEST_IMAGE_WIDTH 1280
+#define TEST_IMAGE_HEIGHT 720
+#define DEFAULT_JPEG_QUALITY 90
-// Add new tests here.
-} // namespace android
+#define SAVE_ENCODING_RESULT true
+#define SAVE_DECODING_RESULT true
+
+namespace android::recoverymap {
+
+class RecoveryMapTest : public testing::Test {
+public:
+ RecoveryMapTest();
+ ~RecoveryMapTest();
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ struct jpegr_uncompressed_struct mRawP010Image;
+ struct jpegr_uncompressed_struct mRawYuv420Image;
+ struct jpegr_compressed_struct mJpegImage;
+};
+
+RecoveryMapTest::RecoveryMapTest() {}
+RecoveryMapTest::~RecoveryMapTest() {}
+
+void RecoveryMapTest::SetUp() {}
+void RecoveryMapTest::TearDown() {
+ free(mRawP010Image.data);
+ free(mRawYuv420Image.data);
+ free(mJpegImage.data);
+}
+
+static size_t getFileSize(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ ALOGW("%s : fstat failed", __func__);
+ return 0;
+ }
+ return st.st_size; // bytes
+}
+
+static bool loadFile(const char filename[], void*& result, int* fileLength) {
+ int fd = open(filename, O_CLOEXEC);
+ if (fd < 0) {
+ return false;
+ }
+ int length = getFileSize(fd);
+ if (length == 0) {
+ close(fd);
+ return false;
+ }
+ if (fileLength != nullptr) {
+ *fileLength = length;
+ }
+ result = malloc(length);
+ if (read(fd, result, length) != static_cast<ssize_t>(length)) {
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+TEST_F(RecoveryMapTest, build) {
+ // Force all of the recovery map lib to be linked by calling all public functions.
+ RecoveryMap recovery_map;
+ recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+ nullptr, 0, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+ nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
+ recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
+}
+
+TEST_F(RecoveryMapTest, writeXmpThenRead) {
+ jpegr_metadata metadata_expected;
+ metadata_expected.transferFunction = JPEGR_TF_HLG;
+ metadata_expected.rangeScalingFactor = 1.25;
+ int length_expected = 1000;
+ std::string xmp = generateXmp(1000, metadata_expected);
+
+ jpegr_metadata metadata_read;
+ EXPECT_TRUE(getMetadataFromXMP(reinterpret_cast<uint8_t*>(xmp[0]), xmp.size(), &metadata_read));
+ ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
+ ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
+
+}
+
+/* Test Encode API-0 and decode */
+// TODO: enable when tonemapper is ready.
+//TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+// int ret;
+//
+// // Load input files.
+// if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+// FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+// }
+// mRawP010Image.width = TEST_IMAGE_WIDTH;
+// mRawP010Image.height = TEST_IMAGE_HEIGHT;
+// mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+//
+// RecoveryMap recoveryMap;
+//
+// jpegr_compressed_struct jpegR;
+// jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+// jpegR.data = malloc(jpegR.maxLength);
+// ret = recoveryMap.encodeJPEGR(
+// &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+// if (ret != OK) {
+// FAIL() << "Error code is " << ret;
+// }
+// if (SAVE_ENCODING_RESULT) {
+// // Output image data to file
+// std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+// if (!imageFile.is_open()) {
+// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+// }
+// imageFile.write((const char*)jpegR.data, jpegR.length);
+// }
+//
+// jpegr_uncompressed_struct decodedJpegR;
+// int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+// decodedJpegR.data = malloc(decodedJpegRSize);
+// ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+// if (ret != OK) {
+// FAIL() << "Error code is " << ret;
+// }
+// if (SAVE_DECODING_RESULT) {
+// // Output image data to file
+// std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+// if (!imageFile.is_open()) {
+// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+// }
+// imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+// }
+//
+// free(jpegR.data);
+// free(decodedJpegR.data);
+//}
+
+/* Test Encode API-1 and decode */
+TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawYuv420Image.width = TEST_IMAGE_WIDTH;
+ mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
+ mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
+ DEFAULT_JPEG_QUALITY, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+/* Test Encode API-2 and decode */
+TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawYuv420Image.width = TEST_IMAGE_WIDTH;
+ mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
+ mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
+ FAIL() << "Load file " << JPEG_IMAGE << " failed";
+ }
+ mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+/* Test Encode API-3 and decode */
+TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
+ FAIL() << "Load file " << JPEG_IMAGE << " failed";
+ }
+ mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
new file mode 100644
index 0000000..f8dd490
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -0,0 +1,881 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmath>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <jpegrecoverymap/recoverymapmath.h>
+
+namespace android::recoverymap {
+
+class RecoveryMapMathTest : public testing::Test {
+public:
+ RecoveryMapMathTest();
+ ~RecoveryMapMathTest();
+
+ float ComparisonEpsilon() { return 1e-4f; }
+ float LuminanceEpsilon() { return 1e-2f; }
+
+ Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
+ return {{{ static_cast<float>(y) / 255.0f,
+ (static_cast<float>(u) - 128.0f) / 255.0f,
+ (static_cast<float>(v) - 128.0f) / 255.0f }}};
+ }
+
+ Color P010(uint16_t y, uint16_t u, uint16_t v) {
+ return {{{ (static_cast<float>(y) - 64.0f) / 876.0f,
+ (static_cast<float>(u) - 64.0f) / 896.0f - 0.5f,
+ (static_cast<float>(v) - 64.0f) / 896.0f - 0.5f }}};
+ }
+
+ float Map(uint8_t e) {
+ return (static_cast<float>(e) - 127.5f) / 127.5f;
+ }
+
+ Color ColorMin(Color e1, Color e2) {
+ return {{{ fmin(e1.r, e2.r), fmin(e1.g, e2.g), fmin(e1.b, e2.b) }}};
+ }
+
+ Color ColorMax(Color e1, Color e2) {
+ return {{{ fmax(e1.r, e2.r), fmax(e1.g, e2.g), fmax(e1.b, e2.b) }}};
+ }
+
+ Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
+ Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; }
+
+ Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+ Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; }
+ Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; }
+
+ Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
+ Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
+
+ Color SrgbYuvRed() { return {{{ 0.299f, -0.1687f, 0.5f }}}; }
+ Color SrgbYuvGreen() { return {{{ 0.587f, -0.3313f, -0.4187f }}}; }
+ Color SrgbYuvBlue() { return {{{ 0.114f, 0.5f, -0.0813f }}}; }
+
+ Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
+ Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
+ Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; }
+
+ float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
+ Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
+ Color rgb = srgbInvOetf(rgb_gamma);
+ float luminance_scaled = luminanceFn(rgb);
+ return luminance_scaled * kSdrWhiteNits;
+ }
+
+ float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
+ ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
+ float scale_factor) {
+ Color rgb_gamma = bt2100YuvToRgb(yuv_gamma);
+ Color rgb = hdrInvOetf(rgb_gamma);
+ rgb = gamutConversionFn(rgb);
+ float luminance_scaled = luminanceFn(rgb);
+ return luminance_scaled * scale_factor;
+ }
+
+ Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
+ Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
+ Color rgb = srgbInvOetf(rgb_gamma);
+ return applyRecovery(rgb, recovery, range_scaling_factor);
+ }
+
+ jpegr_uncompressed_struct Yuv420Image() {
+ static uint8_t pixels[] = {
+ // Y
+ 0x00, 0x10, 0x20, 0x30,
+ 0x01, 0x11, 0x21, 0x31,
+ 0x02, 0x12, 0x22, 0x32,
+ 0x03, 0x13, 0x23, 0x33,
+ // U
+ 0xA0, 0xA1,
+ 0xA2, 0xA3,
+ // V
+ 0xB0, 0xB1,
+ 0xB2, 0xB3,
+ };
+ return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+ }
+
+ Color (*Yuv420Colors())[4] {
+ static Color colors[4][4] = {
+ {
+ Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0),
+ Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1),
+ }, {
+ Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0),
+ Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1),
+ }, {
+ Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2),
+ Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3),
+ }, {
+ Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2),
+ Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3),
+ },
+ };
+ return colors;
+ }
+
+ jpegr_uncompressed_struct P010Image() {
+ static uint16_t pixels[] = {
+ // Y
+ 0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6,
+ 0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6,
+ 0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6,
+ 0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6,
+ // UV
+ 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
+ 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
+ };
+ return { pixels, 4, 4, JPEGR_COLORGAMUT_BT709 };
+ }
+
+ Color (*P010Colors())[4] {
+ static Color colors[4][4] = {
+ {
+ P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0),
+ P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1),
+ }, {
+ P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0),
+ P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1),
+ }, {
+ P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2),
+ P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3),
+ }, {
+ P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2),
+ P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3),
+ },
+ };
+ return colors;
+ }
+
+ jpegr_uncompressed_struct MapImage() {
+ static uint8_t pixels[] = {
+ 0x00, 0x10, 0x20, 0x30,
+ 0x01, 0x11, 0x21, 0x31,
+ 0x02, 0x12, 0x22, 0x32,
+ 0x03, 0x13, 0x23, 0x33,
+ };
+ return { pixels, 4, 4, JPEGR_COLORGAMUT_UNSPECIFIED };
+ }
+
+ float (*MapValues())[4] {
+ static float values[4][4] = {
+ {
+ Map(0x00), Map(0x10), Map(0x20), Map(0x30),
+ }, {
+ Map(0x01), Map(0x11), Map(0x21), Map(0x31),
+ }, {
+ Map(0x02), Map(0x12), Map(0x22), Map(0x32),
+ }, {
+ Map(0x03), Map(0x13), Map(0x23), Map(0x33),
+ },
+ };
+ return values;
+ }
+
+protected:
+ virtual void SetUp();
+ virtual void TearDown();
+};
+
+RecoveryMapMathTest::RecoveryMapMathTest() {}
+RecoveryMapMathTest::~RecoveryMapMathTest() {}
+
+void RecoveryMapMathTest::SetUp() {}
+void RecoveryMapMathTest::TearDown() {}
+
+#define EXPECT_RGB_EQ(e1, e2) \
+ EXPECT_FLOAT_EQ((e1).r, (e2).r); \
+ EXPECT_FLOAT_EQ((e1).g, (e2).g); \
+ EXPECT_FLOAT_EQ((e1).b, (e2).b)
+
+#define EXPECT_RGB_NEAR(e1, e2) \
+ EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon())
+
+#define EXPECT_RGB_CLOSE(e1, e2) \
+ EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \
+ EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
+ EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
+
+#define EXPECT_YUV_EQ(e1, e2) \
+ EXPECT_FLOAT_EQ((e1).y, (e2).y); \
+ EXPECT_FLOAT_EQ((e1).u, (e2).u); \
+ EXPECT_FLOAT_EQ((e1).v, (e2).v)
+
+#define EXPECT_YUV_NEAR(e1, e2) \
+ EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \
+ EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon())
+
+#define EXPECT_YUV_BETWEEN(e, min, max) \
+ EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y), testing::Le((max).y))); \
+ EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u), testing::Le((max).u))); \
+ EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v), testing::Le((max).v)))
+
+// TODO: a bunch of these tests can be parameterized.
+
+TEST_F(RecoveryMapMathTest, ColorConstruct) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ EXPECT_FLOAT_EQ(e1.r, 0.1f);
+ EXPECT_FLOAT_EQ(e1.g, 0.2f);
+ EXPECT_FLOAT_EQ(e1.b, 0.3f);
+
+ EXPECT_FLOAT_EQ(e1.y, 0.1f);
+ EXPECT_FLOAT_EQ(e1.u, 0.2f);
+ EXPECT_FLOAT_EQ(e1.v, 0.3f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorAddColor) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 + e1;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
+
+ e2 += e1;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorAddFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 + 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f);
+
+ e2 += 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorSubtractColor) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 - e1;
+ EXPECT_FLOAT_EQ(e2.r, 0.0f);
+ EXPECT_FLOAT_EQ(e2.g, 0.0f);
+ EXPECT_FLOAT_EQ(e2.b, 0.0f);
+
+ e2 -= e1;
+ EXPECT_FLOAT_EQ(e2.r, -e1.r);
+ EXPECT_FLOAT_EQ(e2.g, -e1.g);
+ EXPECT_FLOAT_EQ(e2.b, -e1.b);
+}
+
+TEST_F(RecoveryMapMathTest, ColorSubtractFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 - 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f);
+
+ e2 -= 0.1f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorMultiplyFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 * 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
+
+ e2 *= 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorDivideFloat) {
+ Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
+
+ Color e2 = e1 / 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f);
+
+ e2 /= 2.0f;
+ EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f);
+ EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f);
+ EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, SrgbLuminance) {
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
+ EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
+}
+
+TEST_F(RecoveryMapMathTest, SrgbYuvToRgb) {
+ Color rgb_black = srgbYuvToRgb(YuvBlack());
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = srgbYuvToRgb(YuvWhite());
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = srgbYuvToRgb(SrgbYuvRed());
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = srgbYuvToRgb(SrgbYuvGreen());
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = srgbYuvToRgb(SrgbYuvBlue());
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbRgbToYuv) {
+ Color yuv_black = srgbRgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+ Color yuv_white = srgbRgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+ Color yuv_r = srgbRgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed());
+
+ Color yuv_g = srgbRgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen());
+
+ Color yuv_b = srgbRgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbRgbYuvRoundtrip) {
+ Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack()));
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite()));
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed()));
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen()));
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue()));
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, SrgbTransferFunction) {
+ EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f);
+ EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon());
+ EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon());
+ EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, P3Luminance) {
+ EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
+ EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100Luminance) {
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
+ EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100YuvToRgb) {
+ Color rgb_black = bt2100YuvToRgb(YuvBlack());
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = bt2100YuvToRgb(YuvWhite());
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed());
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen());
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue());
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100RgbToYuv) {
+ Color yuv_black = bt2100RgbToYuv(RgbBlack());
+ EXPECT_YUV_NEAR(yuv_black, YuvBlack());
+
+ Color yuv_white = bt2100RgbToYuv(RgbWhite());
+ EXPECT_YUV_NEAR(yuv_white, YuvWhite());
+
+ Color yuv_r = bt2100RgbToYuv(RgbRed());
+ EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed());
+
+ Color yuv_g = bt2100RgbToYuv(RgbGreen());
+ EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen());
+
+ Color yuv_b = bt2100RgbToYuv(RgbBlue());
+ EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue());
+}
+
+TEST_F(RecoveryMapMathTest, Bt2100RgbYuvRoundtrip) {
+ Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack()));
+ EXPECT_RGB_NEAR(rgb_black, RgbBlack());
+
+ Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite()));
+ EXPECT_RGB_NEAR(rgb_white, RgbWhite());
+
+ Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed()));
+ EXPECT_RGB_NEAR(rgb_r, RgbRed());
+
+ Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen()));
+ EXPECT_RGB_NEAR(rgb_g, RgbGreen());
+
+ Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue()));
+ EXPECT_RGB_NEAR(rgb_b, RgbBlue());
+}
+
+TEST_F(RecoveryMapMathTest, HlgOetf) {
+ EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
+ EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
+
+ Color e = {{{ 0.04167f, 0.08333f, 0.5f }}};
+ Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}};
+ EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
+}
+
+TEST_F(RecoveryMapMathTest, HlgInvOetf) {
+ EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f);
+ EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
+
+ Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}};
+ Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}};
+ EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
+}
+
+TEST_F(RecoveryMapMathTest, HlgTransferFunctionRoundtrip) {
+ EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f);
+ EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon());
+ EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, PqOetf) {
+ EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f);
+ EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon());
+ EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon());
+ EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
+
+ Color e = {{{ 0.01f, 0.5f, 0.99f }}};
+ Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}};
+ EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
+}
+
+TEST_F(RecoveryMapMathTest, PqInvOetf) {
+ EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f);
+ EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
+
+ Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}};
+ Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}};
+ EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
+}
+
+TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) {
+ EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
+ EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
+ EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
+ EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
+}
+
+TEST_F(RecoveryMapMathTest, ColorConversionLookup) {
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT709),
+ identityConversion);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_P3),
+ p3ToBt709);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT709, JPEGR_COLORGAMUT_BT2100),
+ bt2100ToBt709);
+
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT709),
+ bt709ToP3);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_P3),
+ identityConversion);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_P3, JPEGR_COLORGAMUT_BT2100),
+ bt2100ToP3);
+
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT709),
+ bt709ToBt2100);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_P3),
+ p3ToBt2100);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_BT2100, JPEGR_COLORGAMUT_BT2100),
+ identityConversion);
+
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_UNSPECIFIED),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT709),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_P3),
+ nullptr);
+ EXPECT_EQ(getHdrConversionFn(JPEGR_COLORGAMUT_UNSPECIFIED, JPEGR_COLORGAMUT_BT2100),
+ nullptr);
+}
+
+TEST_F(RecoveryMapMathTest, EncodeRecovery) {
+ EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127);
+ EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127);
+ EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0);
+ EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0);
+
+ EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127);
+ EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255);
+ EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255);
+ EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0);
+ EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0);
+ EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191);
+ EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63);
+
+ EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255);
+ EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0);
+ EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191);
+ EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63);
+
+ EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255);
+ EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0);
+ EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191);
+ EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63);
+}
+
+TEST_F(RecoveryMapMathTest, ApplyRecovery) {
+ EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack());
+ EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack());
+ EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack());
+
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite());
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f);
+
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite());
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f);
+
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite());
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f);
+ EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f);
+
+ Color e = {{{ 0.0f, 0.5f, 1.0f }}};
+
+ EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f);
+ EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e);
+ EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f);
+ EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f);
+}
+
+TEST_F(RecoveryMapMathTest, GetYuv420Pixel) {
+ jpegr_uncompressed_struct image = Yuv420Image();
+ Color (*colors)[4] = Yuv420Colors();
+
+ for (size_t y = 0; y < 4; ++y) {
+ for (size_t x = 0; x < 4; ++x) {
+ EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, GetP010Pixel) {
+ jpegr_uncompressed_struct image = P010Image();
+ Color (*colors)[4] = P010Colors();
+
+ for (size_t y = 0; y < 4; ++y) {
+ for (size_t x = 0; x < 4; ++x) {
+ EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, SampleYuv420) {
+ jpegr_uncompressed_struct image = Yuv420Image();
+ Color (*colors)[4] = Yuv420Colors();
+
+ static const size_t kMapScaleFactor = 2;
+ for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
+ for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
+ Color min = {{{ 1.0f, 1.0f, 1.0f }}};
+ Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+
+ for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
+ for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
+ Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
+ min = ColorMin(min, e);
+ max = ColorMax(max, e);
+ }
+ }
+
+ // Instead of reimplementing the sampling algorithm, confirm that the
+ // sample output is within the range of the min and max of the nearest
+ // points.
+ EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, SampleP010) {
+ jpegr_uncompressed_struct image = P010Image();
+ Color (*colors)[4] = P010Colors();
+
+ static const size_t kMapScaleFactor = 2;
+ for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
+ for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
+ Color min = {{{ 1.0f, 1.0f, 1.0f }}};
+ Color max = {{{ -1.0f, -1.0f, -1.0f }}};
+
+ for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
+ for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
+ Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
+ min = ColorMin(min, e);
+ max = ColorMax(max, e);
+ }
+ }
+
+ // Instead of reimplementing the sampling algorithm, confirm that the
+ // sample output is within the range of the min and max of the nearest
+ // points.
+ EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, SampleMap) {
+ jpegr_uncompressed_struct image = MapImage();
+ float (*values)[4] = MapValues();
+
+ static const size_t kMapScaleFactor = 2;
+ for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
+ for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
+ size_t x_base = x / kMapScaleFactor;
+ size_t y_base = y / kMapScaleFactor;
+
+ float min = 1.0f;
+ float max = -1.0f;
+
+ min = fmin(min, values[y_base][x_base]);
+ max = fmax(max, values[y_base][x_base]);
+ if (y_base + 1 < 4) {
+ min = fmin(min, values[y_base + 1][x_base]);
+ max = fmax(max, values[y_base + 1][x_base]);
+ }
+ if (x_base + 1 < 4) {
+ min = fmin(min, values[y_base][x_base + 1]);
+ max = fmax(max, values[y_base][x_base + 1]);
+ }
+ if (y_base + 1 < 4 && x_base + 1 < 4) {
+ min = fmin(min, values[y_base + 1][x_base + 1]);
+ max = fmax(max, values[y_base + 1][x_base + 1]);
+ }
+
+ // Instead of reimplementing the sampling algorithm, confirm that the
+ // sample output is within the range of the min and max of the nearest
+ // points.
+ EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
+ testing::AllOf(testing::Ge(min), testing::Le(max)));
+ }
+ }
+}
+
+TEST_F(RecoveryMapMathTest, ColorToRgba1010102) {
+ EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
+ EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
+ EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
+ EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
+ EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
+
+ Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
+ EXPECT_EQ(colorToRgba1010102(e_gamma),
+ 0x3 << 30
+ | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff))
+ | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10
+ | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgb) {
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
+ 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance),
+ kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
+ srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
+ srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
+ srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbP3) {
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance),
+ 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance),
+ kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance),
+ p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
+ p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
+ p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceSrgbBt2100) {
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance),
+ 0.0f);
+ EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance),
+ kSdrWhiteNits);
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
+ bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
+ bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
+ EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
+ bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminanceHlg) {
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ 0.0f);
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ kHlgMaxNits);
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion,
+ bt2100Luminance, kHlgMaxNits),
+ bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) {
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ 0.0f);
+ EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ kPqMaxNits);
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
+ EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion,
+ bt2100Luminance, kPqMaxNits),
+ bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
+}
+
+TEST_F(RecoveryMapMathTest, ApplyMap) {
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f),
+ RgbWhite() * 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f),
+ RgbRed() * 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f),
+ RgbGreen() * 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f),
+ RgbBlue() * 8.0f);
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f),
+ RgbWhite() * sqrt(8.0f));
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f),
+ RgbRed() * sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f),
+ RgbGreen() * sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f),
+ RgbBlue() * sqrt(8.0f));
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f),
+ RgbWhite());
+ EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f),
+ RgbRed());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f),
+ RgbGreen());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f),
+ RgbBlue());
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f),
+ RgbWhite() / sqrt(8.0f));
+ EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f),
+ RgbRed() / sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f),
+ RgbGreen() / sqrt(8.0f));
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f),
+ RgbBlue() / sqrt(8.0f));
+
+ EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f),
+ RgbWhite() / 8.0f);
+ EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f),
+ RgbBlack());
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f),
+ RgbRed() / 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f),
+ RgbGreen() / 8.0f);
+ EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f),
+ RgbBlue() / 8.0f);
+}
+
+} // namespace android::recoverymap
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 539cbaa..66a40f1 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -14,12 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "Choreographer"
-//#define LOG_NDEBUG 0
-
#include <android-base/thread_annotations.h>
#include <android/gui/ISurfaceComposer.h>
-#include <gui/DisplayEventDispatcher.h>
+#include <gui/Choreographer.h>
#include <jni.h>
#include <private/android/choreographer.h>
#include <utils/Looper.h>
@@ -31,444 +28,9 @@
#include <queue>
#include <thread>
-namespace {
-struct {
- // Global JVM that is provided by zygote
- JavaVM* jvm = nullptr;
- struct {
- jclass clazz;
- jmethodID getInstance;
- jmethodID registerNativeChoreographerForRefreshRateCallbacks;
- jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
- } displayManagerGlobal;
-} gJni;
+#undef LOG_TAG
+#define LOG_TAG "AChoreographer"
-// Gets the JNIEnv* for this thread, and performs one-off initialization if we
-// have never retrieved a JNIEnv* pointer before.
-JNIEnv* getJniEnv() {
- if (gJni.jvm == nullptr) {
- ALOGW("AChoreographer: No JVM provided!");
- return nullptr;
- }
-
- JNIEnv* env = nullptr;
- if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGD("Attaching thread to JVM for AChoreographer");
- JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
- jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
- if (attachResult != JNI_OK) {
- ALOGE("Unable to attach thread. Error: %d", attachResult);
- return nullptr;
- }
- }
- if (env == nullptr) {
- ALOGW("AChoreographer: No JNI env available!");
- }
- return env;
-}
-
-inline const char* toString(bool value) {
- return value ? "true" : "false";
-}
-} // namespace
-
-namespace android {
-using gui::VsyncEventData;
-
-struct FrameCallback {
- AChoreographer_frameCallback callback;
- AChoreographer_frameCallback64 callback64;
- AChoreographer_vsyncCallback vsyncCallback;
- void* data;
- nsecs_t dueTime;
-
- inline bool operator<(const FrameCallback& rhs) const {
- // Note that this is intentionally flipped because we want callbacks due sooner to be at
- // the head of the queue
- return dueTime > rhs.dueTime;
- }
-};
-
-struct RefreshRateCallback {
- AChoreographer_refreshRateCallback callback;
- void* data;
- bool firstCallbackFired = false;
-};
-
-class Choreographer;
-
-/**
- * Implementation of AChoreographerFrameCallbackData.
- */
-struct ChoreographerFrameCallbackDataImpl {
- int64_t frameTimeNanos{0};
-
- VsyncEventData vsyncEventData;
-
- const Choreographer* choreographer;
-};
-
-struct {
- std::mutex lock;
- std::vector<Choreographer*> ptrs GUARDED_BY(lock);
- std::map<AVsyncId, int64_t> startTimes GUARDED_BY(lock);
- bool registeredToDisplayManager GUARDED_BY(lock) = false;
-
- std::atomic<nsecs_t> mLastKnownVsync = -1;
-} gChoreographers;
-
-class Choreographer : public DisplayEventDispatcher, public MessageHandler {
-public:
- explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
- void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64,
- AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay);
- void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
- EXCLUDES(gChoreographers.lock);
- void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
- // Drains the queue of pending vsync periods and dispatches refresh rate
- // updates to callbacks.
- // The assumption is that this method is only called on a single
- // processing thread, either by looper or by AChoreographer_handleEvents
- void handleRefreshRateUpdates();
- void scheduleLatestConfigRequest();
-
- enum {
- MSG_SCHEDULE_CALLBACKS = 0,
- MSG_SCHEDULE_VSYNC = 1,
- MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
- };
- virtual void handleMessage(const Message& message) override;
-
- static Choreographer* getForThread();
- virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
- int64_t getFrameInterval() const;
- bool inCallback() const;
-
-private:
- Choreographer(const Choreographer&) = delete;
-
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
- VsyncEventData vsyncEventData) override;
- void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
- nsecs_t vsyncPeriod) override;
- void dispatchNullEvent(nsecs_t, PhysicalDisplayId) override;
- void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
- std::vector<FrameRateOverride> overrides) override;
-
- void scheduleCallbacks();
-
- ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
- void registerStartTime() const;
-
- std::mutex mLock;
- // Protected by mLock
- std::priority_queue<FrameCallback> mFrameCallbacks;
- std::vector<RefreshRateCallback> mRefreshRateCallbacks;
-
- nsecs_t mLatestVsyncPeriod = -1;
- VsyncEventData mLastVsyncEventData;
- bool mInCallback = false;
-
- const sp<Looper> mLooper;
- const std::thread::id mThreadId;
-
- // Approximation of num_threads_using_choreographer * num_frames_of_history with leeway.
- static constexpr size_t kMaxStartTimes = 250;
-};
-
-static thread_local Choreographer* gChoreographer;
-Choreographer* Choreographer::getForThread() {
- if (gChoreographer == nullptr) {
- sp<Looper> looper = Looper::getForThread();
- if (!looper.get()) {
- ALOGW("No looper prepared for thread");
- return nullptr;
- }
- gChoreographer = new Choreographer(looper);
- status_t result = gChoreographer->initialize();
- if (result != OK) {
- ALOGW("Failed to initialize");
- return nullptr;
- }
- }
- return gChoreographer;
-}
-
-Choreographer::Choreographer(const sp<Looper>& looper)
- : DisplayEventDispatcher(looper, gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp),
- mLooper(looper),
- mThreadId(std::this_thread::get_id()) {
- std::lock_guard<std::mutex> _l(gChoreographers.lock);
- gChoreographers.ptrs.push_back(this);
-}
-
-Choreographer::~Choreographer() {
- std::lock_guard<std::mutex> _l(gChoreographers.lock);
- gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
- gChoreographers.ptrs.end(),
- [=](Choreographer* c) { return c == this; }),
- gChoreographers.ptrs.end());
- // Only poke DisplayManagerGlobal to unregister if we previously registered
- // callbacks.
- if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
- gChoreographers.registeredToDisplayManager = false;
- JNIEnv* env = getJniEnv();
- if (env == nullptr) {
- ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
- return;
- }
- jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
- gJni.displayManagerGlobal.getInstance);
- if (dmg == nullptr) {
- ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
- } else {
- env->CallVoidMethod(dmg,
- gJni.displayManagerGlobal
- .unregisterNativeChoreographerForRefreshRateCallbacks);
- env->DeleteLocalRef(dmg);
- }
- }
-}
-
-void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64,
- AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
- {
- std::lock_guard<std::mutex> _l{mLock};
- mFrameCallbacks.push(callback);
- }
- if (callback.dueTime <= now) {
- if (std::this_thread::get_id() != mThreadId) {
- if (mLooper != nullptr) {
- Message m{MSG_SCHEDULE_VSYNC};
- mLooper->sendMessage(this, m);
- } else {
- scheduleVsync();
- }
- } else {
- scheduleVsync();
- }
- } else {
- if (mLooper != nullptr) {
- Message m{MSG_SCHEDULE_CALLBACKS};
- mLooper->sendMessageDelayed(delay, this, m);
- } else {
- scheduleCallbacks();
- }
- }
-}
-
-void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
- std::lock_guard<std::mutex> _l{mLock};
- for (const auto& callback : mRefreshRateCallbacks) {
- // Don't re-add callbacks.
- if (cb == callback.callback && data == callback.data) {
- return;
- }
- }
- mRefreshRateCallbacks.emplace_back(
- RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
- bool needsRegistration = false;
- {
- std::lock_guard<std::mutex> _l2(gChoreographers.lock);
- needsRegistration = !gChoreographers.registeredToDisplayManager;
- }
- if (needsRegistration) {
- JNIEnv* env = getJniEnv();
- if (env == nullptr) {
- ALOGW("JNI environment is unavailable, skipping registration");
- return;
- }
- jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
- gJni.displayManagerGlobal.getInstance);
- if (dmg == nullptr) {
- ALOGW("DMS is not initialized yet: skipping registration");
- return;
- } else {
- env->CallVoidMethod(dmg,
- gJni.displayManagerGlobal
- .registerNativeChoreographerForRefreshRateCallbacks,
- reinterpret_cast<int64_t>(this));
- env->DeleteLocalRef(dmg);
- {
- std::lock_guard<std::mutex> _l2(gChoreographers.lock);
- gChoreographers.registeredToDisplayManager = true;
- }
- }
- } else {
- scheduleLatestConfigRequest();
- }
-}
-
-void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
- void* data) {
- std::lock_guard<std::mutex> _l{mLock};
- mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
- mRefreshRateCallbacks.end(),
- [&](const RefreshRateCallback& callback) {
- return cb == callback.callback &&
- data == callback.data;
- }),
- mRefreshRateCallbacks.end());
-}
-
-void Choreographer::scheduleLatestConfigRequest() {
- if (mLooper != nullptr) {
- Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
- mLooper->sendMessage(this, m);
- } else {
- // If the looper thread is detached from Choreographer, then refresh rate
- // changes will be handled in AChoreographer_handlePendingEvents, so we
- // need to wake up the looper thread by writing to the write-end of the
- // socket the looper is listening on.
- // Fortunately, these events are small so sending packets across the
- // socket should be atomic across processes.
- DisplayEventReceiver::Event event;
- event.header =
- DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
- PhysicalDisplayId::fromPort(0), systemTime()};
- injectEvent(event);
- }
-}
-
-void Choreographer::scheduleCallbacks() {
- const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t dueTime;
- {
- std::lock_guard<std::mutex> _l{mLock};
- // If there are no pending callbacks then don't schedule a vsync
- if (mFrameCallbacks.empty()) {
- return;
- }
- dueTime = mFrameCallbacks.top().dueTime;
- }
-
- if (dueTime <= now) {
- ALOGV("choreographer %p ~ scheduling vsync", this);
- scheduleVsync();
- return;
- }
-}
-
-void Choreographer::handleRefreshRateUpdates() {
- std::vector<RefreshRateCallback> callbacks{};
- const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
- const nsecs_t lastPeriod = mLatestVsyncPeriod;
- if (pendingPeriod > 0) {
- mLatestVsyncPeriod = pendingPeriod;
- }
- {
- std::lock_guard<std::mutex> _l{mLock};
- for (auto& cb : mRefreshRateCallbacks) {
- callbacks.push_back(cb);
- cb.firstCallbackFired = true;
- }
- }
-
- for (auto& cb : callbacks) {
- if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
- cb.callback(pendingPeriod, cb.data);
- }
- }
-}
-
-// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
-// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
-// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
- VsyncEventData vsyncEventData) {
- std::vector<FrameCallback> callbacks{};
- {
- std::lock_guard<std::mutex> _l{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
- callbacks.push_back(mFrameCallbacks.top());
- mFrameCallbacks.pop();
- }
- }
- mLastVsyncEventData = vsyncEventData;
- for (const auto& cb : callbacks) {
- if (cb.vsyncCallback != nullptr) {
- const ChoreographerFrameCallbackDataImpl frameCallbackData =
- createFrameCallbackData(timestamp);
- registerStartTime();
- mInCallback = true;
- cb.vsyncCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
- &frameCallbackData),
- cb.data);
- mInCallback = false;
- } else if (cb.callback64 != nullptr) {
- cb.callback64(timestamp, cb.data);
- } else if (cb.callback != nullptr) {
- cb.callback(timestamp, cb.data);
- }
- }
-}
-
-void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
- to_string(displayId).c_str(), toString(connected));
-}
-
-void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
- LOG_ALWAYS_FATAL("dispatchModeChanged was called but was never registered");
-}
-
-void Choreographer::dispatchFrameRateOverrides(nsecs_t, PhysicalDisplayId,
- std::vector<FrameRateOverride>) {
- LOG_ALWAYS_FATAL("dispatchFrameRateOverrides was called but was never registered");
-}
-
-void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) {
- ALOGV("choreographer %p ~ received null event.", this);
- handleRefreshRateUpdates();
-}
-
-void Choreographer::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- case MSG_HANDLE_REFRESH_RATE_UPDATES:
- handleRefreshRateUpdates();
- break;
- }
-}
-
-int64_t Choreographer::getFrameInterval() const {
- return mLastVsyncEventData.frameInterval;
-}
-
-bool Choreographer::inCallback() const {
- return mInCallback;
-}
-
-ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
- return {.frameTimeNanos = timestamp,
- .vsyncEventData = mLastVsyncEventData,
- .choreographer = this};
-}
-
-void Choreographer::registerStartTime() const {
- std::scoped_lock _l(gChoreographers.lock);
- for (VsyncEventData::FrameTimeline frameTimeline : mLastVsyncEventData.frameTimelines) {
- while (gChoreographers.startTimes.size() >= kMaxStartTimes) {
- gChoreographers.startTimes.erase(gChoreographers.startTimes.begin());
- }
- gChoreographers.startTimes[frameTimeline.vsyncId] = systemTime(SYSTEM_TIME_MONOTONIC);
- }
-}
-
-} // namespace android
using namespace android;
static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
@@ -488,27 +50,12 @@
// Glue for private C api
namespace android {
-void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
- std::lock_guard<std::mutex> _l(gChoreographers.lock);
- gChoreographers.mLastKnownVsync.store(vsyncPeriod);
- for (auto c : gChoreographers.ptrs) {
- c->scheduleLatestConfigRequest();
- }
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) {
+ Choreographer::signalRefreshRateCallbacks(vsyncPeriod);
}
void AChoreographer_initJVM(JNIEnv* env) {
- env->GetJavaVM(&gJni.jvm);
- // Now we need to find the java classes.
- jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
- gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
- gJni.displayManagerGlobal.getInstance =
- env->GetStaticMethodID(dmgClass, "getInstance",
- "()Landroid/hardware/display/DisplayManagerGlobal;");
- gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
- env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
- gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
- env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
- "()V");
+ Choreographer::initJVM(env);
}
AChoreographer* AChoreographer_routeGetInstance() {
@@ -583,13 +130,7 @@
}
int64_t AChoreographer_getStartTimeNanosForVsyncId(AVsyncId vsyncId) {
- std::scoped_lock _l(gChoreographers.lock);
- const auto iter = gChoreographers.startTimes.find(vsyncId);
- if (iter == gChoreographers.startTimes.end()) {
- ALOGW("Start time was not found for vsync id: %" PRId64, vsyncId);
- return 0;
- }
- return iter->second;
+ return Choreographer::getStartTimeNanosForVsyncId(vsyncId);
}
} // namespace android
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 60328e4..bf0805b 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -117,15 +117,6 @@
#define CHECK_NOT_NULL(name) \
LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
-namespace {
-
-sp<IBinder> getToken(ADisplay* display) {
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
- return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
-}
-
-} // namespace
-
namespace android {
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
@@ -139,10 +130,9 @@
ui::DisplayConnectionType displayConnectionTypes[size];
int numModes = 0;
for (int i = 0; i < size; ++i) {
- const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
-
ui::StaticDisplayInfo staticInfo;
- if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo);
+ if (const status_t status =
+ SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
status != OK) {
return status;
}
@@ -150,7 +140,7 @@
ui::DynamicDisplayInfo dynamicInfo;
if (const status_t status =
- SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(ids[i].value, &dynamicInfo);
status != OK) {
return status;
}
@@ -260,14 +250,15 @@
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
CHECK_NOT_NULL(display);
- sp<IBinder> token = getToken(display);
ui::DynamicDisplayInfo info;
- if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ if (const auto status =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(impl->id.value, &info);
status != OK) {
return status;
}
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
for (size_t i = 0; i < impl->numConfigs; i++) {
auto* config = impl->configs + i;
if (config->id == info.activeDisplayModeId) {
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 180dce9..cf927db 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -617,15 +617,27 @@
static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
AHARDWAREBUFFER_FORMAT_R8_UNORM,
"HAL and AHardwareBuffer pixel format don't match");
+ static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_16_UINT) ==
+ AHARDWAREBUFFER_FORMAT_R16_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RG_1616_UINT) ==
+ AHARDWAREBUFFER_FORMAT_R16G16_UINT,
+ "HAL and AHardwareBuffer pixel format don't match");
+ static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::RGBA_10101010) ==
+ AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
switch (format) {
case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R16_UINT:
+ case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
case AHARDWAREBUFFER_FORMAT_BLOB:
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
@@ -677,6 +689,7 @@
return 1;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+ case AHARDWAREBUFFER_FORMAT_R16_UINT:
return 2;
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
@@ -686,8 +699,10 @@
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
+ case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
return 4;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
return 8;
default:
return 0;
@@ -702,6 +717,14 @@
return ahardwarebuffer_format;
}
+int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) {
+ GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ auto& mapper = GraphicBufferMapper::get();
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+ mapper.getDataspace(gb->handle, &dataspace);
+ return static_cast<int32_t>(dataspace);
+}
+
uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
using android::hardware::graphics::common::V1_1::BufferUsage;
static_assert(AHARDWAREBUFFER_USAGE_CPU_READ_NEVER == (uint64_t)BufferUsage::CPU_READ_NEVER,
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index c345385..b7b2926 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -232,15 +232,6 @@
return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, 0,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
- ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
-}
-
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index ddfd1d1..6d3d295 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -52,6 +52,11 @@
// convert HAL format to AHardwareBuffer format (note: this is a no-op)
uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
+// retrieves a dataspace from the AHardwareBuffer metadata, if the device
+// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not
+// supported.
+int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer);
+
// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index c35507b..b2e8bea 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -173,6 +173,27 @@
* OpenGL ES: GR_GL_R8
*/
AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R16_UINT
+ * OpenGL ES: GR_GL_R16UI
+ */
+ AHARDWAREBUFFER_FORMAT_R16_UINT = 0x39,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R16G16_UINT
+ * OpenGL ES: GR_GL_RG16UI
+ */
+ AHARDWAREBUFFER_FORMAT_R16G16_UINT = 0x3a,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16
+ * OpenGL ES: N/A
+ */
+ AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM = 0x3b,
};
/**
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index a27e3dd..be6623e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -372,8 +372,12 @@
*
* \return 0 for success, -EINVAL if the window value is invalid.
*/
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
- __INTRODUCED_IN(__ANDROID_API_U__);
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
+ __INTRODUCED_IN(__ANDROID_API_U__) {
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
#ifdef __cplusplus
}
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 76d23fa..c2fd6ef 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -51,7 +51,6 @@
ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
- ANativeWindow_clearFrameRate; # introduced=UpsideDownCake
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
@@ -71,6 +70,7 @@
android::AHardwareBuffer_convertToPixelFormat*;
android::AHardwareBuffer_convertFromGrallocUsageBits*;
android::AHardwareBuffer_convertToGrallocUsageBits*;
+ android::AHardwareBuffer_getDataSpace*;
android::AHardwareBuffer_to_GraphicBuffer*;
android::AHardwareBuffer_to_ANativeWindowBuffer*;
android::AHardwareBuffer_from_GraphicBuffer*;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 04e24ed..8d19c45 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -111,9 +111,23 @@
],
}
+// Used to consolidate and simplify pulling Skia & Skia deps into targets that depend on
+// librenderengine. This allows shared deps to be deduplicated (e.g. Perfetto), which doesn't seem
+// possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs.
+cc_defaults {
+ name: "librenderengine_deps",
+ defaults: ["skia_renderengine_deps"],
+ static_libs: ["libskia_renderengine"],
+}
+
+// Note: if compilation fails when adding librenderengine as a dependency, try adding
+// librenderengine_deps to the defaults field of your dependent target.
cc_library_static {
name: "librenderengine",
- defaults: ["librenderengine_defaults"],
+ defaults: [
+ "librenderengine_defaults",
+ "librenderengine_deps",
+ ],
double_loadable: true,
cflags: [
"-fvisibility=hidden",
@@ -132,7 +146,6 @@
include_dirs: [
"external/skia/src/gpu",
],
- whole_static_libs: ["libskia_renderengine"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index d08c221..341c011 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -39,8 +39,12 @@
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
case RenderEngineType::SKIA_VK:
+#ifdef RE_SKIAVK
ALOGD("RenderEngine with SkiaVK Backend");
return renderengine::skia::SkiaVkRenderEngine::create(args);
+#else
+ LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!");
+#endif
case RenderEngineType::SKIA_GL_THREADED: {
ALOGD("Threaded RenderEngine with SkiaGL Backend");
return renderengine::threaded::RenderEngineThreaded::create(
@@ -50,12 +54,16 @@
args.renderEngineType);
}
case RenderEngineType::SKIA_VK_THREADED:
+#ifdef RE_SKIAVK
ALOGD("Threaded RenderEngine with SkiaVK Backend");
return renderengine::threaded::RenderEngineThreaded::create(
[args]() {
return android::renderengine::skia::SkiaVkRenderEngine::create(args);
},
args.renderEngineType);
+#else
+ LOG_ALWAYS_FATAL("Requested VK backend, but RE_SKIAVK is not defined!");
+#endif
case RenderEngineType::GLES:
default:
ALOGD("RenderEngine with GLES Backend");
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index afbe6cf..55c34cd 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -25,7 +25,7 @@
name: "librenderengine_bench",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "skia_deps",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
srcs: [
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index f9424f0..2b8495c 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+// Allow the SkiaVkRenderEngine class to not be compiled, to save space
+// NOTE: In order to build this class, define `RE_SKIAVK` in a build file.
+#ifdef RE_SKIAVK
+
// #define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "RenderEngine"
@@ -673,3 +677,4 @@
} // namespace skia
} // namespace renderengine
} // namespace android
+#endif // RE_SKIAVK
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 2e0cf45..1e42b80 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -17,6 +17,10 @@
#ifndef SF_SKIAVKRENDERENGINE_H_
#define SF_SKIAVKRENDERENGINE_H_
+// Allow the SkiaVkRenderEngine class to not be compiled, to save space
+// NOTE: In order to build this class, define `RE_SKIAVK` in a build file.
+#ifdef RE_SKIAVK
+
#include <vk/GrVkBackendContext.h>
#include "SkiaRenderEngine.h"
@@ -55,4 +59,5 @@
} // namespace renderengine
} // namespace android
-#endif
+#endif // RE_SKIAVK
+#endif // SF_SKIAVKRENDERENGINE_H_
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 6f328d7..50e166d 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -25,7 +25,7 @@
name: "librenderengine_test",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "skia_deps",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
test_suites: ["device-tests"],
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index f3f2da8..7db95a7 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -112,6 +112,7 @@
virtual bool useColorManagement() const = 0;
};
+#ifdef RE_SKIAVK
class SkiaVkRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaVkRenderEngineFactory"; }
@@ -152,6 +153,8 @@
public:
bool useColorManagement() const override { return true; }
};
+#endif // RE_SKIAVK
+
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -1557,11 +1560,17 @@
expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
}
+#ifdef RE_SKIAVK
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
std::make_shared<SkiaGLESCMRenderEngineFactory>(),
std::make_shared<SkiaVkRenderEngineFactory>(),
std::make_shared<SkiaVkCMRenderEngineFactory>()));
+#else // RE_SKIAVK
+INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
+ testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
+ std::make_shared<SkiaGLESCMRenderEngineFactory>()));
+#endif // RE_SKIAVK
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
if (!GetParam()->typeSupported()) {
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 2b93c6e..b6b9cc4 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -21,9 +21,10 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-cc_library_shared {
+cc_library {
name: "libsensor",
+ host_supported: true,
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 78f692b..2278d39 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -42,6 +42,7 @@
GET_DYNAMIC_SENSOR_LIST,
CREATE_SENSOR_DIRECT_CONNECTION,
SET_OPERATION_PARAMETER,
+ GET_RUNTIME_SENSOR_LIST,
};
class BpSensorServer : public BpInterface<ISensorServer>
@@ -90,6 +91,25 @@
return v;
}
+ virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ data.writeString16(opPackageName);
+ data.writeInt32(deviceId);
+ remote()->transact(GET_RUNTIME_SENSOR_LIST, data, &reply);
+ Sensor s;
+ Vector<Sensor> v;
+ uint32_t n = reply.readUint32();
+ v.setCapacity(n);
+ while (n) {
+ n--;
+ reply.read(s);
+ v.add(s);
+ }
+ return v;
+ }
+
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode, const String16& opPackageName, const String16& attributionTag)
{
@@ -194,6 +214,18 @@
}
return NO_ERROR;
}
+ case GET_RUNTIME_SENSOR_LIST: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ const String16& opPackageName = data.readString16();
+ const int deviceId = data.readInt32();
+ Vector<Sensor> v(getRuntimeSensorList(opPackageName, deviceId));
+ size_t n = v.size();
+ reply->writeUint32(static_cast<uint32_t>(n));
+ for (size_t i = 0; i < n; i++) {
+ reply->write(v[i]);
+ }
+ return NO_ERROR;
+ }
case CREATE_SENSOR_DIRECT_CONNECTION: {
CHECK_INTERFACE(ISensorServer, data, reply);
const String16& opPackageName = data.readString16();
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index ec0ced8..fb895f5 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -264,10 +264,6 @@
mStringType = SENSOR_STRING_TYPE_HEART_BEAT;
mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE;
break;
-
- // TODO: Placeholder for LLOB sensor type
-
-
case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED:
mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 0ba9704..2748276 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -201,6 +201,19 @@
return static_cast<ssize_t>(count);
}
+ssize_t SensorManager::getRuntimeSensorList(int deviceId, Vector<Sensor>& runtimeSensors) {
+ Mutex::Autolock _l(mLock);
+ status_t err = assertStateLocked();
+ if (err < 0) {
+ return static_cast<ssize_t>(err);
+ }
+
+ runtimeSensors = mSensorServer->getRuntimeSensorList(mOpPackageName, deviceId);
+ size_t count = runtimeSensors.size();
+
+ return static_cast<ssize_t>(count);
+}
+
ssize_t SensorManager::getDynamicSensorList(Sensor const* const** list) {
Mutex::Autolock _l(mLock);
status_t err = assertStateLocked();
diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h
index ce5c672..3295196 100644
--- a/libs/sensor/include/sensor/ISensorServer.h
+++ b/libs/sensor/include/sensor/ISensorServer.h
@@ -43,6 +43,7 @@
virtual Vector<Sensor> getSensorList(const String16& opPackageName) = 0;
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0;
+ virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId) = 0;
virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName,
int mode, const String16& opPackageName, const String16& attributionTag) = 0;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 8d0a8a4..0798da2 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -59,6 +59,7 @@
ssize_t getSensorList(Sensor const* const** list);
ssize_t getDynamicSensorList(Vector<Sensor>& list);
ssize_t getDynamicSensorList(Sensor const* const** list);
+ ssize_t getRuntimeSensorList(int deviceId, Vector<Sensor>& list);
Sensor const* getDefaultSensor(int type);
sp<SensorEventQueue> createEventQueue(
String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16(""));
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 2a4a370..42b0cc1 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -68,6 +68,9 @@
// fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
// dataspace for correct conversion to linear colors.
ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+
+ enum SkSLType { Shader, ColorFilter };
+ SkSLType type = Shader;
};
static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
@@ -96,6 +99,10 @@
// 2. Apply color transform matrices in linear space
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+// Generates a shader string that applies color transforms in linear space.
+// This is intended to be plugged into an SkColorFilter
+std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect);
+
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index f80e93f..a3c403e 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -386,12 +386,23 @@
}
}
-void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
- shader.append(R"(
- uniform shader child;
- half4 main(float2 xy) {
- float4 c = float4(child.eval(xy));
- )");
+void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type,
+ std::string& shader) {
+ switch (type) {
+ case LinearEffect::SkSLType::ColorFilter:
+ shader.append(R"(
+ half4 main(half4 inputColor) {
+ float4 c = float4(inputColor);
+ )");
+ break;
+ case LinearEffect::SkSLType::Shader:
+ shader.append(R"(
+ uniform shader child;
+ half4 main(float2 xy) {
+ float4 c = float4(child.eval(xy));
+ )");
+ break;
+ }
if (undoPremultipliedAlpha) {
shader.append(R"(
c.rgb = c.rgb / (c.a + 0.0019);
@@ -459,7 +470,7 @@
generateXYZTransforms(shaderString);
generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, shaderString);
return shaderString;
}
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
index 78e82da..c9663ed 100644
--- a/libs/ui/PublicFormat.cpp
+++ b/libs/ui/PublicFormat.cpp
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#include <ui/GraphicTypes.h> // ui::Dataspace
+#include "aidl/android/hardware/graphics/common/Dataspace.h"
#include <ui/PublicFormat.h>
+
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
-using ui::Dataspace;
+using ::aidl::android::hardware::graphics::common::Dataspace;
int mapPublicFormatToHalFormat(PublicFormat f) {
switch (f) {
@@ -29,6 +30,7 @@
case PublicFormat::DEPTH_POINT_CLOUD:
case PublicFormat::DEPTH_JPEG:
case PublicFormat::HEIC:
+ case PublicFormat::JPEG_R:
return HAL_PIXEL_FORMAT_BLOB;
case PublicFormat::DEPTH16:
return HAL_PIXEL_FORMAT_Y16;
@@ -47,7 +49,7 @@
Dataspace dataspace;
switch (f) {
case PublicFormat::JPEG:
- dataspace = Dataspace::V0_JFIF;
+ dataspace = Dataspace::JFIF;
break;
case PublicFormat::DEPTH_POINT_CLOUD:
case PublicFormat::DEPTH16:
@@ -64,7 +66,7 @@
case PublicFormat::YUV_420_888:
case PublicFormat::NV21:
case PublicFormat::YV12:
- dataspace = Dataspace::V0_JFIF;
+ dataspace = Dataspace::JFIF;
break;
case PublicFormat::DEPTH_JPEG:
dataspace = Dataspace::DYNAMIC_DEPTH;
@@ -72,6 +74,9 @@
case PublicFormat::HEIC:
dataspace = Dataspace::HEIF;
break;
+ case PublicFormat::JPEG_R:
+ dataspace = Dataspace::JPEG_R;
+ break;
default:
// Most formats map to UNKNOWN
dataspace = Dataspace::UNKNOWN;
@@ -139,14 +144,16 @@
switch (ds) {
case Dataspace::DEPTH:
return PublicFormat::DEPTH_POINT_CLOUD;
- case Dataspace::V0_JFIF:
+ case Dataspace::JFIF:
return PublicFormat::JPEG;
case Dataspace::HEIF:
return PublicFormat::HEIC;
default:
if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) {
return PublicFormat::DEPTH_JPEG;
- } else {
+ } else if (dataSpace == static_cast<android_dataspace>(Dataspace::JPEG_R)) {
+ return PublicFormat::JPEG_R;
+ }else {
// Assume otherwise-marked blobs are also JPEG
return PublicFormat::JPEG;
}
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index a2791a6..65a8769 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <type_traits>
+#include <ui/GraphicTypes.h>
#include <ui/Size.h>
#include <utils/Flattenable.h>
#include <utils/Timers.h>
@@ -34,6 +35,7 @@
ui::Size resolution;
float xDpi = 0;
float yDpi = 0;
+ std::vector<ui::Hdr> supportedHdrTypes;
float refreshRate = 0;
nsecs_t appVsyncOffset = 0;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 8c9fe4c..0b77754 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -35,6 +35,7 @@
// we can't use size_t because it may have different width
// in the client process.
ui::DisplayModeId activeDisplayModeId;
+ float renderFrameRate;
std::vector<ui::ColorMode> supportedColorModes;
ui::ColorMode activeColorMode;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 8661c36..1775d39 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -20,6 +20,7 @@
#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
#include <aidl/android/hardware/graphics/common/Compression.h>
#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/common/Interlaced.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
#include <aidl/android/hardware/graphics/common/Smpte2086.h>
@@ -42,7 +43,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
/**
@@ -50,6 +50,7 @@
*/
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Hdr;
using aidl::android::hardware::graphics::common::PlaneLayout;
using aidl::android::hardware::graphics::common::Smpte2086;
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index f422ce4..cf5c2e8 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -53,16 +53,19 @@
// real pixel formats supported for rendering -----------------------------
- PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA
- PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0
- PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB
- PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB
- PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA
- PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB
- PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
- PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
- PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
- PIXEL_FORMAT_R_8 = 0x38,
+ PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA
+ PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0
+ PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB
+ PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB
+ PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA
+ PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB
+ PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
+ PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
+ PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
+ PIXEL_FORMAT_R_8 = 0x38,
+ PIXEL_FORMAT_R_16_UINT = 0x39,
+ PIXEL_FORMAT_RG_1616_UINT = 0x3a,
+ PIXEL_FORMAT_RGBA_10101010 = 0x3b,
};
typedef int32_t PixelFormat;
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
index aa58805..2248cca 100644
--- a/libs/ui/include/ui/PublicFormat.h
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -57,6 +57,7 @@
YCBCR_P010 = 0x36,
DEPTH16 = 0x44363159,
DEPTH_JPEG = 0x69656963,
+ JPEG_R = 0x1005,
HEIC = 0x48454946,
};
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
index 83d431d..c1d60f4 100644
--- a/libs/ui/include/ui/Rotation.h
+++ b/libs/ui/include/ui/Rotation.h
@@ -20,7 +20,14 @@
namespace android::ui {
-enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 };
+enum class Rotation {
+ Rotation0 = 0,
+ Rotation90 = 1,
+ Rotation180 = 2,
+ Rotation270 = 3,
+
+ ftl_last = Rotation270
+};
// Equivalent to Surface.java constants.
constexpr auto ROTATION_0 = Rotation::Rotation0;
diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h
index a461cb4..cd31167 100644
--- a/libs/ui/include_types/ui/DataspaceUtils.h
+++ b/libs/ui/include_types/ui/DataspaceUtils.h
@@ -22,8 +22,10 @@
inline bool isHdrDataspace(ui::Dataspace dataspace) {
const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+ const auto range = dataspace & HAL_DATASPACE_RANGE_MASK;
- return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG ||
+ range == HAL_DATASPACE_RANGE_EXTENDED;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
index 3e09671..ffe6438 100644
--- a/libs/ui/tests/DataspaceUtils_test.cpp
+++ b/libs/ui/tests/DataspaceUtils_test.cpp
@@ -29,12 +29,13 @@
EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
+ // The original formulation of scRGB indicates the same white points as that
+ // of sRGB, however scRGB may be used to implement HDR.
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
- // scRGB defines a very wide gamut but not an expanded luminance range
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
deleted file mode 100644
index 0bda798..0000000
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2016 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
- "buffer_hub_queue_client.cpp",
- "buffer_hub_queue_parcelable.cpp",
-]
-
-includeFiles = [
- "include",
-]
-
-staticLibraries = [
- "libbufferhub",
-]
-
-sharedLibraries = [
- "libbinder",
- "libcutils",
- "liblog",
- "libui",
- "libutils",
- "libpdx_default_transport",
-]
-
-headerLibraries = [
- "libdvr_headers",
- "libnativebase_headers",
-]
-
-cc_library_shared {
- name: "libbufferhubqueue",
- cflags: [
- "-DLOG_TAG=\"libbufferhubqueue\"",
- "-DTRACE=0",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-Wall",
- "-Werror",
- "-Wno-format",
- "-Wno-unused-parameter",
- "-Wno-unused-variable",
- ],
- srcs: sourceFiles,
- export_include_dirs: includeFiles,
- export_static_lib_headers: staticLibraries,
- static_libs: staticLibraries,
- shared_libs: sharedLibraries,
- header_libs: headerLibraries,
-}
-
-subdirs = ["benchmarks", "tests"]
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
deleted file mode 100644
index 2d3fa4a..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ /dev/null
@@ -1,823 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_client.h"
-
-#include <inttypes.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/epoll.h>
-
-#include <array>
-
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <pdx/trace.h>
-
-#define RETRY_EINTR(fnc_call) \
- ([&]() -> decltype(fnc_call) { \
- decltype(fnc_call) result; \
- do { \
- result = (fnc_call); \
- } while (result == -1 && errno == EINTR); \
- return result; \
- })()
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-std::pair<int32_t, int32_t> Unstuff(uint64_t value) {
- return {static_cast<int32_t>(value >> 32),
- static_cast<int32_t>(value & ((1ull << 32) - 1))};
-}
-
-uint64_t Stuff(int32_t a, int32_t b) {
- const uint32_t ua = static_cast<uint32_t>(a);
- const uint32_t ub = static_cast<uint32_t>(b);
- return (static_cast<uint64_t>(ua) << 32) | static_cast<uint64_t>(ub);
-}
-
-} // anonymous namespace
-
-BufferHubQueue::BufferHubQueue(LocalChannelHandle channel_handle)
- : Client{pdx::default_transport::ClientChannel::Create(
- std::move(channel_handle))} {
- Initialize();
-}
-
-BufferHubQueue::BufferHubQueue(const std::string& endpoint_path)
- : Client{
- pdx::default_transport::ClientChannelFactory::Create(endpoint_path)} {
- Initialize();
-}
-
-void BufferHubQueue::Initialize() {
- int ret = epoll_fd_.Create();
- if (ret < 0) {
- ALOGE("BufferHubQueue::BufferHubQueue: Failed to create epoll fd: %s",
- strerror(-ret));
- return;
- }
-
- epoll_event event = {
- .events = EPOLLIN | EPOLLET,
- .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}};
- ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event);
- if (ret < 0) {
- ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__,
- strerror(-ret));
- }
-}
-
-Status<void> BufferHubQueue::ImportQueue() {
- auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>();
- if (!status) {
- ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return ErrorStatus(status.error());
- } else {
- SetupQueue(status.get());
- return {};
- }
-}
-
-void BufferHubQueue::SetupQueue(const QueueInfo& queue_info) {
- is_async_ = queue_info.producer_config.is_async;
- default_width_ = queue_info.producer_config.default_width;
- default_height_ = queue_info.producer_config.default_height;
- default_format_ = queue_info.producer_config.default_format;
- user_metadata_size_ = queue_info.producer_config.user_metadata_size;
- id_ = queue_info.id;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateConsumerQueue() {
- if (auto status = CreateConsumerQueueHandle(/*silent*/ false))
- return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
- else
- return nullptr;
-}
-
-std::unique_ptr<ConsumerQueue> BufferHubQueue::CreateSilentConsumerQueue() {
- if (auto status = CreateConsumerQueueHandle(/*silent*/ true))
- return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
- else
- return nullptr;
-}
-
-Status<LocalChannelHandle> BufferHubQueue::CreateConsumerQueueHandle(
- bool silent) {
- auto status = InvokeRemoteMethod<BufferHubRPC::CreateConsumerQueue>(silent);
- if (!status) {
- ALOGE(
- "BufferHubQueue::CreateConsumerQueue: Failed to create consumer queue: "
- "%s",
- status.GetErrorMessage().c_str());
- return ErrorStatus(status.error());
- }
-
- return status;
-}
-
-pdx::Status<ConsumerQueueParcelable>
-BufferHubQueue::CreateConsumerQueueParcelable(bool silent) {
- auto status = CreateConsumerQueueHandle(silent);
- if (!status)
- return status.error_status();
-
- // A temporary consumer queue client to pull its channel parcelable.
- auto consumer_queue =
- std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
- ConsumerQueueParcelable queue_parcelable(
- consumer_queue->GetChannel()->TakeChannelParcelable());
-
- if (!queue_parcelable.IsValid()) {
- ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__);
- return ErrorStatus(EINVAL);
- }
-
- return {std::move(queue_parcelable)};
-}
-
-bool BufferHubQueue::WaitForBuffers(int timeout) {
- ATRACE_NAME("BufferHubQueue::WaitForBuffers");
- std::array<epoll_event, kMaxEvents> events;
-
- // Loop at least once to check for hangups.
- do {
- ALOGD_IF(
- TRACE,
- "BufferHubQueue::WaitForBuffers: queue_id=%d count=%zu capacity=%zu",
- id(), count(), capacity());
-
- // If there is already a buffer then just check for hangup without waiting.
- const int ret = epoll_fd_.Wait(events.data(), events.size(),
- count() == 0 ? timeout : 0);
-
- if (ret == 0) {
- ALOGI_IF(TRACE,
- "BufferHubQueue::WaitForBuffers: No events before timeout: "
- "queue_id=%d",
- id());
- return count() != 0;
- }
-
- if (ret < 0 && ret != -EINTR) {
- ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret));
- return false;
- }
-
- const int num_events = ret;
-
- // A BufferQueue's epoll fd tracks N+1 events, where there are N events,
- // one for each buffer in the queue, and one extra event for the queue
- // client itself.
- for (int i = 0; i < num_events; i++) {
- int32_t event_fd;
- int32_t index;
- std::tie(event_fd, index) = Unstuff(events[i].data.u64);
-
- PDX_TRACE_FORMAT(
- "epoll_event|queue_id=%d;num_events=%d;event_index=%d;event_fd=%d;"
- "slot=%d|",
- id(), num_events, i, event_fd, index);
-
- ALOGD_IF(TRACE,
- "BufferHubQueue::WaitForBuffers: event %d: event_fd=%d index=%d",
- i, event_fd, index);
-
- if (is_buffer_event_index(index)) {
- HandleBufferEvent(static_cast<size_t>(index), event_fd,
- events[i].events);
- } else if (is_queue_event_index(index)) {
- HandleQueueEvent(events[i].events);
- } else {
- ALOGW(
- "BufferHubQueue::WaitForBuffers: Unknown event type event_fd=%d "
- "index=%d",
- event_fd, index);
- }
- }
- } while (count() == 0 && capacity() > 0 && !hung_up());
-
- return count() != 0;
-}
-
-Status<void> BufferHubQueue::HandleBufferEvent(size_t slot, int event_fd,
- int poll_events) {
- ATRACE_NAME("BufferHubQueue::HandleBufferEvent");
- if (!buffers_[slot]) {
- ALOGW("BufferHubQueue::HandleBufferEvent: Invalid buffer slot: %zu", slot);
- return ErrorStatus(ENOENT);
- }
-
- auto status = buffers_[slot]->GetEventMask(poll_events);
- if (!status) {
- ALOGW("BufferHubQueue::HandleBufferEvent: Failed to get event mask: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- const int events = status.get();
- PDX_TRACE_FORMAT(
- "buffer|queue_id=%d;buffer_id=%d;slot=%zu;event_fd=%d;poll_events=%x;"
- "events=%d|",
- id(), buffers_[slot]->id(), slot, event_fd, poll_events, events);
-
- if (events & EPOLLIN) {
- return Enqueue({buffers_[slot], slot, buffers_[slot]->GetQueueIndex()});
- } else if (events & EPOLLHUP) {
- ALOGW(
- "BufferHubQueue::HandleBufferEvent: Received EPOLLHUP event: slot=%zu "
- "event_fd=%d buffer_id=%d",
- slot, buffers_[slot]->event_fd(), buffers_[slot]->id());
- return RemoveBuffer(slot);
- } else {
- ALOGW(
- "BufferHubQueue::HandleBufferEvent: Unknown event, slot=%zu, epoll "
- "events=%d",
- slot, events);
- }
-
- return {};
-}
-
-Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) {
- ATRACE_NAME("BufferHubQueue::HandleQueueEvent");
- auto status = GetEventMask(poll_event);
- if (!status) {
- ALOGW("BufferHubQueue::HandleQueueEvent: Failed to get event mask: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- const int events = status.get();
- if (events & EPOLLIN) {
- // Note that after buffer imports, if |count()| still returns 0, epoll
- // wait will be tried again to acquire the newly imported buffer.
- auto buffer_status = OnBufferAllocated();
- if (!buffer_status) {
- ALOGE("%s: Failed to import buffer: %s", __FUNCTION__,
- buffer_status.GetErrorMessage().c_str());
- }
- } else if (events & EPOLLHUP) {
- ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__);
- hung_up_ = true;
- } else {
- ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events);
- }
-
- return {};
-}
-
-Status<void> BufferHubQueue::AddBuffer(
- const std::shared_ptr<BufferHubBase>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(),
- slot);
-
- if (is_full()) {
- ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_);
- return ErrorStatus(E2BIG);
- }
-
- if (buffers_[slot]) {
- // Replace the buffer if the slot is occupied. This could happen when the
- // producer side replaced the slot with a newly allocated buffer. Remove the
- // buffer before setting up with the new one.
- auto remove_status = RemoveBuffer(slot);
- if (!remove_status)
- return remove_status.error_status();
- }
-
- for (const auto& event_source : buffer->GetEventSources()) {
- epoll_event event = {.events = event_source.event_mask | EPOLLET,
- .data = {.u64 = Stuff(buffer->event_fd(), slot)}};
- const int ret =
- epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event);
- if (ret < 0) {
- ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__,
- strerror(-ret));
- return ErrorStatus(-ret);
- }
- }
-
- buffers_[slot] = buffer;
- capacity_++;
- return {};
-}
-
-Status<void> BufferHubQueue::RemoveBuffer(size_t slot) {
- ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot);
-
- if (buffers_[slot]) {
- for (const auto& event_source : buffers_[slot]->GetEventSources()) {
- const int ret =
- epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr);
- if (ret < 0) {
- ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__,
- strerror(-ret));
- return ErrorStatus(-ret);
- }
- }
-
- // Trigger OnBufferRemoved callback if registered.
- if (on_buffer_removed_)
- on_buffer_removed_(buffers_[slot]);
-
- buffers_[slot] = nullptr;
- capacity_--;
- }
-
- return {};
-}
-
-Status<void> BufferHubQueue::Enqueue(Entry entry) {
- if (!is_full()) {
- // Find and remove the enqueued buffer from unavailable_buffers_slot if
- // exist.
- auto enqueued_buffer_iter = std::find_if(
- unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(),
- [&entry](size_t slot) -> bool { return slot == entry.slot; });
- if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) {
- unavailable_buffers_slot_.erase(enqueued_buffer_iter);
- }
-
- available_buffers_.push(std::move(entry));
-
- // Trigger OnBufferAvailable callback if registered.
- if (on_buffer_available_)
- on_buffer_available_();
-
- return {};
- } else {
- ALOGE("%s: Buffer queue is full!", __FUNCTION__);
- return ErrorStatus(E2BIG);
- }
-}
-
-Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout,
- size_t* slot) {
- ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout);
-
- PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count());
-
- if (count() == 0) {
- if (!WaitForBuffers(timeout))
- return ErrorStatus(ETIMEDOUT);
- }
-
- auto& entry = available_buffers_.top();
- PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(),
- entry.slot);
-
- std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer);
- *slot = entry.slot;
-
- available_buffers_.pop();
- unavailable_buffers_slot_.push_back(*slot);
-
- return {std::move(buffer)};
-}
-
-void BufferHubQueue::SetBufferAvailableCallback(
- BufferAvailableCallback callback) {
- on_buffer_available_ = callback;
-}
-
-void BufferHubQueue::SetBufferRemovedCallback(BufferRemovedCallback callback) {
- on_buffer_removed_ = callback;
-}
-
-pdx::Status<void> BufferHubQueue::FreeAllBuffers() {
- // Clear all available buffers.
- while (!available_buffers_.empty())
- available_buffers_.pop();
-
- pdx::Status<void> last_error; // No error.
- // Clear all buffers this producer queue is tracking.
- for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) {
- if (buffers_[slot] != nullptr) {
- auto status = RemoveBuffer(slot);
- if (!status) {
- ALOGE(
- "ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
- "slot=%zu.",
- slot);
- last_error = status.error_status();
- }
- }
- }
-
- return last_error;
-}
-
-ProducerQueue::ProducerQueue(LocalChannelHandle handle)
- : BASE(std::move(handle)) {
- auto status = ImportQueue();
- if (!status) {
- ALOGE("ProducerQueue::ProducerQueue: Failed to import queue: %s",
- status.GetErrorMessage().c_str());
- Close(-status.error());
- }
-}
-
-ProducerQueue::ProducerQueue(const ProducerQueueConfig& config,
- const UsagePolicy& usage)
- : BASE(BufferHubRPC::kClientPath) {
- auto status =
- InvokeRemoteMethod<BufferHubRPC::CreateProducerQueue>(config, usage);
- if (!status) {
- ALOGE("ProducerQueue::ProducerQueue: Failed to create producer queue: %s",
- status.GetErrorMessage().c_str());
- Close(-status.error());
- return;
- }
-
- SetupQueue(status.get());
-}
-
-Status<std::vector<size_t>> ProducerQueue::AllocateBuffers(
- uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t buffer_count) {
- if (buffer_count == 0) {
- return {std::vector<size_t>()};
- }
-
- if (capacity() + buffer_count > kMaxQueueCapacity) {
- ALOGE(
- "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot "
- "allocate %zu more buffer(s).",
- capacity(), buffer_count);
- return ErrorStatus(E2BIG);
- }
-
- Status<std::vector<std::pair<LocalChannelHandle, size_t>>> status =
- InvokeRemoteMethod<BufferHubRPC::ProducerQueueAllocateBuffers>(
- width, height, layer_count, format, usage, buffer_count);
- if (!status) {
- ALOGE("ProducerQueue::AllocateBuffers: failed to allocate buffers: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto buffer_handle_slots = status.take();
- LOG_ALWAYS_FATAL_IF(buffer_handle_slots.size() != buffer_count,
- "BufferHubRPC::ProducerQueueAllocateBuffers should "
- "return %zu buffer handle(s), but returned %zu instead.",
- buffer_count, buffer_handle_slots.size());
-
- std::vector<size_t> buffer_slots;
- buffer_slots.reserve(buffer_count);
-
- // Bookkeeping for each buffer.
- for (auto& hs : buffer_handle_slots) {
- auto& buffer_handle = hs.first;
- size_t buffer_slot = hs.second;
-
- // Note that import might (though very unlikely) fail. If so, buffer_handle
- // will be closed and included in returned buffer_slots.
- if (AddBuffer(ProducerBuffer::Import(std::move(buffer_handle)),
- buffer_slot)) {
- ALOGD_IF(TRACE, "ProducerQueue::AllocateBuffers: new buffer at slot: %zu",
- buffer_slot);
- buffer_slots.push_back(buffer_slot);
- }
- }
-
- if (buffer_slots.size() != buffer_count) {
- // Error out if the count of imported buffer(s) is not correct.
- ALOGE(
- "ProducerQueue::AllocateBuffers: requested to import %zu "
- "buffers, but actually imported %zu buffers.",
- buffer_count, buffer_slots.size());
- return ErrorStatus(ENOMEM);
- }
-
- return {std::move(buffer_slots)};
-}
-
-Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height,
- uint32_t layer_count,
- uint32_t format, uint64_t usage) {
- // We only allocate one buffer at a time.
- constexpr size_t buffer_count = 1;
- auto status =
- AllocateBuffers(width, height, layer_count, format, usage, buffer_count);
- if (!status) {
- ALOGE("ProducerQueue::AllocateBuffer: Failed to allocate buffer: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return {status.get()[0]};
-}
-
-Status<void> ProducerQueue::AddBuffer(
- const std::shared_ptr<ProducerBuffer>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "ProducerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu",
- id(), buffer->id(), slot);
- // For producer buffer, we need to enqueue the newly added buffer
- // immediately. Producer queue starts with all buffers in available state.
- auto status = BufferHubQueue::AddBuffer(buffer, slot);
- if (!status)
- return status;
-
- return BufferHubQueue::Enqueue({buffer, slot, 0ULL});
-}
-
-Status<size_t> ProducerQueue::InsertBuffer(
- const std::shared_ptr<ProducerBuffer>& buffer) {
- if (buffer == nullptr ||
- !BufferHubDefs::isClientGained(buffer->buffer_state(),
- buffer->client_state_mask())) {
- ALOGE(
- "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
- "gained state.");
- return ErrorStatus(EINVAL);
- }
-
- auto status_or_slot =
- InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>(
- buffer->cid());
- if (!status_or_slot) {
- ALOGE(
- "ProducerQueue::InsertBuffer: Failed to insert producer buffer: "
- "buffer_cid=%d, error: %s.",
- buffer->cid(), status_or_slot.GetErrorMessage().c_str());
- return status_or_slot.error_status();
- }
-
- size_t slot = status_or_slot.get();
-
- // Note that we are calling AddBuffer() from the base class to explicitly
- // avoid Enqueue() the ProducerBuffer.
- auto status = BufferHubQueue::AddBuffer(buffer, slot);
- if (!status) {
- ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
- return {slot};
-}
-
-Status<void> ProducerQueue::RemoveBuffer(size_t slot) {
- auto status =
- InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot);
- if (!status) {
- ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return BufferHubQueue::RemoveBuffer(slot);
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
- int timeout, size_t* slot, LocalHandle* release_fence) {
- DvrNativeBufferMetadata canonical_meta;
- return Dequeue(timeout, slot, &canonical_meta, release_fence);
-}
-
-pdx::Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* release_fence, bool gain_posted_buffer) {
- ATRACE_NAME("ProducerQueue::Dequeue");
- if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) {
- ALOGE("%s: Invalid parameter.", __FUNCTION__);
- return ErrorStatus(EINVAL);
- }
-
- std::shared_ptr<ProducerBuffer> buffer;
- Status<std::shared_ptr<BufferHubBase>> dequeue_status =
- BufferHubQueue::Dequeue(timeout, slot);
- if (dequeue_status.ok()) {
- buffer = std::static_pointer_cast<ProducerBuffer>(dequeue_status.take());
- } else {
- if (gain_posted_buffer) {
- Status<std::shared_ptr<ProducerBuffer>> dequeue_unacquired_status =
- ProducerQueue::DequeueUnacquiredBuffer(slot);
- if (!dequeue_unacquired_status.ok()) {
- ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__,
- dequeue_unacquired_status.error());
- return dequeue_unacquired_status.error_status();
- }
- buffer = dequeue_unacquired_status.take();
- } else {
- return dequeue_status.error_status();
- }
- }
- const int ret =
- buffer->GainAsync(out_meta, release_fence, gain_posted_buffer);
- if (ret < 0 && ret != -EALREADY)
- return ErrorStatus(-ret);
-
- return {std::move(buffer)};
-}
-
-Status<std::shared_ptr<ProducerBuffer>> ProducerQueue::DequeueUnacquiredBuffer(
- size_t* slot) {
- if (unavailable_buffers_slot_.size() < 1) {
- ALOGE(
- "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in "
- "acquired state if exist.",
- __FUNCTION__);
- return ErrorStatus(ENOMEM);
- }
-
- // Find the first buffer that is not in acquired state from
- // unavailable_buffers_slot_.
- for (auto iter = unavailable_buffers_slot_.begin();
- iter != unavailable_buffers_slot_.end(); iter++) {
- std::shared_ptr<ProducerBuffer> buffer = ProducerQueue::GetBuffer(*iter);
- if (buffer == nullptr) {
- ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__,
- static_cast<int>(*slot));
- return ErrorStatus(EIO);
- }
- if (!BufferHubDefs::isAnyClientAcquired(buffer->buffer_state())) {
- *slot = *iter;
- unavailable_buffers_slot_.erase(iter);
- unavailable_buffers_slot_.push_back(*slot);
- ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d",
- __FUNCTION__, static_cast<int>(*slot));
- return {std::move(buffer)};
- }
- }
- ALOGE(
- "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.",
- __FUNCTION__);
- return ErrorStatus(EBUSY);
-}
-
-pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
- if (capacity() != 0) {
- ALOGE(
- "%s: producer queue can only be taken out as a parcelable when empty. "
- "Current queue capacity: %zu",
- __FUNCTION__, capacity());
- return ErrorStatus(EINVAL);
- }
-
- std::unique_ptr<pdx::ClientChannel> channel = TakeChannel();
- ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable());
-
- // Here the queue parcelable is returned and holds the underlying system
- // resources backing the queue; while the original client channel of this
- // producer queue is destroyed in place so that this client can no longer
- // provide producer operations.
- return {std::move(queue_parcelable)};
-}
-
-/*static */
-std::unique_ptr<ConsumerQueue> ConsumerQueue::Import(
- LocalChannelHandle handle) {
- return std::unique_ptr<ConsumerQueue>(new ConsumerQueue(std::move(handle)));
-}
-
-ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
- : BufferHubQueue(std::move(handle)) {
- auto status = ImportQueue();
- if (!status) {
- ALOGE("%s: Failed to import queue: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- Close(-status.error());
- }
-
- auto import_status = ImportBuffers();
- if (import_status) {
- ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get());
- } else {
- ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
- import_status.GetErrorMessage().c_str());
- }
-}
-
-Status<size_t> ConsumerQueue::ImportBuffers() {
- auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>();
- if (!status) {
- if (status.error() == EBADR) {
- ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__);
- return {0};
- } else {
- ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
- }
-
- int ret;
- Status<void> last_error;
- size_t imported_buffers_count = 0;
-
- auto buffer_handle_slots = status.take();
- for (auto& buffer_handle_slot : buffer_handle_slots) {
- ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__,
- buffer_handle_slot.first.value());
-
- std::unique_ptr<ConsumerBuffer> consumer_buffer =
- ConsumerBuffer::Import(std::move(buffer_handle_slot.first));
- if (!consumer_buffer) {
- ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__,
- buffer_handle_slot.second);
- last_error = ErrorStatus(EPIPE);
- continue;
- }
-
- auto add_status =
- AddBuffer(std::move(consumer_buffer), buffer_handle_slot.second);
- if (!add_status) {
- ALOGE("%s: Failed to add buffer: %s", __FUNCTION__,
- add_status.GetErrorMessage().c_str());
- last_error = add_status;
- } else {
- imported_buffers_count++;
- }
- }
-
- if (imported_buffers_count > 0)
- return {imported_buffers_count};
- else
- return last_error.error_status();
-}
-
-Status<void> ConsumerQueue::AddBuffer(
- const std::shared_ptr<ConsumerBuffer>& buffer, size_t slot) {
- ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(),
- buffer->id(), slot);
- return BufferHubQueue::AddBuffer(buffer, slot);
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
- int timeout, size_t* slot, void* meta, size_t user_metadata_size,
- LocalHandle* acquire_fence) {
- if (user_metadata_size != user_metadata_size_) {
- ALOGE(
- "%s: Metadata size (%zu) for the dequeuing buffer does not match "
- "metadata size (%zu) for the queue.",
- __FUNCTION__, user_metadata_size, user_metadata_size_);
- return ErrorStatus(EINVAL);
- }
-
- DvrNativeBufferMetadata canonical_meta;
- auto status = Dequeue(timeout, slot, &canonical_meta, acquire_fence);
- if (!status)
- return status.error_status();
-
- if (meta && user_metadata_size) {
- void* metadata_src =
- reinterpret_cast<void*>(canonical_meta.user_metadata_ptr);
- if (metadata_src) {
- memcpy(meta, metadata_src, user_metadata_size);
- } else {
- ALOGW("%s: no user-defined metadata.", __FUNCTION__);
- }
- }
-
- return status;
-}
-
-Status<std::shared_ptr<ConsumerBuffer>> ConsumerQueue::Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* acquire_fence) {
- ATRACE_NAME("ConsumerQueue::Dequeue");
- if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) {
- ALOGE("%s: Invalid parameter.", __FUNCTION__);
- return ErrorStatus(EINVAL);
- }
-
- auto status = BufferHubQueue::Dequeue(timeout, slot);
- if (!status)
- return status.error_status();
-
- auto buffer = std::static_pointer_cast<ConsumerBuffer>(status.take());
- const int ret = buffer->AcquireAsync(out_meta, acquire_fence);
- if (ret < 0)
- return ErrorStatus(-ret);
-
- return {std::move(buffer)};
-}
-
-Status<void> ConsumerQueue::OnBufferAllocated() {
- ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id());
-
- auto status = ImportBuffers();
- if (!status) {
- ALOGE("%s: Failed to import buffers: %s", __FUNCTION__,
- status.GetErrorMessage().c_str());
- return ErrorStatus(status.error());
- } else if (status.get() == 0) {
- ALOGW("%s: No new buffers allocated!", __FUNCTION__);
- return ErrorStatus(ENOBUFS);
- } else {
- ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__,
- status.get());
- return {};
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
deleted file mode 100644
index f705749..0000000
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "include/private/dvr/buffer_hub_queue_parcelable.h"
-
-#include <binder/Parcel.h>
-#include <pdx/default_transport/channel_parcelable.h>
-
-namespace android {
-namespace dvr {
-
-template <BufferHubQueueParcelableMagic Magic>
-bool BufferHubQueueParcelable<Magic>::IsValid() const {
- return !!channel_parcelable_ && channel_parcelable_->IsValid();
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() {
- if (!IsValid()) {
- ALOGE(
- "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel.");
- return {}; // Returns an empty channel handle.
- }
-
- // Take channel handle out of the parcelable and reset the parcelable.
- pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle();
- // Now channel_parcelable_ should already be invalid, but reset it to release
- // the invalid parcelable object from unique_ptr.
- channel_parcelable_ = nullptr;
- return handle;
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const {
- if (!IsValid()) {
- ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel.");
- return -EINVAL;
- }
-
- status_t res = parcel->writeUint32(Magic);
- if (res != OK) {
- ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
- return res;
- }
-
- return channel_parcelable_->writeToParcel(parcel);
-}
-
-template <BufferHubQueueParcelableMagic Magic>
-status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) {
- if (IsValid()) {
- ALOGE(
- "BufferHubQueueParcelable::readFromParcel: This parcelable object has "
- "been initialized already.");
- return -EINVAL;
- }
-
- uint32_t out_magic = 0;
- status_t res = OK;
-
- res = parcel->readUint32(&out_magic);
- if (res != OK)
- return res;
-
- if (out_magic != Magic) {
- ALOGE(
- "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, "
- "epxected: 0x%x",
- out_magic, Magic);
- return -EINVAL;
- }
-
- // (Re)Alocate channel parcelable object.
- channel_parcelable_ =
- std::make_unique<pdx::default_transport::ChannelParcelable>();
- return channel_parcelable_->readFromParcel(parcel);
-}
-
-template class BufferHubQueueParcelable<
- BufferHubQueueParcelableMagic::Producer>;
-template class BufferHubQueueParcelable<
- BufferHubQueueParcelableMagic::Consumer>;
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
deleted file mode 100644
index 74b4b3d..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ /dev/null
@@ -1,476 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
-
-#include <ui/BufferQueueDefs.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/buffer_hub_queue_parcelable.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/epoll_file_descriptor.h>
-#include <private/dvr/producer_buffer.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-#include <memory>
-#include <queue>
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-class ConsumerQueue;
-
-// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are
-// automatically re-requeued when released by the remote side.
-class BufferHubQueue : public pdx::Client {
- public:
- using BufferAvailableCallback = std::function<void()>;
- using BufferRemovedCallback =
- std::function<void(const std::shared_ptr<BufferHubBase>&)>;
-
- virtual ~BufferHubQueue() {}
-
- // Creates a new consumer queue that is attached to the producer. Returns
- // a new consumer queue client or nullptr on failure.
- std::unique_ptr<ConsumerQueue> CreateConsumerQueue();
-
- // Creates a new consumer queue that is attached to the producer. This queue
- // sets each of its imported consumer buffers to the ignored state to avoid
- // participation in lifecycle events.
- std::unique_ptr<ConsumerQueue> CreateSilentConsumerQueue();
-
- // Returns whether the buffer queue is in async mode.
- bool is_async() const { return is_async_; }
-
- // Returns the default buffer width of this buffer queue.
- uint32_t default_width() const { return default_width_; }
-
- // Returns the default buffer height of this buffer queue.
- uint32_t default_height() const { return default_height_; }
-
- // Returns the default buffer format of this buffer queue.
- uint32_t default_format() const { return default_format_; }
-
- // Creates a new consumer in handle form for immediate transport over RPC.
- pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
- bool silent = false);
-
- // Creates a new consumer in parcelable form for immediate transport over
- // Binder.
- pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable(
- bool silent = false);
-
- // Returns the number of buffers avaiable for dequeue.
- size_t count() const { return available_buffers_.size(); }
-
- // Returns the total number of buffers that the queue is tracking.
- size_t capacity() const { return capacity_; }
-
- // Returns the size of metadata structure associated with this queue.
- size_t metadata_size() const { return user_metadata_size_; }
-
- // Returns whether the buffer queue is full.
- bool is_full() const {
- return available_buffers_.size() >= kMaxQueueCapacity;
- }
-
- // Returns whether the buffer queue is connected to bufferhubd.
- bool is_connected() const { return !!GetChannel(); }
-
- int GetBufferId(size_t slot) const {
- return (slot < buffers_.size() && buffers_[slot]) ? buffers_[slot]->id()
- : -1;
- }
-
- std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const {
- return buffers_[slot];
- }
-
- pdx::Status<int> GetEventMask(int events) {
- if (auto* client_channel = GetChannel()) {
- return client_channel->GetEventMask(events);
- } else {
- return pdx::ErrorStatus(EINVAL);
- }
- }
-
- // Returns an fd that signals pending queue events using
- // EPOLLIN/POLLIN/readible. Either HandleQueueEvents or WaitForBuffers may be
- // called to handle pending queue events.
- int queue_fd() const { return epoll_fd_.Get(); }
-
- // Handles any pending events, returning available buffers to the queue and
- // reaping disconnected buffers. Returns true if successful, false if an error
- // occurred.
- bool HandleQueueEvents() { return WaitForBuffers(0); }
-
- // Set buffer event callbacks, which are std::function wrappers. The caller is
- // responsible for ensuring the validity of these callbacks' callable targets.
- void SetBufferAvailableCallback(BufferAvailableCallback callback);
- void SetBufferRemovedCallback(BufferRemovedCallback callback);
-
- // The queue tracks at most this many buffers.
- static constexpr size_t kMaxQueueCapacity =
- android::BufferQueueDefs::NUM_BUFFER_SLOTS;
-
- static constexpr int kNoTimeOut = -1;
-
- int id() const { return id_; }
- bool hung_up() const { return hung_up_; }
-
- protected:
- explicit BufferHubQueue(pdx::LocalChannelHandle channel);
- explicit BufferHubQueue(const std::string& endpoint_path);
-
- // Imports the queue parameters by querying BufferHub for the parameters for
- // this channel.
- pdx::Status<void> ImportQueue();
-
- // Sets up the queue with the given parameters.
- void SetupQueue(const QueueInfo& queue_info);
-
- // Register a buffer for management by the queue. Used by subclasses to add a
- // buffer to internal bookkeeping.
- pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer,
- size_t slot);
-
- // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only
- // to deregister a buffer for epoll and internal bookkeeping.
- virtual pdx::Status<void> RemoveBuffer(size_t slot);
-
- // Free all buffers that belongs to this queue. Can only be called from
- // producer side.
- virtual pdx::Status<void> FreeAllBuffers();
-
- // Dequeue a buffer from the free queue, blocking until one is available. The
- // timeout argument specifies the number of milliseconds that |Dequeue()| will
- // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely,
- // while specifying a timeout equal to zero cause Dequeue() to return
- // immediately, even if no buffers are available.
- pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout,
- size_t* slot);
-
- // Waits for buffers to become available and adds them to the available queue.
- bool WaitForBuffers(int timeout);
-
- pdx::Status<void> HandleBufferEvent(size_t slot, int event_fd,
- int poll_events);
- pdx::Status<void> HandleQueueEvent(int poll_events);
-
- // Entry in the priority queue of available buffers that stores related
- // per-buffer data.
- struct Entry {
- Entry() : slot(0) {}
- Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot,
- uint64_t in_index)
- : buffer(in_buffer), slot(in_slot), index(in_index) {}
- Entry(const std::shared_ptr<BufferHubBase>& in_buffer,
- std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence,
- size_t in_slot)
- : buffer(in_buffer),
- metadata(std::move(in_metadata)),
- fence(std::move(in_fence)),
- slot(in_slot) {}
- Entry(Entry&&) = default;
- Entry& operator=(Entry&&) = default;
-
- std::shared_ptr<BufferHubBase> buffer;
- std::unique_ptr<uint8_t[]> metadata;
- pdx::LocalHandle fence;
- size_t slot;
- uint64_t index;
- };
-
- struct EntryComparator {
- bool operator()(const Entry& lhs, const Entry& rhs) {
- return lhs.index > rhs.index;
- }
- };
-
- // Enqueues a buffer to the available list (Gained for producer or Acquireed
- // for consumer).
- pdx::Status<void> Enqueue(Entry entry);
-
- // Called when a buffer is allocated remotely.
- virtual pdx::Status<void> OnBufferAllocated() { return {}; }
-
- // Size of the metadata that buffers in this queue cary.
- size_t user_metadata_size_{0};
-
- // Buffers and related data that are available for dequeue.
- std::priority_queue<Entry, std::vector<Entry>, EntryComparator>
- available_buffers_;
-
- // Slot of the buffers that are not available for normal dequeue. For example,
- // the slot of posted or acquired buffers in the perspective of a producer.
- std::vector<size_t> unavailable_buffers_slot_;
-
- private:
- void Initialize();
-
- // Special epoll data field indicating that the epoll event refers to the
- // queue.
- static constexpr int64_t kEpollQueueEventIndex = -1;
-
- static constexpr size_t kMaxEvents = 128;
-
- // The u64 data field of an epoll event is interpreted as int64_t:
- // When |index| >= 0 and |index| < kMaxQueueCapacity it refers to a specific
- // element of |buffers_| as a direct index;
- static bool is_buffer_event_index(int64_t index) {
- return index >= 0 &&
- index < static_cast<int64_t>(BufferHubQueue::kMaxQueueCapacity);
- }
-
- // When |index| == kEpollQueueEventIndex it refers to the queue itself.
- static bool is_queue_event_index(int64_t index) {
- return index == BufferHubQueue::kEpollQueueEventIndex;
- }
-
- // Whether the buffer queue is operating in Async mode.
- // From GVR's perspective of view, this means a buffer can be acquired
- // asynchronously by the compositor.
- // From Android Surface's perspective of view, this is equivalent to
- // IGraphicBufferProducer's async mode. When in async mode, a producer
- // will never block even if consumer is running slow.
- bool is_async_{false};
-
- // Default buffer width that is set during ProducerQueue's creation.
- uint32_t default_width_{1};
-
- // Default buffer height that is set during ProducerQueue's creation.
- uint32_t default_height_{1};
-
- // Default buffer format that is set during ProducerQueue's creation.
- uint32_t default_format_{1}; // PIXEL_FORMAT_RGBA_8888
-
- // Tracks the buffers belonging to this queue. Buffers are stored according to
- // "slot" in this vector. Each slot is a logical id of the buffer within this
- // queue regardless of its queue position or presence in the ring buffer.
- std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_;
-
- // Keeps track with how many buffers have been added into the queue.
- size_t capacity_{0};
-
- // Epoll fd used to manage buffer events.
- EpollFileDescriptor epoll_fd_;
-
- // Flag indicating that the other side hung up. For ProducerQueues this
- // triggers when BufferHub dies or explicitly closes the queue channel. For
- // ConsumerQueues this can either mean the same or that the ProducerQueue on
- // the other end hung up.
- bool hung_up_{false};
-
- // Global id for the queue that is consistent across processes.
- int id_{-1};
-
- // Buffer event callbacks
- BufferAvailableCallback on_buffer_available_;
- BufferRemovedCallback on_buffer_removed_;
-
- BufferHubQueue(const BufferHubQueue&) = delete;
- void operator=(BufferHubQueue&) = delete;
-};
-
-class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> {
- public:
- // Usage bits in |usage_set_mask| will be automatically masked on. Usage bits
- // in |usage_clear_mask| will be automatically masked off. Note that
- // |usage_set_mask| and |usage_clear_mask| may conflict with each other, but
- // |usage_set_mask| takes precedence over |usage_clear_mask|. All buffer
- // allocation through this producer queue shall not have any of the usage bits
- // in |usage_deny_set_mask| set. Allocation calls violating this will be
- // rejected. All buffer allocation through this producer queue must have all
- // the usage bits in |usage_deny_clear_mask| set. Allocation calls violating
- // this will be rejected. Note that |usage_deny_set_mask| and
- // |usage_deny_clear_mask| shall not conflict with each other. Such
- // configuration will be treated as invalid input on creation.
- static std::unique_ptr<ProducerQueue> Create(
- const ProducerQueueConfig& config, const UsagePolicy& usage) {
- return BASE::Create(config, usage);
- }
-
- // Import a ProducerQueue from a channel handle.
- static std::unique_ptr<ProducerQueue> Import(pdx::LocalChannelHandle handle) {
- return BASE::Create(std::move(handle));
- }
-
- // Get a producer buffer. Note that the method doesn't check whether the
- // buffer slot has a valid buffer that has been allocated already. When no
- // buffer has been imported before it returns nullptr; otherwise it returns
- // a shared pointer to a ProducerBuffer.
- std::shared_ptr<ProducerBuffer> GetBuffer(size_t slot) const {
- return std::static_pointer_cast<ProducerBuffer>(
- BufferHubQueue::GetBuffer(slot));
- }
-
- // Batch allocate buffers. Once allocated, producer buffers are automatically
- // enqueue'd into the ProducerQueue and available to use (i.e. in GAINED
- // state). Upon success, returns a list of slots for each buffer allocated.
- pdx::Status<std::vector<size_t>> AllocateBuffers(
- uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t buffer_count);
-
- // Allocate producer buffer to populate the queue. Once allocated, a producer
- // buffer is automatically enqueue'd into the ProducerQueue and available to
- // use (i.e. in GAINED state). Upon success, returns the slot number for the
- // buffer allocated.
- pdx::Status<size_t> AllocateBuffer(uint32_t width, uint32_t height,
- uint32_t layer_count, uint32_t format,
- uint64_t usage);
-
- // Add a producer buffer to populate the queue. Once added, a producer buffer
- // is available to use (i.e. in GAINED state).
- pdx::Status<void> AddBuffer(const std::shared_ptr<ProducerBuffer>& buffer,
- size_t slot);
-
- // Inserts a ProducerBuffer into the queue. On success, the method returns the
- // |slot| number where the new buffer gets inserted. Note that the buffer
- // being inserted should be in Gain'ed state prior to the call and it's
- // considered as already Dequeued when the function returns.
- pdx::Status<size_t> InsertBuffer(
- const std::shared_ptr<ProducerBuffer>& buffer);
-
- // Remove producer buffer from the queue.
- pdx::Status<void> RemoveBuffer(size_t slot) override;
-
- // Free all buffers on this producer queue.
- pdx::Status<void> FreeAllBuffers() override {
- return BufferHubQueue::FreeAllBuffers();
- }
-
- // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
- // and caller should call Post() once it's done writing to release the buffer
- // to the consumer side.
- // @return a buffer in gained state, which was originally in released state.
- pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
- int timeout, size_t* slot, pdx::LocalHandle* release_fence);
-
- // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
- // and caller should call Post() once it's done writing to release the buffer
- // to the consumer side.
- //
- // @param timeout to dequeue a buffer.
- // @param slot is the slot of the output ProducerBuffer.
- // @param release_fence for gaining a buffer.
- // @param out_meta metadata of the output buffer.
- // @param gain_posted_buffer whether to gain posted buffer if no released
- // buffer is available to gain.
- // @return a buffer in gained state, which was originally in released state if
- // gain_posted_buffer is false, or in posted/released state if
- // gain_posted_buffer is true.
- // TODO(b/112007999): gain_posted_buffer true is only used to prevent
- // libdvrtracking from starving when there are non-responding clients. This
- // gain_posted_buffer param can be removed once libdvrtracking start to use
- // the new AHardwareBuffer API.
- pdx::Status<std::shared_ptr<ProducerBuffer>> Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* release_fence, bool gain_posted_buffer = false);
-
- // Enqueues a producer buffer in the queue.
- pdx::Status<void> Enqueue(const std::shared_ptr<ProducerBuffer>& buffer,
- size_t slot, uint64_t index) {
- return BufferHubQueue::Enqueue({buffer, slot, index});
- }
-
- // Takes out the current producer queue as a binder parcelable object. Note
- // that the queue must be empty to be exportable. After successful export, the
- // producer queue client should no longer be used.
- pdx::Status<ProducerQueueParcelable> TakeAsParcelable();
-
- private:
- friend BASE;
-
- // Constructors are automatically exposed through ProducerQueue::Create(...)
- // static template methods inherited from ClientBase, which take the same
- // arguments as the constructors.
- explicit ProducerQueue(pdx::LocalChannelHandle handle);
- ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage);
-
- // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode,
- // and caller should call Post() once it's done writing to release the buffer
- // to the consumer side.
- //
- // @param slot the slot of the returned buffer.
- // @return a buffer in gained state, which was originally in posted state or
- // released state.
- pdx::Status<std::shared_ptr<ProducerBuffer>> DequeueUnacquiredBuffer(
- size_t* slot);
-};
-
-class ConsumerQueue : public BufferHubQueue {
- public:
- // Get a consumer buffer. Note that the method doesn't check whether the
- // buffer slot has a valid buffer that has been imported already. When no
- // buffer has been imported before it returns nullptr; otherwise returns a
- // shared pointer to a ConsumerBuffer.
- std::shared_ptr<ConsumerBuffer> GetBuffer(size_t slot) const {
- return std::static_pointer_cast<ConsumerBuffer>(
- BufferHubQueue::GetBuffer(slot));
- }
-
- // Import a ConsumerQueue from a channel handle. |ignore_on_import| controls
- // whether or not buffers are set to be ignored when imported. This may be
- // used to avoid participation in the buffer lifecycle by a consumer queue
- // that is only used to spawn other consumer queues, such as in an
- // intermediate service.
- static std::unique_ptr<ConsumerQueue> Import(pdx::LocalChannelHandle handle);
-
- // Import newly created buffers from the service side.
- // Returns number of buffers successfully imported or an error.
- pdx::Status<size_t> ImportBuffers();
-
- // Dequeue a consumer buffer to read. The returned buffer in |Acquired|'ed
- // mode, and caller should call Releasse() once it's done writing to release
- // the buffer to the producer side. |meta| is passed along from BufferHub,
- // The user of ProducerBuffer is responsible with making sure that the
- // Dequeue() is done with the corect metadata type and size with those used
- // when the buffer is orignally created.
- template <typename Meta>
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, Meta* meta, pdx::LocalHandle* acquire_fence) {
- return Dequeue(timeout, slot, meta, sizeof(*meta), acquire_fence);
- }
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, pdx::LocalHandle* acquire_fence) {
- return Dequeue(timeout, slot, nullptr, 0, acquire_fence);
- }
-
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, void* meta, size_t user_metadata_size,
- pdx::LocalHandle* acquire_fence);
- pdx::Status<std::shared_ptr<ConsumerBuffer>> Dequeue(
- int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta,
- pdx::LocalHandle* acquire_fence);
-
- private:
- friend BufferHubQueue;
-
- explicit ConsumerQueue(pdx::LocalChannelHandle handle);
-
- // Add a consumer buffer to populate the queue. Once added, a consumer buffer
- // is NOT available to use until the producer side |Post| it. |WaitForBuffers|
- // will catch the |Post| and |Acquire| the buffer to make it available for
- // consumer.
- pdx::Status<void> AddBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- size_t slot);
-
- pdx::Status<void> OnBufferAllocated() override;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_CLIENT_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
deleted file mode 100644
index 36ab5f6..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Weverything"
-#endif
-
-// The following headers are included without checking every warning.
-// TODO(b/72172820): Remove the workaround once we have enforced -Weverything
-// in these headers and their dependencies.
-#include <pdx/channel_parcelable.h>
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-namespace android {
-namespace dvr {
-
-enum BufferHubQueueParcelableMagic : uint32_t {
- Producer = 0x62687170, // 'bhqp'
- Consumer = 0x62687163, // 'bhqc'
-};
-
-template <BufferHubQueueParcelableMagic Magic>
-class BufferHubQueueParcelable : public Parcelable {
- public:
- BufferHubQueueParcelable() = default;
-
- BufferHubQueueParcelable(BufferHubQueueParcelable&& other) noexcept = default;
- BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) noexcept {
- channel_parcelable_ = std::move(other.channel_parcelable_);
- return *this;
- }
-
- // Constructs an parcelable contains the channel parcelable.
- explicit BufferHubQueueParcelable(
- std::unique_ptr<pdx::ChannelParcelable> channel_parcelable)
- : channel_parcelable_(std::move(channel_parcelable)) {}
-
- BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete;
- void operator=(const BufferHubQueueParcelable&) = delete;
-
- bool IsValid() const;
-
- // Returns a channel handle constructed from this parcelable object and takes
- // the ownership of all resources from the parcelable object.
- pdx::LocalChannelHandle TakeChannelHandle();
-
- // Serializes the queue parcelable into the given parcel. Note that no system
- // resources are getting duplicated, nor did the parcel takes ownership of the
- // queue parcelable. Thus, the parcelable object must remain valid for the
- // lifetime of the parcel.
- status_t writeToParcel(Parcel* parcel) const override;
-
- // Deserialize the queue parcelable from the given parcel. Note that system
- // resources are duplicated from the parcel into the queue parcelable. Returns
- // error if the targeting parcelable object is already valid.
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_;
-};
-
-using ProducerQueueParcelable =
- BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>;
-using ConsumerQueueParcelable =
- BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>;
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
deleted file mode 100644
index 2f14f7c..0000000
--- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-#define ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
-
-#include <android-base/unique_fd.h>
-#include <log/log.h>
-#include <sys/epoll.h>
-
-namespace android {
-namespace dvr {
-
-class EpollFileDescriptor {
- public:
- static const int CTL_ADD = EPOLL_CTL_ADD;
- static const int CTL_MOD = EPOLL_CTL_MOD;
- static const int CTL_DEL = EPOLL_CTL_DEL;
-
- EpollFileDescriptor() : fd_(-1) {}
-
- // Constructs an EpollFileDescriptor from an integer file descriptor and
- // takes ownership.
- explicit EpollFileDescriptor(int fd) : fd_(fd) {}
-
- bool IsValid() const { return fd_.get() >= 0; }
-
- int Create() {
- if (IsValid()) {
- ALOGW("epoll fd has already been created.");
- return -EALREADY;
- }
-
- fd_.reset(epoll_create1(EPOLL_CLOEXEC));
-
- if (fd_.get() < 0)
- return -errno;
- else
- return 0;
- }
-
- int Control(int op, int target_fd, epoll_event* ev) {
- if (epoll_ctl(fd_.get(), op, target_fd, ev) < 0)
- return -errno;
- else
- return 0;
- }
-
- int Wait(epoll_event* events, int maxevents, int timeout) {
- int ret = epoll_wait(fd_.get(), events, maxevents, timeout);
-
- if (ret < 0)
- return -errno;
- else
- return ret;
- }
-
- int Get() const { return fd_.get(); }
-
- private:
- base::unique_fd fd_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_EPOLL_FILE_DESCRIPTOR_H_
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
deleted file mode 100644
index e373376..0000000
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-header_libraries = [
- "libdvr_headers",
-]
-
-shared_libraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "liblog",
- "libhardware",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
-]
-
-static_libraries = [
- "libchrome",
- "libdvrcommon",
- "libperformance",
-]
-
-cc_test {
- srcs: ["buffer_hub_queue-test.cpp"],
- header_libs: header_libraries,
- static_libs: static_libraries,
- shared_libs: shared_libraries,
- cflags: [
- "-DLOG_TAG=\"buffer_hub_queue-test\"",
- "-DTRACE=0",
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
- "-Wno-error=sign-compare", // to fix later
- ],
- name: "buffer_hub_queue-test",
-}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
deleted file mode 100644
index 6ae603b..0000000
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ /dev/null
@@ -1,1083 +0,0 @@
-#include <base/logging.h>
-#include <binder/Parcel.h>
-#include <dvr/dvr_api.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/producer_buffer.h>
-
-#include <gtest/gtest.h>
-#include <poll.h>
-#include <sys/eventfd.h>
-
-#include <vector>
-
-// Enable/disable debug logging.
-#define TRACE 0
-
-namespace android {
-namespace dvr {
-
-using pdx::LocalChannelHandle;
-using pdx::LocalHandle;
-
-namespace {
-
-constexpr uint32_t kBufferWidth = 100;
-constexpr uint32_t kBufferHeight = 1;
-constexpr uint32_t kBufferLayerCount = 1;
-constexpr uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
-constexpr uint64_t kBufferUsage = GRALLOC_USAGE_SW_READ_RARELY;
-constexpr int kTimeoutMs = 100;
-constexpr int kNoTimeout = 0;
-
-class BufferHubQueueTest : public ::testing::Test {
- public:
- bool CreateProducerQueue(const ProducerQueueConfig& config,
- const UsagePolicy& usage) {
- producer_queue_ = ProducerQueue::Create(config, usage);
- return producer_queue_ != nullptr;
- }
-
- bool CreateConsumerQueue() {
- if (producer_queue_) {
- consumer_queue_ = producer_queue_->CreateConsumerQueue();
- return consumer_queue_ != nullptr;
- } else {
- return false;
- }
- }
-
- bool CreateQueues(const ProducerQueueConfig& config,
- const UsagePolicy& usage) {
- return CreateProducerQueue(config, usage) && CreateConsumerQueue();
- }
-
- void AllocateBuffer(size_t* slot_out = nullptr) {
- // Create producer buffer.
- auto status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount,
- kBufferFormat, kBufferUsage);
-
- ASSERT_TRUE(status.ok());
- size_t slot = status.take();
- if (slot_out)
- *slot_out = slot;
- }
-
- bool WaitAndHandleOnce(BufferHubQueue* queue, int timeout_ms) {
- pollfd pfd{queue->queue_fd(), POLLIN, 0};
- int ret;
- do {
- ret = poll(&pfd, 1, timeout_ms);
- } while (ret == -1 && errno == EINTR);
-
- if (ret < 0) {
- ALOGW("Failed to poll queue %d's event fd, error: %s.", queue->id(),
- strerror(errno));
- return false;
- } else if (ret == 0) {
- return false;
- }
- return queue->HandleQueueEvents();
- }
-
- protected:
- ProducerQueueConfigBuilder config_builder_;
- std::unique_ptr<ProducerQueue> producer_queue_;
- std::unique_ptr<ConsumerQueue> consumer_queue_;
-};
-
-TEST_F(BufferHubQueueTest, TestDequeue) {
- const int64_t nb_dequeue_times = 16;
-
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate only one buffer.
- AllocateBuffer();
-
- // But dequeue multiple times.
- for (int64_t i = 0; i < nb_dequeue_times; i++) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer gains a buffer.
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Producer posts the buffer.
- mi.index = i;
- EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
-
- // Consumer acquires a buffer.
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
- EXPECT_EQ(mi.index, i);
- EXPECT_EQ(mo.index, i);
-
- // Consumer releases the buffer.
- EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
- }
-}
-
-TEST_F(BufferHubQueueTest,
- TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withConsumerBuffer) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate 3 buffers to use.
- const size_t test_queue_capacity = 3;
- for (int64_t i = 0; i < test_queue_capacity; i++) {
- AllocateBuffer();
- }
- EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
- size_t producer_slot, consumer_slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer posts 2 buffers and remember their posted sequence.
- std::deque<size_t> posted_slots;
- for (int64_t i = 0; i < 2; i++) {
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Producer should not be gaining posted buffer when there are still
- // available buffers to gain.
- auto found_iter =
- std::find(posted_slots.begin(), posted_slots.end(), producer_slot);
- EXPECT_EQ(found_iter, posted_slots.end());
- posted_slots.push_back(producer_slot);
-
- // Producer posts the buffer.
- mi.index = i;
- EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
- }
-
- // Consumer acquires one buffer.
- auto c1_status =
- consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
- // Consumer should get the oldest posted buffer. No checks here.
- // posted_slots[0] should be in acquired state now.
- EXPECT_EQ(mo.index, 0);
- // Consumer releases the buffer.
- EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0);
- // posted_slots[0] should be in released state now.
-
- // Producer gain and post 2 buffers.
- for (int64_t i = 0; i < 2; i++) {
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // The gained buffer should be the one in released state or the one haven't
- // been use.
- EXPECT_NE(posted_slots[1], producer_slot);
-
- mi.index = i + 2;
- EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
- }
-
- // Producer gains a buffer.
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // The gained buffer should be the oldest posted buffer.
- EXPECT_EQ(posted_slots[1], producer_slot);
-
- // Producer posts the buffer.
- mi.index = 4;
- EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle()));
-}
-
-TEST_F(BufferHubQueueTest,
- TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noConsumerBuffer) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate 4 buffers to use.
- const size_t test_queue_capacity = 4;
- for (int64_t i = 0; i < test_queue_capacity; i++) {
- AllocateBuffer();
- }
- EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity);
-
- // Post all allowed buffers and remember their posted sequence.
- std::deque<size_t> posted_slots;
- for (int64_t i = 0; i < test_queue_capacity; i++) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer gains a buffer.
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Producer should not be gaining posted buffer when there are still
- // available buffers to gain.
- auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot);
- EXPECT_EQ(found_iter, posted_slots.end());
- posted_slots.push_back(slot);
-
- // Producer posts the buffer.
- mi.index = i;
- EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
- }
-
- // Gain posted buffers in sequence.
- const int64_t nb_dequeue_all_times = 2;
- for (int j = 0; j < nb_dequeue_all_times; ++j) {
- for (int i = 0; i < test_queue_capacity; ++i) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- // Producer gains a buffer.
- auto p1_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // The gained buffer should be the oldest posted buffer.
- EXPECT_EQ(posted_slots[i], slot);
-
- // Producer posts the buffer.
- mi.index = i + test_queue_capacity * (j + 1);
- EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0);
- }
- }
-}
-
-TEST_F(BufferHubQueueTest, TestProducerConsumer) {
- const size_t kBufferCount = 16;
- size_t slot;
- DvrNativeBufferMetadata mi, mo;
- LocalHandle fence;
-
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- for (size_t i = 0; i < kBufferCount; i++) {
- AllocateBuffer();
-
- // Producer queue has all the available buffers on initialize.
- ASSERT_EQ(producer_queue_->count(), i + 1);
- ASSERT_EQ(producer_queue_->capacity(), i + 1);
-
- // Consumer queue has no avaiable buffer on initialize.
- ASSERT_EQ(consumer_queue_->count(), 0U);
- // Consumer queue does not import buffers until a dequeue is issued.
- ASSERT_EQ(consumer_queue_->capacity(), i);
- // Dequeue returns timeout since no buffer is ready to consumer, but
- // this implicitly triggers buffer import and bump up |capacity|.
- auto status = consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
- ASSERT_FALSE(status.ok());
- ASSERT_EQ(ETIMEDOUT, status.error());
- ASSERT_EQ(consumer_queue_->capacity(), i + 1);
- }
-
- // Use eventfd as a stand-in for a fence.
- LocalHandle post_fence(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-
- for (size_t i = 0; i < kBufferCount; i++) {
- // First time there is no buffer available to dequeue.
- auto consumer_status =
- consumer_queue_->Dequeue(kNoTimeout, &slot, &mo, &fence);
- ASSERT_FALSE(consumer_status.ok());
- ASSERT_EQ(consumer_status.error(), ETIMEDOUT);
-
- // Make sure Producer buffer is POSTED so that it's ready to Accquire
- // in the consumer's Dequeue() function.
- auto producer_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- auto producer = producer_status.take();
- ASSERT_NE(nullptr, producer);
-
- mi.index = static_cast<int64_t>(i);
- ASSERT_EQ(producer->PostAsync(&mi, post_fence), 0);
-
- // Second time the just the POSTED buffer should be dequeued.
- consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok());
- EXPECT_TRUE(fence.IsValid());
-
- auto consumer = consumer_status.take();
- ASSERT_NE(nullptr, consumer);
- ASSERT_EQ(mi.index, mo.index);
- }
-}
-
-TEST_F(BufferHubQueueTest, TestInsertBuffer) {
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
- consumer_queue_ = producer_queue_->CreateConsumerQueue();
- ASSERT_TRUE(consumer_queue_ != nullptr);
- EXPECT_EQ(producer_queue_->capacity(), 0);
- EXPECT_EQ(consumer_queue_->capacity(), 0);
-
- std::shared_ptr<ProducerBuffer> p1 = ProducerBuffer::Create(
- kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
- ASSERT_TRUE(p1 != nullptr);
- ASSERT_EQ(p1->GainAsync(), 0);
-
- // Inserting a posted buffer will fail.
- DvrNativeBufferMetadata meta;
- EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0);
- auto status_or_slot = producer_queue_->InsertBuffer(p1);
- EXPECT_FALSE(status_or_slot.ok());
- EXPECT_EQ(status_or_slot.error(), EINVAL);
-
- // Inserting a gained buffer will succeed.
- std::shared_ptr<ProducerBuffer> p2 = ProducerBuffer::Create(
- kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
- ASSERT_EQ(p2->GainAsync(), 0);
- ASSERT_TRUE(p2 != nullptr);
- status_or_slot = producer_queue_->InsertBuffer(p2);
- EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
- // This is the first buffer inserted, should take slot 0.
- size_t slot = status_or_slot.get();
- EXPECT_EQ(slot, 0);
-
- // Wait and expect the consumer to kick up the newly inserted buffer.
- WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
- EXPECT_EQ(consumer_queue_->capacity(), 1ULL);
-}
-
-TEST_F(BufferHubQueueTest, TestRemoveBuffer) {
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
- DvrNativeBufferMetadata mo;
-
- // Allocate buffers.
- const size_t kBufferCount = 4u;
- for (size_t i = 0; i < kBufferCount; i++) {
- AllocateBuffer();
- }
- ASSERT_EQ(kBufferCount, producer_queue_->count());
- ASSERT_EQ(kBufferCount, producer_queue_->capacity());
-
- consumer_queue_ = producer_queue_->CreateConsumerQueue();
- ASSERT_NE(nullptr, consumer_queue_);
-
- // Check that buffers are correctly imported on construction.
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_EQ(0u, consumer_queue_->count());
-
- // Dequeue all the buffers and keep track of them in an array. This prevents
- // the producer queue ring buffer ref counts from interfering with the tests.
- struct Entry {
- std::shared_ptr<ProducerBuffer> buffer;
- LocalHandle fence;
- size_t slot;
- };
- std::array<Entry, kBufferCount> buffers;
-
- for (size_t i = 0; i < kBufferCount; i++) {
- Entry* entry = &buffers[i];
- auto producer_status =
- producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence);
- ASSERT_TRUE(producer_status.ok());
- entry->buffer = producer_status.take();
- ASSERT_NE(nullptr, entry->buffer);
- }
-
- // Remove a buffer and make sure both queues reflect the change.
- ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[0].slot));
- EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
- // As long as the removed buffer is still alive the consumer queue won't know
- // its gone.
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Release the removed buffer.
- buffers[0].buffer = nullptr;
-
- // Now the consumer queue should know it's gone.
- EXPECT_FALSE(WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs));
- ASSERT_EQ(kBufferCount - 1, consumer_queue_->capacity());
-
- // Allocate a new buffer. This should take the first empty slot.
- size_t slot;
- AllocateBuffer(&slot);
- ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
- EXPECT_EQ(buffers[0].slot, slot);
- EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
- // The consumer queue should pick up the new buffer.
- EXPECT_EQ(kBufferCount - 1, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Remove and allocate a buffer.
- ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[1].slot));
- EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
- buffers[1].buffer = nullptr;
-
- AllocateBuffer(&slot);
- ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
- EXPECT_EQ(buffers[1].slot, slot);
- EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
- // The consumer queue should pick up the new buffer but the count shouldn't
- // change.
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Remove and allocate a buffer, but don't free the buffer right away.
- ASSERT_TRUE(producer_queue_->RemoveBuffer(buffers[2].slot));
- EXPECT_EQ(kBufferCount - 1, producer_queue_->capacity());
-
- AllocateBuffer(&slot);
- ALOGE_IF(TRACE, "ALLOCATE %zu", slot);
- EXPECT_EQ(buffers[2].slot, slot);
- EXPECT_EQ(kBufferCount, producer_queue_->capacity());
-
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-
- // Release the producer buffer to trigger a POLLHUP event for an already
- // removed buffer.
- buffers[2].buffer = nullptr;
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
- EXPECT_FALSE(consumer_queue_->HandleQueueEvents());
- EXPECT_EQ(kBufferCount, consumer_queue_->capacity());
-}
-
-TEST_F(BufferHubQueueTest, TestMultipleConsumers) {
- // ProducerConfigureBuilder doesn't set Metadata{size}, which means there
- // is no metadata associated with this BufferQueue's buffer.
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
- // Allocate buffers.
- const size_t kBufferCount = 4u;
- for (size_t i = 0; i < kBufferCount; i++) {
- AllocateBuffer();
- }
- ASSERT_EQ(kBufferCount, producer_queue_->count());
-
- // Build a silent consumer queue to test multi-consumer queue features.
- auto silent_queue = producer_queue_->CreateSilentConsumerQueue();
- ASSERT_NE(nullptr, silent_queue);
-
- // Check that silent queue doesn't import buffers on creation.
- EXPECT_EQ(silent_queue->capacity(), 0U);
-
- // Dequeue and post a buffer.
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
- auto producer_status =
- producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(producer_status.ok());
- auto producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
- // After post, check the number of remaining available buffers.
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
- // Currently we expect no buffer to be available prior to calling
- // WaitForBuffers/HandleQueueEvents.
- // TODO(eieio): Note this behavior may change in the future.
- EXPECT_EQ(silent_queue->count(), 0U);
- EXPECT_FALSE(silent_queue->HandleQueueEvents());
- EXPECT_EQ(silent_queue->count(), 0U);
-
- // Build a new consumer queue to test multi-consumer queue features.
- consumer_queue_ = silent_queue->CreateConsumerQueue();
- ASSERT_NE(consumer_queue_, nullptr);
-
- // Check that buffers are correctly imported on construction.
- EXPECT_EQ(consumer_queue_->capacity(), kBufferCount);
- // Buffers are only imported, but their availability is not checked until
- // first call to Dequeue().
- EXPECT_EQ(consumer_queue_->count(), 0U);
-
- // Reclaim released/ignored buffers.
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
- usleep(10000);
- WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-
- // Post another buffer.
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- EXPECT_EQ(producer_buffer->PostAsync(&mi, {}), 0);
-
- // Verify that the consumer queue receives it.
- size_t consumer_queue_count = consumer_queue_->count();
- WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs);
- EXPECT_GT(consumer_queue_->count(), consumer_queue_count);
-
- // Save the current consumer queue buffer count to compare after the dequeue.
- consumer_queue_count = consumer_queue_->count();
-
- // Dequeue and acquire/release (discard) buffers on the consumer end.
- auto consumer_status =
- consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(consumer_status.ok());
- auto consumer_buffer = consumer_status.take();
- ASSERT_NE(consumer_buffer, nullptr);
- consumer_buffer->Discard();
-
- // Buffer should be returned to the producer queue without being handled by
- // the silent consumer queue.
- EXPECT_LT(consumer_queue_->count(), consumer_queue_count);
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 2);
-
- WaitAndHandleOnce(producer_queue_.get(), kTimeoutMs);
- EXPECT_EQ(producer_queue_->count(), kBufferCount - 1);
-}
-
-struct TestUserMetadata {
- char a;
- int32_t b;
- int64_t c;
-};
-
-constexpr uint64_t kUserMetadataSize =
- static_cast<uint64_t>(sizeof(TestUserMetadata));
-
-TEST_F(BufferHubQueueTest, TestUserMetadata) {
- ASSERT_TRUE(CreateQueues(
- config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
- AllocateBuffer();
-
- std::vector<TestUserMetadata> user_metadata_list = {
- {'0', 0, 0}, {'1', 10, 3333}, {'@', 123, 1000000000}};
-
- for (auto user_metadata : user_metadata_list) {
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
-
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // TODO(b/69469185): Test against metadata from consumer once we implement
- // release metadata properly.
- // EXPECT_EQ(mo.user_metadata_ptr, 0U);
- // EXPECT_EQ(mo.user_metadata_size, 0U);
-
- mi.user_metadata_size = kUserMetadataSize;
- mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
- EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
-
- EXPECT_EQ(mo.user_metadata_size, kUserMetadataSize);
- auto out_user_metadata =
- reinterpret_cast<TestUserMetadata*>(mo.user_metadata_ptr);
- EXPECT_EQ(user_metadata.a, out_user_metadata->a);
- EXPECT_EQ(user_metadata.b, out_user_metadata->b);
- EXPECT_EQ(user_metadata.c, out_user_metadata->c);
-
- // When release, empty metadata is also legit.
- mi.user_metadata_size = 0U;
- mi.user_metadata_ptr = 0U;
- c1->ReleaseAsync(&mi, {});
- }
-}
-
-TEST_F(BufferHubQueueTest, TestUserMetadataMismatch) {
- ASSERT_TRUE(CreateQueues(
- config_builder_.SetMetadata<TestUserMetadata>().Build(), UsagePolicy{}));
-
- AllocateBuffer();
-
- TestUserMetadata user_metadata;
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // Post with mismatched user metadata size will fail. But the producer buffer
- // itself should stay untouched.
- mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
- mi.user_metadata_size = kUserMetadataSize + 1;
- EXPECT_EQ(p1->PostAsync(&mi, {}), -E2BIG);
- // Post with the exact same user metdata size can success.
- mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
- mi.user_metadata_size = kUserMetadataSize;
- EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestEnqueue) {
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
- UsagePolicy{}));
- AllocateBuffer();
-
- size_t slot;
- LocalHandle fence;
- DvrNativeBufferMetadata mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(nullptr, p1);
-
- producer_queue_->Enqueue(p1, slot, 0ULL);
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_FALSE(c1_status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateBuffer) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- size_t ps1;
- AllocateBuffer();
- LocalHandle fence;
- DvrNativeBufferMetadata mi, mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &ps1, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_NE(p1, nullptr);
-
- // producer queue is exhausted
- size_t ps2;
- auto p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
- ASSERT_FALSE(p2_status.ok());
- ASSERT_EQ(ETIMEDOUT, p2_status.error());
-
- // dynamically add buffer.
- AllocateBuffer();
- ASSERT_EQ(producer_queue_->count(), 1U);
- ASSERT_EQ(producer_queue_->capacity(), 2U);
-
- // now we can dequeue again
- p2_status = producer_queue_->Dequeue(kTimeoutMs, &ps2, &mo, &fence);
- ASSERT_TRUE(p2_status.ok());
- auto p2 = p2_status.take();
- ASSERT_NE(p2, nullptr);
- ASSERT_EQ(producer_queue_->count(), 0U);
- // p1 and p2 should have different slot number
- ASSERT_NE(ps1, ps2);
-
- // Consumer queue does not import buffers until |Dequeue| or |ImportBuffers|
- // are called. So far consumer_queue_ should be empty.
- ASSERT_EQ(consumer_queue_->count(), 0U);
-
- int64_t seq = 1;
- mi.index = seq;
- ASSERT_EQ(p1->PostAsync(&mi, {}), 0);
-
- size_t cs1, cs2;
- auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
- ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
- auto c1 = c1_status.take();
- ASSERT_NE(c1, nullptr);
- ASSERT_EQ(consumer_queue_->count(), 0U);
- ASSERT_EQ(consumer_queue_->capacity(), 2U);
- ASSERT_EQ(cs1, ps1);
-
- ASSERT_EQ(p2->PostAsync(&mi, {}), 0);
- auto c2_status = consumer_queue_->Dequeue(kTimeoutMs, &cs2, &mo, &fence);
- ASSERT_TRUE(c2_status.ok());
- auto c2 = c2_status.take();
- ASSERT_NE(c2, nullptr);
- ASSERT_EQ(cs2, ps2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
- ASSERT_EQ(producer_queue_->capacity(), 0);
- auto status = producer_queue_->AllocateBuffers(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage, /*buffer_count=*/2);
- ASSERT_TRUE(status.ok());
- std::vector<size_t> buffer_slots = status.take();
- ASSERT_EQ(buffer_slots.size(), 2);
- ASSERT_EQ(producer_queue_->capacity(), 2);
-}
-
-TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
- ASSERT_EQ(producer_queue_->capacity(), 0);
- auto status = producer_queue_->AllocateBuffers(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage, /*buffer_count=*/0);
- ASSERT_TRUE(status.ok());
- std::vector<size_t> buffer_slots = status.take();
- ASSERT_EQ(buffer_slots.size(), 0);
- ASSERT_EQ(producer_queue_->capacity(), 0);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageSetMask) {
- const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(
- CreateQueues(config_builder_.Build(), UsagePolicy{set_mask, 0, 0, 0}));
-
- // When allocation, leave out |set_mask| from usage bits on purpose.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage & ~set_mask);
- ASSERT_TRUE(status.ok());
-
- LocalHandle fence;
- size_t slot;
- DvrNativeBufferMetadata mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_EQ(p1->usage() & set_mask, set_mask);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageClearMask) {
- const uint32_t clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(
- CreateQueues(config_builder_.Build(), UsagePolicy{0, clear_mask, 0, 0}));
-
- // When allocation, add |clear_mask| into usage bits on purpose.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage | clear_mask);
- ASSERT_TRUE(status.ok());
-
- LocalHandle fence;
- size_t slot;
- DvrNativeBufferMetadata mo;
- auto p1_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(p1_status.ok());
- auto p1 = p1_status.take();
- ASSERT_EQ(p1->usage() & clear_mask, 0U);
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenySetMask) {
- const uint32_t deny_set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
- UsagePolicy{0, 0, deny_set_mask, 0}));
-
- // Now that |deny_set_mask| is illegal, allocation without those bits should
- // be able to succeed.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage & ~deny_set_mask);
- ASSERT_TRUE(status.ok());
-
- // While allocation with those bits should fail.
- status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount, kBufferFormat,
- kBufferUsage | deny_set_mask);
- ASSERT_FALSE(status.ok());
- ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestUsageDenyClearMask) {
- const uint32_t deny_clear_mask = GRALLOC_USAGE_SW_WRITE_OFTEN;
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<int64_t>().Build(),
- UsagePolicy{0, 0, 0, deny_clear_mask}));
-
- // Now that clearing |deny_clear_mask| is illegal (i.e. setting these bits are
- // mandatory), allocation with those bits should be able to succeed.
- auto status = producer_queue_->AllocateBuffer(
- kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat,
- kBufferUsage | deny_clear_mask);
- ASSERT_TRUE(status.ok());
-
- // While allocation without those bits should fail.
- status = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount, kBufferFormat,
- kBufferUsage & ~deny_clear_mask);
- ASSERT_FALSE(status.ok());
- ASSERT_EQ(EINVAL, status.error());
-}
-
-TEST_F(BufferHubQueueTest, TestQueueInfo) {
- static const bool kIsAsync = true;
- ASSERT_TRUE(CreateQueues(config_builder_.SetIsAsync(kIsAsync)
- .SetDefaultWidth(kBufferWidth)
- .SetDefaultHeight(kBufferHeight)
- .SetDefaultFormat(kBufferFormat)
- .Build(),
- UsagePolicy{}));
-
- EXPECT_EQ(producer_queue_->default_width(), kBufferWidth);
- EXPECT_EQ(producer_queue_->default_height(), kBufferHeight);
- EXPECT_EQ(producer_queue_->default_format(), kBufferFormat);
- EXPECT_EQ(producer_queue_->is_async(), kIsAsync);
-
- EXPECT_EQ(consumer_queue_->default_width(), kBufferWidth);
- EXPECT_EQ(consumer_queue_->default_height(), kBufferHeight);
- EXPECT_EQ(consumer_queue_->default_format(), kBufferFormat);
- EXPECT_EQ(consumer_queue_->is_async(), kIsAsync);
-}
-
-TEST_F(BufferHubQueueTest, TestFreeAllBuffers) {
- constexpr size_t kBufferCount = 2;
-
-#define CHECK_NO_BUFFER_THEN_ALLOCATE(num_buffers) \
- EXPECT_EQ(consumer_queue_->count(), 0U); \
- EXPECT_EQ(consumer_queue_->capacity(), 0U); \
- EXPECT_EQ(producer_queue_->count(), 0U); \
- EXPECT_EQ(producer_queue_->capacity(), 0U); \
- for (size_t i = 0; i < num_buffers; i++) { \
- AllocateBuffer(); \
- } \
- EXPECT_EQ(producer_queue_->count(), num_buffers); \
- EXPECT_EQ(producer_queue_->capacity(), num_buffers);
-
- size_t slot;
- LocalHandle fence;
- pdx::Status<void> status;
- pdx::Status<std::shared_ptr<ConsumerBuffer>> consumer_status;
- pdx::Status<std::shared_ptr<ProducerBuffer>> producer_status;
- std::shared_ptr<ConsumerBuffer> consumer_buffer;
- std::shared_ptr<ProducerBuffer> producer_buffer;
- DvrNativeBufferMetadata mi, mo;
-
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- // Free all buffers when buffers are avaible for dequeue.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when one buffer is dequeued.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when all buffers are dequeued.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- for (size_t i = 0; i < kBufferCount; i++) {
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- }
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when one buffer is posted.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(nullptr, producer_buffer);
- ASSERT_EQ(0, producer_buffer->PostAsync(&mi, fence));
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when all buffers are posted.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- for (size_t i = 0; i < kBufferCount; i++) {
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
- }
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // Free all buffers when all buffers are acquired.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
- for (size_t i = 0; i < kBufferCount; i++) {
- producer_status = producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(producer_status.ok());
- producer_buffer = producer_status.take();
- ASSERT_NE(producer_buffer, nullptr);
- ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
- consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
- }
-
- status = producer_queue_->FreeAllBuffers();
- EXPECT_TRUE(status.ok());
-
- // In addition to FreeAllBuffers() from the queue, it is also required to
- // delete all references to the ProducerBuffer (i.e. the PDX client).
- producer_buffer = nullptr;
-
- // Crank consumer queue events to pickup EPOLLHUP events on the queue.
- consumer_queue_->HandleQueueEvents();
-
- // One last check.
- CHECK_NO_BUFFER_THEN_ALLOCATE(kBufferCount);
-
-#undef CHECK_NO_BUFFER_THEN_ALLOCATE
-}
-
-TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) {
- ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(),
- UsagePolicy{}));
-
- // Allocate only one buffer.
- AllocateBuffer();
-
- // Export should fail as the queue is not empty.
- auto status = producer_queue_->TakeAsParcelable();
- EXPECT_FALSE(status.ok());
-}
-
-TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) {
- ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
-
- auto s1 = producer_queue_->TakeAsParcelable();
- EXPECT_TRUE(s1.ok());
-
- ProducerQueueParcelable output_parcelable = s1.take();
- EXPECT_TRUE(output_parcelable.IsValid());
-
- Parcel parcel;
- status_t res;
- res = output_parcelable.writeToParcel(&parcel);
- EXPECT_EQ(res, OK);
-
- // After written into parcelable, the output_parcelable is still valid has
- // keeps the producer channel alive.
- EXPECT_TRUE(output_parcelable.IsValid());
-
- // Creating producer buffer should fail.
- auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
- kBufferLayerCount, kBufferFormat,
- kBufferUsage);
- ASSERT_FALSE(s2.ok());
-
- // Reset the data position so that we can read back from the same parcel
- // without doing actually Binder IPC.
- parcel.setDataPosition(0);
- producer_queue_ = nullptr;
-
- // Recreate the producer queue from the parcel.
- ProducerQueueParcelable input_parcelable;
- EXPECT_FALSE(input_parcelable.IsValid());
-
- res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, OK);
- EXPECT_TRUE(input_parcelable.IsValid());
-
- EXPECT_EQ(producer_queue_, nullptr);
- producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle());
- EXPECT_FALSE(input_parcelable.IsValid());
- ASSERT_NE(producer_queue_, nullptr);
-
- // Newly created queue from the parcel can allocate buffer, post buffer to
- // consumer.
- EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
- EXPECT_EQ(producer_queue_->count(), 1U);
- EXPECT_EQ(producer_queue_->capacity(), 1U);
-
- size_t slot;
- DvrNativeBufferMetadata producer_meta;
- DvrNativeBufferMetadata consumer_meta;
- LocalHandle fence;
- auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
- EXPECT_TRUE(s3.ok());
-
- std::shared_ptr<ProducerBuffer> p1 = s3.take();
- ASSERT_NE(p1, nullptr);
-
- producer_meta.timestamp = 42;
- EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
- // Make sure the buffer can be dequeued from consumer side.
- auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
- EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
- auto consumer = s4.take();
- ASSERT_NE(consumer, nullptr);
- EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) {
- ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
-
- auto s1 = producer_queue_->CreateConsumerQueueParcelable();
- EXPECT_TRUE(s1.ok());
- ConsumerQueueParcelable output_parcelable = s1.take();
- EXPECT_TRUE(output_parcelable.IsValid());
-
- // Write to a Parcel new object.
- Parcel parcel;
- status_t res;
- res = output_parcelable.writeToParcel(&parcel);
-
- // Reset the data position so that we can read back from the same parcel
- // without doing actually Binder IPC.
- parcel.setDataPosition(0);
-
- // No consumer queue created yet.
- EXPECT_EQ(consumer_queue_, nullptr);
-
- // If the parcel contains a consumer queue, read into a
- // ProducerQueueParcelable should fail.
- ProducerQueueParcelable wrongly_typed_parcelable;
- EXPECT_FALSE(wrongly_typed_parcelable.IsValid());
- res = wrongly_typed_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, -EINVAL);
- parcel.setDataPosition(0);
-
- // Create the consumer queue from the parcel.
- ConsumerQueueParcelable input_parcelable;
- EXPECT_FALSE(input_parcelable.IsValid());
-
- res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, OK);
- EXPECT_TRUE(input_parcelable.IsValid());
-
- consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
- EXPECT_FALSE(input_parcelable.IsValid());
- ASSERT_NE(consumer_queue_, nullptr);
-
- EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
- EXPECT_EQ(producer_queue_->count(), 1U);
- EXPECT_EQ(producer_queue_->capacity(), 1U);
-
- size_t slot;
- DvrNativeBufferMetadata producer_meta;
- DvrNativeBufferMetadata consumer_meta;
- LocalHandle fence;
- auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
- EXPECT_TRUE(s2.ok());
-
- std::shared_ptr<ProducerBuffer> p1 = s2.take();
- ASSERT_NE(p1, nullptr);
-
- producer_meta.timestamp = 42;
- EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
-
- // Make sure the buffer can be dequeued from consumer side.
- auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
- EXPECT_EQ(consumer_queue_->capacity(), 1U);
-
- auto consumer = s3.take();
- ASSERT_NE(consumer, nullptr);
- EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
-}
-
-} // namespace
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
deleted file mode 100644
index b0ed950..0000000
--- a/libs/vr/libdisplay/Android.bp
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2015 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
- "display_client.cpp",
- "display_manager_client.cpp",
- "display_protocol.cpp",
- "shared_buffer_helpers.cpp",
- "vsync_service.cpp",
-]
-
-localIncludeFiles = [
- "include",
-]
-
-sharedLibraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "liblog",
- "libutils",
- "libui",
- "libgui",
- "libhardware",
- "libsync",
- "libnativewindow",
- "libpdx_default_transport",
-]
-
-staticLibraries = [
- "libdvrcommon",
- "libbroadcastring",
-]
-
-headerLibraries = [
- "vulkan_headers",
- "libdvr_headers",
-]
-
-cc_library {
- srcs: sourceFiles,
- cflags: ["-DLOG_TAG=\"libdisplay\"",
- "-DTRACE=0",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-DGL_GLEXT_PROTOTYPES",
- "-DEGL_EGLEXT_PROTOTYPES",
- "-Wall",
- "-Werror",
- ], // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
- export_include_dirs: localIncludeFiles,
- shared_libs: sharedLibraries,
- static_libs: staticLibraries,
- header_libs: headerLibraries,
- export_header_lib_headers: headerLibraries,
-
- name: "libdisplay",
-}
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
deleted file mode 100644
index 62856df..0000000
--- a/libs/vr/libdisplay/display_client.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-#include "include/private/dvr/display_client.h"
-
-#include <cutils/native_handle.h>
-#include <log/log.h>
-#include <pdx/default_transport/client_channel.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/status.h>
-
-#include <mutex>
-
-#include <private/dvr/display_protocol.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-Surface::Surface(LocalChannelHandle channel_handle, int* error)
- : BASE{pdx::default_transport::ClientChannel::Create(
- std::move(channel_handle))} {
- auto status = InvokeRemoteMethod<DisplayProtocol::GetSurfaceInfo>();
- if (!status) {
- ALOGE("Surface::Surface: Failed to get surface info: %s",
- status.GetErrorMessage().c_str());
- Close(status.error());
- if (error)
- *error = status.error();
- }
-
- surface_id_ = status.get().surface_id;
- z_order_ = status.get().z_order;
- visible_ = status.get().visible;
-}
-
-Surface::Surface(const SurfaceAttributes& attributes, int* error)
- : BASE{pdx::default_transport::ClientChannelFactory::Create(
- DisplayProtocol::kClientPath),
- kInfiniteTimeout} {
- auto status = InvokeRemoteMethod<DisplayProtocol::CreateSurface>(attributes);
- if (!status) {
- ALOGE("Surface::Surface: Failed to create display surface: %s",
- status.GetErrorMessage().c_str());
- Close(status.error());
- if (error)
- *error = status.error();
- }
-
- surface_id_ = status.get().surface_id;
- z_order_ = status.get().z_order;
- visible_ = status.get().visible;
-}
-
-Status<void> Surface::SetVisible(bool visible) {
- return SetAttributes(
- {{SurfaceAttribute::Visible, SurfaceAttributeValue{visible}}});
-}
-
-Status<void> Surface::SetZOrder(int z_order) {
- return SetAttributes(
- {{SurfaceAttribute::ZOrder, SurfaceAttributeValue{z_order}}});
-}
-
-Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
- auto status = InvokeRemoteMethod<DisplayProtocol::SetAttributes>(attributes);
- if (!status) {
- ALOGE(
- "Surface::SetAttributes: Failed to set display surface "
- "attributes: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- // Set the local cached copies of the attributes we care about from the full
- // set of attributes sent to the display service.
- for (const auto& attribute : attributes) {
- const auto& key = attribute.first;
- const auto* variant = &attribute.second;
- bool invalid_value = false;
- switch (key) {
- case SurfaceAttribute::Visible:
- invalid_value =
- !IfAnyOf<int32_t, int64_t, bool>::Get(variant, &visible_);
- break;
- case SurfaceAttribute::ZOrder:
- invalid_value = !IfAnyOf<int32_t>::Get(variant, &z_order_);
- break;
- }
-
- if (invalid_value) {
- ALOGW(
- "Surface::SetAttributes: Failed to set display surface "
- "attribute %d because of incompatible type: %d",
- key, variant->index());
- }
- }
-
- return {};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
- uint32_t width, uint32_t height, uint32_t format, size_t metadata_size) {
- ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
- auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(
- ProducerQueueConfigBuilder()
- .SetDefaultWidth(width)
- .SetDefaultHeight(height)
- .SetDefaultFormat(format)
- .SetMetadataSize(metadata_size)
- .Build());
- if (!status) {
- ALOGE("Surface::CreateQueue: Failed to create queue: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto producer_queue = ProducerQueue::Import(status.take());
- if (!producer_queue) {
- ALOGE("Surface::CreateQueue: Failed to import producer queue!");
- return ErrorStatus(ENOMEM);
- }
-
- return {std::move(producer_queue)};
-}
-
-Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
- uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
- uint64_t usage, size_t capacity, size_t metadata_size) {
- ALOGD_IF(TRACE,
- "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
- "usage=%" PRIx64 " capacity=%zu",
- width, height, layer_count, format, usage, capacity);
- auto status = CreateQueue(width, height, format, metadata_size);
- if (!status)
- return status.error_status();
-
- auto producer_queue = status.take();
-
- ALOGD_IF(TRACE, "Surface::CreateQueue: Allocating %zu buffers...", capacity);
- auto allocate_status = producer_queue->AllocateBuffers(
- width, height, layer_count, format, usage, capacity);
- if (!allocate_status) {
- ALOGE("Surface::CreateQueue: Failed to allocate buffer on queue_id=%d: %s",
- producer_queue->id(), allocate_status.GetErrorMessage().c_str());
- return allocate_status.error_status();
- }
-
- return {std::move(producer_queue)};
-}
-
-DisplayClient::DisplayClient(int* error)
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- DisplayProtocol::kClientPath),
- kInfiniteTimeout) {
- if (error)
- *error = Client::error();
-}
-
-Status<Metrics> DisplayClient::GetDisplayMetrics() {
- return InvokeRemoteMethod<DisplayProtocol::GetMetrics>();
-}
-
-Status<std::string> DisplayClient::GetConfigurationData(
- ConfigFileType config_type) {
- auto status =
- InvokeRemoteMethod<DisplayProtocol::GetConfigurationData>(config_type);
- if (!status && status.error() != ENOENT) {
- ALOGE(
- "DisplayClient::GetConfigurationData: Unable to get"
- "configuration data. Error: %s",
- status.GetErrorMessage().c_str());
- }
- return status;
-}
-
-Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() {
- return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>();
-}
-
-Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
- const SurfaceAttributes& attributes) {
- int error;
- if (auto client = Surface::Create(attributes, &error))
- return {std::move(client)};
- else
- return ErrorStatus(error);
-}
-
-pdx::Status<std::unique_ptr<IonBuffer>> DisplayClient::SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage) {
- auto status =
- InvokeRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(key, size, usage);
- if (!status) {
- ALOGE(
- "DisplayClient::SetupGlobalBuffer: Failed to create the global buffer "
- "%s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto ion_buffer = std::make_unique<IonBuffer>();
- auto native_buffer_handle = status.take();
- const int ret = native_buffer_handle.Import(ion_buffer.get());
- if (ret < 0) {
- ALOGE(
- "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
- "key=%d; error=%s",
- key, strerror(-ret));
- return ErrorStatus(-ret);
- }
-
- return {std::move(ion_buffer)};
-}
-
-pdx::Status<void> DisplayClient::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
- auto status = InvokeRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(key);
- if (!status) {
- ALOGE("DisplayClient::DeleteGlobalBuffer Failed: %s",
- status.GetErrorMessage().c_str());
- }
-
- return status;
-}
-
-Status<std::unique_ptr<IonBuffer>> DisplayClient::GetGlobalBuffer(
- DvrGlobalBufferKey key) {
- auto status = InvokeRemoteMethod<DisplayProtocol::GetGlobalBuffer>(key);
- if (!status) {
- ALOGE(
- "DisplayClient::GetGlobalBuffer: Failed to get named buffer: key=%d; "
- "error=%s",
- key, status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- auto ion_buffer = std::make_unique<IonBuffer>();
- auto native_buffer_handle = status.take();
- const int ret = native_buffer_handle.Import(ion_buffer.get());
- if (ret < 0) {
- ALOGE(
- "DisplayClient::GetGlobalBuffer: Failed to import global buffer: "
- "key=%d; error=%s",
- key, strerror(-ret));
- return ErrorStatus(-ret);
- }
-
- return {std::move(ion_buffer)};
-}
-
-Status<bool> DisplayClient::IsVrAppRunning() {
- return InvokeRemoteMethod<DisplayProtocol::IsVrAppRunning>();
-}
-
-} // namespace display
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp
deleted file mode 100644
index fdeeb70..0000000
--- a/libs/vr/libdisplay/display_manager_client.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "include/private/dvr/display_manager_client.h"
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <utils/Log.h>
-
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace display {
-
-DisplayManagerClient::DisplayManagerClient()
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- DisplayManagerProtocol::kClientPath)) {}
-
-DisplayManagerClient::~DisplayManagerClient() {}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerClient::GetSurfaceState() {
- auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceState>();
- if (!status) {
- ALOGE(
- "DisplayManagerClient::GetSurfaceState: Failed to get surface info: %s",
- status.GetErrorMessage().c_str());
- }
-
- return status;
-}
-
-pdx::Status<std::unique_ptr<ConsumerQueue>>
-DisplayManagerClient::GetSurfaceQueue(int surface_id, int queue_id) {
- auto status = InvokeRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
- surface_id, queue_id);
- if (!status) {
- ALOGE(
- "DisplayManagerClient::GetSurfaceQueue: Failed to get queue for "
- "surface_id=%d queue_id=%d: %s",
- surface_id, queue_id, status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return {ConsumerQueue::Import(status.take())};
-}
-
-} // namespace display
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/display_protocol.cpp b/libs/vr/libdisplay/display_protocol.cpp
deleted file mode 100644
index 773f9a5..0000000
--- a/libs/vr/libdisplay/display_protocol.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "include/private/dvr/display_protocol.h"
-
-namespace android {
-namespace dvr {
-namespace display {
-
-constexpr char DisplayProtocol::kClientPath[];
-constexpr char DisplayManagerProtocol::kClientPath[];
-constexpr char VSyncProtocol::kClientPath[];
-
-} // namespace display
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/include/CPPLINT.cfg b/libs/vr/libdisplay/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
deleted file mode 100644
index 81546ac..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_CLIENT_H_
-
-#include <dvr/dvr_api.h>
-#include <hardware/hwcomposer.h>
-#include <pdx/client.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-namespace display {
-
-class Surface : public pdx::ClientBase<Surface> {
- public:
- // Utility named constructor. This can be removed once ClientBase::Create is
- // refactored to return Status<T> types.
- static pdx::Status<std::unique_ptr<Surface>> CreateSurface(
- const SurfaceAttributes& attributes) {
- int error;
- pdx::Status<std::unique_ptr<Surface>> status;
- if (auto surface = Create(attributes, &error))
- status.SetValue(std::move(surface));
- else
- status.SetError(error);
- return status;
- }
-
- int surface_id() const { return surface_id_; }
- int z_order() const { return z_order_; }
- bool visible() const { return visible_; }
-
- pdx::Status<void> SetVisible(bool visible);
- pdx::Status<void> SetZOrder(int z_order);
- pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);
-
- // Creates an empty queue.
- pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
- uint32_t height,
- uint32_t format,
- size_t metadata_size);
-
- // Creates a queue and populates it with |capacity| buffers of the specified
- // parameters.
- pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
- uint32_t height,
- uint32_t layer_count,
- uint32_t format,
- uint64_t usage,
- size_t capacity,
- size_t metadata_size);
-
- private:
- friend BASE;
-
- int surface_id_ = -1;
- int z_order_ = 0;
- bool visible_ = false;
-
- // TODO(eieio,avakulenko): Remove error param once pdx::ClientBase::Create()
- // returns Status<T>.
- explicit Surface(const SurfaceAttributes& attributes, int* error = nullptr);
- explicit Surface(pdx::LocalChannelHandle channel_handle,
- int* error = nullptr);
-
- Surface(const Surface&) = delete;
- void operator=(const Surface&) = delete;
-};
-
-class DisplayClient : public pdx::ClientBase<DisplayClient> {
- public:
- pdx::Status<Metrics> GetDisplayMetrics();
- pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type);
- pdx::Status<uint8_t> GetDisplayIdentificationPort();
- pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage);
- pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
- pdx::Status<std::unique_ptr<IonBuffer>> GetGlobalBuffer(
- DvrGlobalBufferKey key);
- pdx::Status<std::unique_ptr<Surface>> CreateSurface(
- const SurfaceAttributes& attributes);
-
- // Temporary query for current VR status. Will be removed later.
- pdx::Status<bool> IsVrAppRunning();
-
- private:
- friend BASE;
-
- explicit DisplayClient(int* error = nullptr);
-
- DisplayClient(const DisplayClient&) = delete;
- void operator=(const DisplayClient&) = delete;
-};
-
-} // namespace display
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DISPLAY_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h b/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
deleted file mode 100644
index 45aef51..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_manager_client.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-#define ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
-
-#include <string>
-#include <vector>
-
-#include <pdx/client.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-namespace android {
-namespace dvr {
-
-class IonBuffer;
-class ConsumerQueue;
-
-namespace display {
-
-class DisplayManagerClient : public pdx::ClientBase<DisplayManagerClient> {
- public:
- ~DisplayManagerClient() override;
-
- pdx::Status<std::vector<SurfaceState>> GetSurfaceState();
- pdx::Status<std::unique_ptr<ConsumerQueue>> GetSurfaceQueue(int surface_id,
- int queue_id);
-
- using Client::event_fd;
-
- pdx::Status<int> GetEventMask(int events) {
- if (auto* client_channel = GetChannel())
- return client_channel->GetEventMask(events);
- else
- return pdx::ErrorStatus(EINVAL);
- }
-
- private:
- friend BASE;
-
- DisplayManagerClient();
-
- DisplayManagerClient(const DisplayManagerClient&) = delete;
- void operator=(const DisplayManagerClient&) = delete;
-};
-
-} // namespace display
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DISPLAY_MANAGER_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
deleted file mode 100644
index 9f4cc4a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ /dev/null
@@ -1,304 +0,0 @@
-#ifndef ANDROID_DVR_DISPLAY_PROTOCOL_H_
-#define ANDROID_DVR_DISPLAY_PROTOCOL_H_
-
-#include <sys/types.h>
-
-#include <array>
-#include <map>
-
-#include <dvr/dvr_display_types.h>
-
-#include <dvr/dvr_api.h>
-#include <pdx/rpc/buffer_wrapper.h>
-#include <pdx/rpc/remote_method.h>
-#include <pdx/rpc/serializable.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/bufferhub_rpc.h>
-
-// RPC protocol definitions for DVR display services (VrFlinger).
-
-namespace android {
-namespace dvr {
-namespace display {
-
-// Native display metrics.
-struct Metrics {
- // Basic display properties.
- uint32_t display_width;
- uint32_t display_height;
- uint32_t display_x_dpi;
- uint32_t display_y_dpi;
- uint32_t vsync_period_ns;
-
- // HMD metrics.
- // TODO(eieio): Determine how these fields should be populated. On phones
- // these values are determined at runtime by VrCore based on which headset the
- // phone is in. On dedicated hardware this needs to come from somewhere else.
- // Perhaps these should be moved to a separate structure that is returned by a
- // separate runtime call.
- uint32_t distorted_width;
- uint32_t distorted_height;
- uint32_t hmd_ipd_mm;
- float inter_lens_distance_m;
- std::array<float, 4> left_fov_lrbt;
- std::array<float, 4> right_fov_lrbt;
-
- private:
- PDX_SERIALIZABLE_MEMBERS(Metrics, display_width, display_height,
- display_x_dpi, display_y_dpi, vsync_period_ns,
- distorted_width, distorted_height, hmd_ipd_mm,
- inter_lens_distance_m, left_fov_lrbt,
- right_fov_lrbt);
-};
-
-// Serializable base type for enum structs. Enum structs are easier to use than
-// enum classes, especially for bitmasks. This base type provides common
-// utilities for flags types.
-template <typename Integer>
-class Flags {
- public:
- using Base = Flags<Integer>;
- using Type = Integer;
-
- // NOLINTNEXTLINE(google-explicit-constructor)
- Flags(const Integer& value) : value_{value} {}
- Flags(const Flags&) = default;
- Flags& operator=(const Flags&) = default;
-
- Integer value() const { return value_; }
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator Integer() const { return value_; }
-
- bool IsSet(Integer bits) const { return (value_ & bits) == bits; }
- bool IsClear(Integer bits) const { return (value_ & bits) == 0; }
-
- void Set(Integer bits) { value_ |= bits; }
- void Clear(Integer bits) { value_ &= ~bits; }
-
- Integer operator|(Integer bits) const { return value_ | bits; }
- Integer operator&(Integer bits) const { return value_ & bits; }
-
- Flags& operator|=(Integer bits) {
- value_ |= bits;
- return *this;
- }
- Flags& operator&=(Integer bits) {
- value_ &= bits;
- return *this;
- }
-
- private:
- Integer value_;
-
- PDX_SERIALIZABLE_MEMBERS(Flags<Integer>, value_);
-};
-
-// Flags indicating what changed since last update.
-struct SurfaceUpdateFlags : public Flags<uint32_t> {
- enum : Type {
- None = DVR_SURFACE_UPDATE_FLAGS_NONE,
- NewSurface = DVR_SURFACE_UPDATE_FLAGS_NEW_SURFACE,
- BuffersChanged = DVR_SURFACE_UPDATE_FLAGS_BUFFERS_CHANGED,
- VisibilityChanged = DVR_SURFACE_UPDATE_FLAGS_VISIBILITY_CHANGED,
- AttributesChanged = DVR_SURFACE_UPDATE_FLAGS_ATTRIBUTES_CHANGED,
- };
-
- SurfaceUpdateFlags() : Base{None} {}
- using Base::Base;
-};
-
-// Surface attribute key/value types.
-using SurfaceAttributeKey = int32_t;
-using SurfaceAttributeValue =
- pdx::rpc::Variant<int32_t, int64_t, bool, float, std::array<float, 2>,
- std::array<float, 3>, std::array<float, 4>,
- std::array<float, 8>, std::array<float, 16>>;
-
-// Defined surface attribute keys.
-struct SurfaceAttribute : public Flags<SurfaceAttributeKey> {
- enum : Type {
- // Keys in the negative integer space are interpreted by VrFlinger for
- // direct surfaces.
- Direct = DVR_SURFACE_ATTRIBUTE_DIRECT,
- ZOrder = DVR_SURFACE_ATTRIBUTE_Z_ORDER,
- Visible = DVR_SURFACE_ATTRIBUTE_VISIBLE,
-
- // Invalid key. May be used to terminate C style lists in public API code.
- Invalid = DVR_SURFACE_ATTRIBUTE_INVALID,
-
- // Positive keys are interpreted by the compositor only.
- FirstUserKey = DVR_SURFACE_ATTRIBUTE_FIRST_USER_KEY,
- };
-
- SurfaceAttribute() : Base{Invalid} {}
- using Base::Base;
-};
-
-// Collection of surface attribute key/value pairs.
-using SurfaceAttributes = std::map<SurfaceAttributeKey, SurfaceAttributeValue>;
-
-struct SurfaceState {
- int32_t surface_id;
- int32_t process_id;
- int32_t user_id;
-
- SurfaceAttributes surface_attributes;
- SurfaceUpdateFlags update_flags;
- std::vector<int32_t> queue_ids;
-
- // Convenience accessors.
- bool GetVisible() const {
- bool bool_value = false;
- GetAttribute(SurfaceAttribute::Visible, &bool_value,
- ValidTypes<int32_t, int64_t, bool, float>{});
- return bool_value;
- }
-
- int GetZOrder() const {
- int int_value = 0;
- GetAttribute(SurfaceAttribute::ZOrder, &int_value,
- ValidTypes<int32_t, int64_t, float>{});
- return int_value;
- }
-
- private:
- template <typename... Types>
- struct ValidTypes {};
-
- template <typename ReturnType, typename... Types>
- bool GetAttribute(SurfaceAttributeKey key, ReturnType* out_value,
- ValidTypes<Types...>) const {
- auto search = surface_attributes.find(key);
- if (search != surface_attributes.end())
- return pdx::rpc::IfAnyOf<Types...>::Get(&search->second, out_value);
- else
- return false;
- }
-
- PDX_SERIALIZABLE_MEMBERS(SurfaceState, surface_id, process_id,
- surface_attributes, update_flags, queue_ids);
-};
-
-struct SurfaceInfo {
- int surface_id;
- bool visible;
- int z_order;
-
- private:
- PDX_SERIALIZABLE_MEMBERS(SurfaceInfo, surface_id, visible, z_order);
-};
-
-enum class ConfigFileType : uint32_t {
- kLensMetrics,
- kDeviceMetrics,
- kDeviceConfiguration,
- kDeviceEdid
-};
-
-struct DisplayProtocol {
- // Service path.
- static constexpr char kClientPath[] = "system/vr/display/client";
-
- // Op codes.
- enum {
- kOpGetMetrics = 0,
- kOpGetConfigurationData,
- kOpSetupGlobalBuffer,
- kOpDeleteGlobalBuffer,
- kOpGetGlobalBuffer,
- kOpIsVrAppRunning,
- kOpCreateSurface,
- kOpGetSurfaceInfo,
- kOpCreateQueue,
- kOpSetAttributes,
- kOpGetDisplayIdentificationPort,
- };
-
- // Aliases.
- using LocalChannelHandle = pdx::LocalChannelHandle;
- using Void = pdx::rpc::Void;
-
- // Methods.
- PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
- PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
- std::string(ConfigFileType config_type));
- PDX_REMOTE_METHOD(GetDisplayIdentificationPort,
- kOpGetDisplayIdentificationPort, uint8_t(Void));
- PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer,
- LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size,
- uint64_t usage));
- PDX_REMOTE_METHOD(DeleteGlobalBuffer, kOpDeleteGlobalBuffer,
- void(DvrGlobalBufferKey key));
- PDX_REMOTE_METHOD(GetGlobalBuffer, kOpGetGlobalBuffer,
- LocalNativeBufferHandle(DvrGlobalBufferKey key));
- PDX_REMOTE_METHOD(IsVrAppRunning, kOpIsVrAppRunning, bool(Void));
- PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
- SurfaceInfo(const SurfaceAttributes& attributes));
- PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
- PDX_REMOTE_METHOD(
- CreateQueue, kOpCreateQueue,
- LocalChannelHandle(const ProducerQueueConfig& producer_config));
- PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
- void(const SurfaceAttributes& attributes));
-};
-
-struct DisplayManagerProtocol {
- // Service path.
- static constexpr char kClientPath[] = "system/vr/display/manager";
-
- // Op codes.
- enum {
- kOpGetSurfaceState = 0,
- kOpGetSurfaceQueue,
- };
-
- // Aliases.
- using LocalChannelHandle = pdx::LocalChannelHandle;
- using Void = pdx::rpc::Void;
-
- // Methods.
- PDX_REMOTE_METHOD(GetSurfaceState, kOpGetSurfaceState,
- std::vector<SurfaceState>(Void));
- PDX_REMOTE_METHOD(GetSurfaceQueue, kOpGetSurfaceQueue,
- LocalChannelHandle(int surface_id, int queue_id));
-};
-
-struct VSyncSchedInfo {
- int64_t vsync_period_ns;
- int64_t timestamp_ns;
- uint32_t next_vsync_count;
-
- private:
- PDX_SERIALIZABLE_MEMBERS(VSyncSchedInfo, vsync_period_ns, timestamp_ns,
- next_vsync_count);
-};
-
-struct VSyncProtocol {
- // Service path.
- static constexpr char kClientPath[] = "system/vr/display/vsync";
-
- // Op codes.
- enum {
- kOpWait = 0,
- kOpAck,
- kOpGetLastTimestamp,
- kOpGetSchedInfo,
- kOpAcknowledge,
- };
-
- // Aliases.
- using Void = pdx::rpc::Void;
- using Timestamp = int64_t;
-
- // Methods.
- PDX_REMOTE_METHOD(Wait, kOpWait, Timestamp(Void));
- PDX_REMOTE_METHOD(GetLastTimestamp, kOpGetLastTimestamp, Timestamp(Void));
- PDX_REMOTE_METHOD(GetSchedInfo, kOpGetSchedInfo, VSyncSchedInfo(Void));
- PDX_REMOTE_METHOD(Acknowledge, kOpAcknowledge, void(Void));
-};
-
-} // namespace display
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_DISPLAY_PROTOCOL_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h b/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
deleted file mode 100644
index 20541a6..0000000
--- a/libs/vr/libdisplay/include/private/dvr/shared_buffer_helpers.h
+++ /dev/null
@@ -1,146 +0,0 @@
-#ifndef ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-#define ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
-
-#include <assert.h>
-#include <tuple>
-
-#include <libbroadcastring/broadcast_ring.h>
-#include <private/dvr/display_client.h>
-
-namespace android {
-namespace dvr {
-
-// The buffer usage type for mapped shared buffers.
-enum class CPUUsageMode { READ_OFTEN, READ_RARELY, WRITE_OFTEN, WRITE_RARELY };
-
-// Holds the memory for the mapped shared buffer. Unlocks and releases the
-// underlying IonBuffer in destructor.
-class CPUMappedBuffer {
- public:
- // This constructor will create a display client and get the buffer from it.
- CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode);
-
- // If you already have the IonBuffer, use this. It will take ownership.
- CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode);
-
- // Use this if you do not want to take ownership.
- CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode);
-
- ~CPUMappedBuffer();
-
- // Getters.
- size_t Size() const { return size_; }
- void* Address() const { return address_; }
- bool IsMapped() const { return Address() != nullptr; }
-
- // Attempt mapping this buffer to the CPU addressable space.
- // This will create a display client and see if the buffer exists.
- // If the buffer has not been setup yet, you will need to try again later.
- void TryMapping();
-
- protected:
- // The memory area if we managed to map it.
- size_t size_ = 0;
- void* address_ = nullptr;
-
- // If we are polling the display client, the buffer key here.
- DvrGlobalBufferKey buffer_key_;
-
- // If we just own the IonBuffer outright, it's here.
- std::unique_ptr<IonBuffer> owned_buffer_ = nullptr;
-
- // The last time we connected to the display service.
- int64_t last_display_service_connection_ns_ = 0;
-
- // If we do not own the IonBuffer, it's here
- IonBuffer* buffer_ = nullptr;
-
- // The usage mode.
- CPUUsageMode usage_mode_ = CPUUsageMode::READ_OFTEN;
-};
-
-// Represents a broadcast ring inside a mapped shared memory buffer.
-// If has the same set of constructors as CPUMappedBuffer.
-// The template argument is the concrete BroadcastRing class that this buffer
-// holds.
-template <class RingType>
-class CPUMappedBroadcastRing : public CPUMappedBuffer {
- public:
- CPUMappedBroadcastRing(DvrGlobalBufferKey key, CPUUsageMode mode)
- : CPUMappedBuffer(key, mode) {}
-
- CPUMappedBroadcastRing(std::unique_ptr<IonBuffer> buffer, CPUUsageMode mode)
- : CPUMappedBuffer(std::move(buffer), mode) {}
-
- CPUMappedBroadcastRing(IonBuffer* buffer, CPUUsageMode mode)
- : CPUMappedBuffer(buffer, mode) {}
-
- // Helper function for publishing records in the ring.
- void Publish(const typename RingType::Record& record) {
- assert((usage_mode_ == CPUUsageMode::WRITE_OFTEN) ||
- (usage_mode_ == CPUUsageMode::WRITE_RARELY));
-
- auto ring = Ring();
- if (ring) {
- ring->Put(record);
- }
- }
-
- // Helper function for getting records from the ring.
- // Returns true if we were able to retrieve the latest.
- bool GetNewest(typename RingType::Record* record) {
- assert((usage_mode_ == CPUUsageMode::READ_OFTEN) ||
- (usage_mode_ == CPUUsageMode::READ_RARELY));
-
- auto ring = Ring();
- if (ring) {
- return ring->GetNewest(&sequence_, record);
- }
-
- return false;
- }
-
- // Try obtaining the ring. If the named buffer has not been created yet, it
- // will return nullptr.
- RingType* Ring() {
- // No ring created yet?
- if (ring_ == nullptr) {
- // Not mapped the memory yet?
- if (IsMapped() == false) {
- TryMapping();
- }
-
- // If have the memory mapped, allocate the ring.
- if (IsMapped()) {
- switch (usage_mode_) {
- case CPUUsageMode::READ_OFTEN:
- case CPUUsageMode::READ_RARELY: {
- RingType ring;
- bool import_ok;
- std::tie(ring, import_ok) = RingType::Import(address_, size_);
- if (import_ok) {
- ring_ = std::make_unique<RingType>(ring);
- }
- } break;
- case CPUUsageMode::WRITE_OFTEN:
- case CPUUsageMode::WRITE_RARELY:
- ring_ =
- std::make_unique<RingType>(RingType::Create(address_, size_));
- break;
- }
- }
- }
-
- return ring_.get();
- }
-
- protected:
- std::unique_ptr<RingType> ring_ = nullptr;
-
- uint32_t sequence_ = 0;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SHARED_BUFFER_HELPERS_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
deleted file mode 100644
index 152464a..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_service.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
-#define ANDROID_DVR_VSYNC_SERVICE_H_
-
-#include <binder/IInterface.h>
-
-namespace android {
-namespace dvr {
-
-class IVsyncCallback : public IInterface {
- public:
- DECLARE_META_INTERFACE(VsyncCallback)
-
- enum {
- ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
- };
-
- virtual status_t onVsync(int64_t vsync_timestamp) = 0;
-};
-
-class BnVsyncCallback : public BnInterface<IVsyncCallback> {
- public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-// Register a callback with IVsyncService to be notified of vsync events and
-// timestamps. There's also a shared memory vsync buffer defined in
-// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
-// memory buffer that make it preferable in certain situations:
-//
-// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
-// is always available as long as surface flinger is running.
-//
-// 2. IVsyncService will make a binder callback when a vsync event occurs. This
-// allows the client to not write code to implement periodic "get the latest
-// vsync" calls, which is necessary with the vsync shared memory buffer.
-//
-// 3. The IVsyncService provides the real vsync timestamp reported by hardware
-// composer, whereas the vsync shared memory buffer only has predicted vsync
-// times.
-class IVsyncService : public IInterface {
-public:
- DECLARE_META_INTERFACE(VsyncService)
-
- static const char* GetServiceName() { return "vrflinger_vsync"; }
-
- enum {
- REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
- UNREGISTER_CALLBACK
- };
-
- virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
- virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
-};
-
-class BnVsyncService : public BnInterface<IVsyncService> {
- public:
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags = 0);
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/shared_buffer_helpers.cpp b/libs/vr/libdisplay/shared_buffer_helpers.cpp
deleted file mode 100644
index 6ebf487..0000000
--- a/libs/vr/libdisplay/shared_buffer_helpers.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-// We will not poll the display service for buffers more frequently than this.
-constexpr size_t kDisplayServiceTriesPerSecond = 2;
-} // namespace
-
-CPUMappedBuffer::CPUMappedBuffer(DvrGlobalBufferKey key, CPUUsageMode mode)
- : buffer_key_(key), usage_mode_(mode) {
- TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(std::unique_ptr<IonBuffer> buffer,
- CPUUsageMode mode)
- : owned_buffer_(std::move(buffer)),
- buffer_(owned_buffer_.get()),
- usage_mode_(mode) {
- TryMapping();
-}
-
-CPUMappedBuffer::CPUMappedBuffer(IonBuffer* buffer, CPUUsageMode mode)
- : buffer_(buffer), usage_mode_(mode) {
- TryMapping();
-}
-
-CPUMappedBuffer::~CPUMappedBuffer() {
- if (IsMapped()) {
- buffer_->Unlock();
- }
-}
-
-void CPUMappedBuffer::TryMapping() {
- // Do we have an IonBuffer for this shared memory object?
- if (buffer_ == nullptr) {
- // Has it been too long since we last connected to the display service?
- const auto current_time_ns = GetSystemClockNs();
- if ((current_time_ns - last_display_service_connection_ns_) <
- (1e9 / kDisplayServiceTriesPerSecond)) {
- // Early exit.
- return;
- }
- last_display_service_connection_ns_ = current_time_ns;
-
- // Create a display client and get the buffer.
- auto display_client = display::DisplayClient::Create();
- if (display_client) {
- auto get_result = display_client->GetGlobalBuffer(buffer_key_);
- if (get_result.ok()) {
- owned_buffer_ = get_result.take();
- buffer_ = owned_buffer_.get();
- } else {
- // The buffer has not been created yet. This is OK, we will keep
- // retrying.
- }
- } else {
- ALOGE("Unable to create display client for shared buffer access");
- }
- }
-
- if (buffer_) {
- auto usage = buffer_->usage() & ~GRALLOC_USAGE_SW_READ_MASK &
- ~GRALLOC_USAGE_SW_WRITE_MASK;
-
- // Figure out the usage bits.
- switch (usage_mode_) {
- case CPUUsageMode::READ_OFTEN:
- usage |= GRALLOC_USAGE_SW_READ_OFTEN;
- break;
- case CPUUsageMode::READ_RARELY:
- usage |= GRALLOC_USAGE_SW_READ_RARELY;
- break;
- case CPUUsageMode::WRITE_OFTEN:
- usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
- break;
- case CPUUsageMode::WRITE_RARELY:
- usage |= GRALLOC_USAGE_SW_WRITE_RARELY;
- break;
- }
-
- int width = static_cast<int>(buffer_->width());
- int height = 1;
- const auto ret = buffer_->Lock(usage, 0, 0, width, height, &address_);
-
- if (ret < 0 || !address_) {
- ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, address_);
- buffer_->Unlock();
- } else {
- size_ = width;
- }
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libdisplay/system/CPPLINT.cfg b/libs/vr/libdisplay/system/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libdisplay/system/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
deleted file mode 100644
index 04d4f30..0000000
--- a/libs/vr/libdisplay/vsync_service.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "include/private/dvr/vsync_service.h"
-
-#include <binder/Parcel.h>
-#include <log/log.h>
-
-namespace android {
-namespace dvr {
-
-status_t BnVsyncCallback::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch (code) {
- case ON_VSYNC: {
- CHECK_INTERFACE(IVsyncCallback, data, reply);
- int64_t vsync_timestamp = 0;
- status_t result = data.readInt64(&vsync_timestamp);
- if (result != OK) {
- ALOGE("onVsync failed to readInt64: %d", result);
- return result;
- }
- onVsync(vsync_timestamp);
- return OK;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-class BpVsyncCallback : public BpInterface<IVsyncCallback> {
-public:
- explicit BpVsyncCallback(const sp<IBinder>& impl)
- : BpInterface<IVsyncCallback>(impl) {}
- virtual ~BpVsyncCallback() {}
-
- virtual status_t onVsync(int64_t vsync_timestamp) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IVsyncCallback::getInterfaceDescriptor());
- if (result != OK) {
- ALOGE("onVsync failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeInt64(vsync_timestamp);
- if (result != OK) {
- ALOGE("onVsync failed to writeInt64: %d", result);
- return result;
- }
- result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply,
- IBinder::FLAG_ONEWAY);
- if (result != OK) {
- ALOGE("onVsync failed to transact: %d", result);
- return result;
- }
- return result;
- }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
-
-
-status_t BnVsyncService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch (code) {
- case REGISTER_CALLBACK: {
- CHECK_INTERFACE(IVsyncService, data, reply);
- sp<IBinder> callback;
- status_t result = data.readStrongBinder(&callback);
- if (result != OK) {
- ALOGE("registerCallback failed to readStrongBinder: %d", result);
- return result;
- }
- registerCallback(interface_cast<IVsyncCallback>(callback));
- return OK;
- }
- case UNREGISTER_CALLBACK: {
- CHECK_INTERFACE(IVsyncService, data, reply);
- sp<IBinder> callback;
- status_t result = data.readStrongBinder(&callback);
- if (result != OK) {
- ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
- return result;
- }
- unregisterCallback(interface_cast<IVsyncCallback>(callback));
- return OK;
- }
- default: {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-}
-
-class BpVsyncService : public BpInterface<IVsyncService> {
-public:
- explicit BpVsyncService(const sp<IBinder>& impl)
- : BpInterface<IVsyncService>(impl) {}
- virtual ~BpVsyncService() {}
-
- virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IVsyncService::getInterfaceDescriptor());
- if (result != OK) {
- ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(IInterface::asBinder(callback));
- if (result != OK) {
- ALOGE("registerCallback failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(
- BnVsyncService::REGISTER_CALLBACK, data, &reply);
- if (result != OK) {
- ALOGE("registerCallback failed to transact: %d", result);
- return result;
- }
- return result;
- }
-
- virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(
- IVsyncService::getInterfaceDescriptor());
- if (result != OK) {
- ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(IInterface::asBinder(callback));
- if (result != OK) {
- ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(
- BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
- if (result != OK) {
- ALOGE("unregisterCallback failed to transact: %d", result);
- return result;
- }
- return result;
- }
-};
-
-IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
deleted file mode 100644
index 40a5099..0000000
--- a/libs/vr/libvrsensor/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2015 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
- "pose_client.cpp",
- "latency_model.cpp",
-]
-
-includeFiles = [
- "include",
-]
-
-staticLibraries = [
- "libdisplay",
- "libdvrcommon",
- "libbroadcastring",
-]
-
-sharedLibraries = [
- "libbase",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libhardware",
- "liblog",
- "libutils",
- "libui",
- "libpdx_default_transport",
-]
-
-cc_library {
- srcs: sourceFiles,
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-macro-redefined",
- ],
- export_include_dirs: includeFiles,
- static_libs: staticLibraries,
- shared_libs: sharedLibraries,
- header_libs: ["libdvr_headers"],
- name: "libvrsensor",
-}
diff --git a/libs/vr/libvrsensor/include/CPPLINT.cfg b/libs/vr/libvrsensor/include/CPPLINT.cfg
deleted file mode 100644
index 2f8a3c0..0000000
--- a/libs/vr/libvrsensor/include/CPPLINT.cfg
+++ /dev/null
@@ -1 +0,0 @@
-filter=-build/header_guard
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
deleted file mode 100644
index b663a67..0000000
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ /dev/null
@@ -1,176 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_H_
-#define ANDROID_DVR_POSE_CLIENT_H_
-
-#ifdef __ARM_NEON
-#include <arm_neon.h>
-#else
-#ifndef __FLOAT32X4T_86
-#define __FLOAT32X4T_86
-typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
-#endif
-#endif
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <dvr/dvr_pose.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-// Returned by the async pose ring buffer access API.
-typedef struct DvrPoseRingBufferInfo {
- // Read-only pointer to the pose ring buffer. The current pose is in this
- // buffer at element buffer[current_frame & (buffer_size - 1)]. The next
- // frame's forecasted pose is at element
- // ((current_frame + 1) & (buffer_size - 1)). And so on. The poses are
- // predicted for when 50% of the corresponding frame's pixel data is visible
- // to the user.
- // The last value returned by dvrPresent is the count for the next frame,
- // which is the earliest that the application could display something if they
- // were to render promptly. (TODO(jbates) move this comment to dvrPresent).
- volatile const DvrPoseAsync* buffer;
- // Minimum number of accurate forecasted poses including the current frame's
- // pose. This is the number of poses that are udpated by the pose service.
- // If the application reads past this count, they will get a stale prediction
- // from a previous frame. Guaranteed to be at least 2.
- uint32_t min_future_count;
- // Number of elements in buffer. At least 8 and greater than min_future_count.
- // Guaranteed to be a power of two. The total size of the buffer in bytes is:
- // total_count * sizeof(DvrPoseAsync)
- uint32_t total_count;
-} DvrPoseRingBufferInfo;
-
-typedef enum DvrPoseMode {
- DVR_POSE_MODE_6DOF = 0,
- DVR_POSE_MODE_3DOF,
- DVR_POSE_MODE_MOCK_FROZEN,
- DVR_POSE_MODE_MOCK_HEAD_TURN_SLOW,
- DVR_POSE_MODE_MOCK_HEAD_TURN_FAST,
- DVR_POSE_MODE_MOCK_ROTATE_SLOW,
- DVR_POSE_MODE_MOCK_ROTATE_MEDIUM,
- DVR_POSE_MODE_MOCK_ROTATE_FAST,
- DVR_POSE_MODE_MOCK_CIRCLE_STRAFE,
- DVR_POSE_MODE_FLOAT,
- DVR_POSE_MODE_MOCK_MOTION_SICKNESS,
-
- // Always last.
- DVR_POSE_MODE_COUNT,
-} DvrPoseMode;
-
-typedef enum DvrControllerId {
- DVR_CONTROLLER_0 = 0,
- DVR_CONTROLLER_1 = 1,
-} DvrControllerId;
-
-// Creates a new pose client.
-//
-// @return Pointer to the created pose client, nullptr on failure.
-DvrPoseClient* dvrPoseClientCreate();
-
-// Destroys a pose client.
-//
-// @param client Pointer to the pose client to be destroyed.
-void dvrPoseClientDestroy(DvrPoseClient* client);
-
-// Gets the pose for the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-// Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
- DvrPoseAsync* out_pose);
-
-// Gets the current vsync count.
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client);
-
-// Gets the pose for the given controller at the given vsync count.
-//
-// @param client Pointer to the pose client.
-// @param controller_id The controller id.
-// @param vsync_count Vsync that this pose should be forward-predicted to.
-// Typically this is the count returned by dvrGetNextVsyncCount.
-// @param out_pose Struct to store pose state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
- uint32_t vsync_count, DvrPoseAsync* out_pose);
-
-// Enables/disables logging for the controller fusion.
-//
-// @param client Pointer to the pose client.
-// @param enable True starts logging, False stops.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable);
-
-// DEPRECATED
-// Polls current pose state.
-//
-// @param client Pointer to the pose client.
-// @param state Struct to store polled state.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state);
-
-// Freezes the pose to the provided state.
-//
-// Future poll operations will return this state until a different state is
-// frozen or dvrPoseClientModeSet() is called with a different mode. The timestamp is
-// not frozen.
-//
-// @param client Pointer to the pose client.
-// @param frozen_state State pose to be frozen to.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state);
-
-// Sets the pose service mode.
-//
-// @param mode The requested pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode);
-
-// Gets the pose service mode.
-//
-// @param mode Return value for the current pose mode.
-// @return Zero on success, negative error code on failure.
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode);
-
-// Get access to the shared memory pose ring buffer.
-// A future pose at vsync <current> + <offset> is accessed at index:
-// index = (<current> + <offset>) % out_buffer_size
-// Where <current> was the last value returned by dvrPresent and
-// <offset> is less than or equal to |out_min_future_count|.
-// |out_buffer| will be set to a pointer to the buffer.
-// |out_fd| will be set to the gralloc buffer file descriptor, which is
-// required for binding this buffer for GPU use.
-// Returns 0 on success.
-int dvrPoseClientGetRingBuffer(DvrPoseClient* client,
- DvrPoseRingBufferInfo* out_info);
-
-// Sets enabled state for sensors pose processing.
-//
-// @param enabled Whether sensors are enabled or disabled.
-// @return Zero on success
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled);
-
-// Requests a burst of data samples from pose service. The data samples are
-// passed through a shared memory buffer obtained by calling
-// dvrPoseClientGetDataReader().
-//
-// @param DvrPoseDataCaptureRequest Parameters on how to capture data.
-// @return Zero on success.
-int dvrPoseClientDataCapture(DvrPoseClient* client,
- const DvrPoseDataCaptureRequest* request);
-
-// Destroys the write buffer queue for the given |data_type|.
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // ANDROID_DVR_POSE_CLIENT_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/latency_model.h b/libs/vr/libvrsensor/include/private/dvr/latency_model.h
deleted file mode 100644
index bf0e687..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/latency_model.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_LATENCY_MODEL_H_
-#define ANDROID_DVR_LATENCY_MODEL_H_
-
-#include <vector>
-
-namespace android {
-namespace dvr {
-
-// This class models the latency from sensors. It will look at the first
-// window_size measurements and return their average after that.
-class LatencyModel {
- public:
- explicit LatencyModel(size_t window_size);
- ~LatencyModel() = default;
-
- void AddLatency(int64_t latency_ns);
- int64_t CurrentLatencyEstimate() const { return latency_; }
-
- private:
- size_t window_size_;
- int64_t latency_sum_ = 0;
- size_t num_summed_ = 0;
- int64_t latency_ = 0;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_LATENCY_MODEL_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h b/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
deleted file mode 100644
index 7bf1cd4..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose-ipc.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef ANDROID_DVR_POSE_IPC_H_
-#define ANDROID_DVR_POSE_IPC_H_
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DVR_POSE_SERVICE_BASE "system/vr/pose"
-#define DVR_POSE_SERVICE_CLIENT (DVR_POSE_SERVICE_BASE "/client")
-
-enum {
- DVR_POSE_FREEZE = 0,
- DVR_POSE_SET_MODE,
- DVR_POSE_GET_MODE,
- DVR_POSE_GET_CONTROLLER_RING_BUFFER,
- DVR_POSE_LOG_CONTROLLER,
- DVR_POSE_SENSORS_ENABLE,
- DVR_POSE_GET_TANGO_READER,
- DVR_POSE_DATA_CAPTURE,
- DVR_POSE_TANGO_READER_DESTROY,
-};
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // ANDROID_DVR_POSE_IPC_H_
diff --git a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h b/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
deleted file mode 100644
index 39592bb..0000000
--- a/libs/vr/libvrsensor/include/private/dvr/pose_client_internal.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-#define ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
-
-#include <private/dvr/buffer_hub_queue_client.h>
-
-using android::dvr::ConsumerQueue;
-
-typedef struct DvrPoseClient DvrPoseClient;
-
-namespace android {
-namespace dvr {
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient *client, uint64_t data_type,
- ConsumerQueue **queue_out);
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_POSE_CLIENT_INTERNAL_H_
diff --git a/libs/vr/libvrsensor/latency_model.cpp b/libs/vr/libvrsensor/latency_model.cpp
deleted file mode 100644
index d3a4521..0000000
--- a/libs/vr/libvrsensor/latency_model.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <private/dvr/latency_model.h>
-
-#include <cmath>
-
-namespace android {
-namespace dvr {
-
-LatencyModel::LatencyModel(size_t window_size) : window_size_(window_size) {}
-
-void LatencyModel::AddLatency(int64_t latency_ns) {
- // Not enough samples yet?
- if (num_summed_ < window_size_) {
- // Accumulate.
- latency_sum_ += latency_ns;
-
- // Have enough samples for latency estimate?
- if (++num_summed_ == window_size_) {
- latency_ = latency_sum_ / window_size_;
- }
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp
deleted file mode 100644
index 4ff6a09..0000000
--- a/libs/vr/libvrsensor/pose_client.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-#define LOG_TAG "PoseClient"
-#include <dvr/dvr_shared_buffers.h>
-#include <dvr/pose_client.h>
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <pdx/client.h>
-#include <pdx/default_transport/client_channel_factory.h>
-#include <pdx/file_handle.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/consumer_buffer.h>
-#include <private/dvr/display_client.h>
-#include <private/dvr/pose-ipc.h>
-#include <private/dvr/shared_buffer_helpers.h>
-
-using android::dvr::ConsumerQueue;
-using android::pdx::LocalHandle;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Status;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-namespace {
-
-typedef CPUMappedBroadcastRing<DvrPoseRing> SensorPoseRing;
-
-constexpr static int32_t MAX_CONTROLLERS = 2;
-} // namespace
-
-// PoseClient is a remote interface to the pose service in sensord.
-class PoseClient : public pdx::ClientBase<PoseClient> {
- public:
- ~PoseClient() override {}
-
- // Casts C handle into an instance of this class.
- static PoseClient* FromC(DvrPoseClient* client) {
- return reinterpret_cast<PoseClient*>(client);
- }
-
- // Polls the pose service for the current state and stores it in *state.
- // Returns zero on success, a negative error code otherwise.
- int Poll(DvrPose* state) {
- // Allocate the helper class to access the sensor pose buffer.
- if (sensor_pose_buffer_ == nullptr) {
- sensor_pose_buffer_ = std::make_unique<SensorPoseRing>(
- DvrGlobalBuffers::kSensorPoseBuffer, CPUUsageMode::READ_RARELY);
- }
-
- if (state) {
- if (sensor_pose_buffer_->GetNewest(state)) {
- return 0;
- } else {
- return -EAGAIN;
- }
- }
-
- return -EINVAL;
- }
-
- int GetPose(uint32_t vsync_count, DvrPoseAsync* out_pose) {
- const auto vsync_buffer = GetVsyncBuffer();
- if (vsync_buffer) {
- *out_pose =
- vsync_buffer
- ->vsync_poses[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
- return 0;
- } else {
- return -EAGAIN;
- }
- }
-
- uint32_t GetVsyncCount() {
- const auto vsync_buffer = GetVsyncBuffer();
- if (vsync_buffer) {
- return vsync_buffer->vsync_count;
- }
-
- return 0;
- }
-
- int GetControllerPose(int32_t controller_id, uint32_t vsync_count,
- DvrPoseAsync* out_pose) {
- if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
- return -EINVAL;
- }
- if (!controllers_[controller_id].mapped_pose_buffer) {
- int ret = GetControllerRingBuffer(controller_id);
- if (ret < 0)
- return ret;
- }
- *out_pose =
- controllers_[controller_id]
- .mapped_pose_buffer[vsync_count & DvrVsyncPoseBuffer::kIndexMask];
- return 0;
- }
-
- int LogController(bool enable) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_LOG_CONTROLLER, &enable,
- sizeof(enable), nullptr, 0);
- ALOGE_IF(!status, "Pose LogController() failed because: %s",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Freezes the pose to the provided state. Future poll operations will return
- // this state until a different state is frozen or SetMode() is called with a
- // different mode.
- // Returns zero on success, a negative error code otherwise.
- int Freeze(const DvrPose& frozen_state) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_FREEZE, &frozen_state,
- sizeof(frozen_state), nullptr, 0);
- ALOGE_IF(!status, "Pose Freeze() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Sets the data mode for the pose service.
- int SetMode(DvrPoseMode mode) {
- Transaction trans{*this};
- Status<int> status =
- trans.Send<int>(DVR_POSE_SET_MODE, &mode, sizeof(mode), nullptr, 0);
- ALOGE_IF(!status, "Pose SetPoseMode() failed because: %s",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Gets the data mode for the pose service.
- int GetMode(DvrPoseMode* out_mode) {
- int mode;
- Transaction trans{*this};
- Status<int> status =
- trans.Send<int>(DVR_POSE_GET_MODE, nullptr, 0, &mode, sizeof(mode));
- ALOGE_IF(!status, "Pose GetPoseMode() failed because: %s",
- status.GetErrorMessage().c_str());
- if (status)
- *out_mode = DvrPoseMode(mode);
- return ReturnStatusOrError(status);
- }
-
- int GetTangoReaderHandle(uint64_t data_type, ConsumerQueue** queue_out) {
- // Get buffer.
- Transaction trans{*this};
- Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
- DVR_POSE_GET_TANGO_READER, &data_type, sizeof(data_type), nullptr, 0);
-
- if (!status) {
- ALOGE("PoseClient GetTangoReaderHandle() failed because: %s",
- status.GetErrorMessage().c_str());
- *queue_out = nullptr;
- return -status.error();
- }
-
- std::unique_ptr<ConsumerQueue> consumer_queue =
- ConsumerQueue::Import(status.take());
- *queue_out = consumer_queue.release();
- return 0;
- }
-
- int DataCapture(const DvrPoseDataCaptureRequest* request) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_DATA_CAPTURE, request,
- sizeof(*request), nullptr, 0);
- ALOGE_IF(!status, "PoseClient DataCapture() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- int DataReaderDestroy(uint64_t data_type) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_TANGO_READER_DESTROY,
- &data_type, sizeof(data_type), nullptr,
- 0);
- ALOGE_IF(!status, "PoseClient DataReaderDestroy() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- // Enables or disables all pose processing from sensors
- int EnableSensors(bool enabled) {
- Transaction trans{*this};
- Status<int> status = trans.Send<int>(DVR_POSE_SENSORS_ENABLE, &enabled,
- sizeof(enabled), nullptr, 0);
- ALOGE_IF(!status, "Pose EnableSensors() failed because: %s\n",
- status.GetErrorMessage().c_str());
- return ReturnStatusOrError(status);
- }
-
- int GetRingBuffer(DvrPoseRingBufferInfo* out_info) {
- // First time mapping the buffer?
- const auto vsync_buffer = GetVsyncBuffer();
- if (vsync_buffer) {
- if (out_info) {
- out_info->min_future_count = DvrVsyncPoseBuffer::kMinFutureCount;
- out_info->total_count = DvrVsyncPoseBuffer::kSize;
- out_info->buffer = vsync_buffer->vsync_poses;
- }
- return -EINVAL;
- }
-
- return -EAGAIN;
- }
-
- int GetControllerRingBuffer(int32_t controller_id) {
- if (controller_id < 0 || controller_id >= MAX_CONTROLLERS) {
- return -EINVAL;
- }
- ControllerClientState& client_state = controllers_[controller_id];
- if (client_state.pose_buffer.get()) {
- return 0;
- }
-
- Transaction trans{*this};
- Status<LocalChannelHandle> status = trans.Send<LocalChannelHandle>(
- DVR_POSE_GET_CONTROLLER_RING_BUFFER, &controller_id,
- sizeof(controller_id), nullptr, 0);
- if (!status) {
- return -status.error();
- }
-
- auto buffer = ConsumerBuffer::Import(status.take());
- if (!buffer) {
- ALOGE("Pose failed to import ring buffer");
- return -EIO;
- }
- constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync);
- void* addr = nullptr;
- int ret = buffer->GetBlobReadWritePointer(size, &addr);
- if (ret < 0 || !addr) {
- ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr);
- return -EIO;
- }
- client_state.pose_buffer.swap(buffer);
- client_state.mapped_pose_buffer = static_cast<const DvrPoseAsync*>(addr);
- ALOGI(
- "Mapped controller %d pose data translation %f,%f,%f quat %f,%f,%f,%f",
- controller_id, client_state.mapped_pose_buffer[0].position[0],
- client_state.mapped_pose_buffer[0].position[1],
- client_state.mapped_pose_buffer[0].position[2],
- client_state.mapped_pose_buffer[0].orientation[0],
- client_state.mapped_pose_buffer[0].orientation[1],
- client_state.mapped_pose_buffer[0].orientation[2],
- client_state.mapped_pose_buffer[0].orientation[3]);
- return 0;
- }
-
- private:
- friend BASE;
-
- // Set up a channel to the pose service.
- PoseClient()
- : BASE(pdx::default_transport::ClientChannelFactory::Create(
- DVR_POSE_SERVICE_CLIENT)) {
- // TODO(eieio): Cache the pose and make timeout 0 so that the API doesn't
- // block while waiting for the pose service to come back up.
- EnableAutoReconnect(kInfiniteTimeout);
- }
-
- PoseClient(const PoseClient&) = delete;
- PoseClient& operator=(const PoseClient&) = delete;
-
- const DvrVsyncPoseBuffer* GetVsyncBuffer() {
- if (mapped_vsync_pose_buffer_ == nullptr) {
- if (vsync_pose_buffer_ == nullptr) {
- // The constructor tries mapping it so we do not need TryMapping after.
- vsync_pose_buffer_ = std::make_unique<CPUMappedBuffer>(
- DvrGlobalBuffers::kVsyncPoseBuffer, CPUUsageMode::READ_OFTEN);
- } else if (vsync_pose_buffer_->IsMapped() == false) {
- vsync_pose_buffer_->TryMapping();
- }
-
- if (vsync_pose_buffer_->IsMapped()) {
- mapped_vsync_pose_buffer_ =
- static_cast<DvrVsyncPoseBuffer*>(vsync_pose_buffer_->Address());
- }
- }
-
- return mapped_vsync_pose_buffer_;
- }
-
- // The vsync pose buffer if already mapped.
- std::unique_ptr<CPUMappedBuffer> vsync_pose_buffer_;
-
- // The direct sensor pose buffer.
- std::unique_ptr<SensorPoseRing> sensor_pose_buffer_;
-
- const DvrVsyncPoseBuffer* mapped_vsync_pose_buffer_ = nullptr;
-
- struct ControllerClientState {
- std::unique_ptr<ConsumerBuffer> pose_buffer;
- const DvrPoseAsync* mapped_pose_buffer = nullptr;
- };
- ControllerClientState controllers_[MAX_CONTROLLERS];
-};
-
-int dvrPoseClientGetDataReaderHandle(DvrPoseClient* client, uint64_t type,
- ConsumerQueue** queue_out) {
- return PoseClient::FromC(client)->GetTangoReaderHandle(type, queue_out);
-}
-
-} // namespace dvr
-} // namespace android
-
-using android::dvr::PoseClient;
-
-extern "C" {
-
-DvrPoseClient* dvrPoseClientCreate() {
- auto* client = PoseClient::Create().release();
- return reinterpret_cast<DvrPoseClient*>(client);
-}
-
-void dvrPoseClientDestroy(DvrPoseClient* client) {
- delete PoseClient::FromC(client);
-}
-
-int dvrPoseClientGet(DvrPoseClient* client, uint32_t vsync_count,
- DvrPoseAsync* out_pose) {
- return PoseClient::FromC(client)->GetPose(vsync_count, out_pose);
-}
-
-uint32_t dvrPoseClientGetVsyncCount(DvrPoseClient* client) {
- return PoseClient::FromC(client)->GetVsyncCount();
-}
-
-int dvrPoseClientGetController(DvrPoseClient* client, int32_t controller_id,
- uint32_t vsync_count, DvrPoseAsync* out_pose) {
- return PoseClient::FromC(client)->GetControllerPose(controller_id,
- vsync_count, out_pose);
-}
-
-int dvrPoseClientLogController(DvrPoseClient* client, bool enable) {
- return PoseClient::FromC(client)->LogController(enable);
-}
-
-int dvrPoseClientPoll(DvrPoseClient* client, DvrPose* state) {
- return PoseClient::FromC(client)->Poll(state);
-}
-
-int dvrPoseClientFreeze(DvrPoseClient* client, const DvrPose* frozen_state) {
- return PoseClient::FromC(client)->Freeze(*frozen_state);
-}
-
-int dvrPoseClientModeSet(DvrPoseClient* client, DvrPoseMode mode) {
- return PoseClient::FromC(client)->SetMode(mode);
-}
-
-int dvrPoseClientModeGet(DvrPoseClient* client, DvrPoseMode* mode) {
- return PoseClient::FromC(client)->GetMode(mode);
-}
-
-int dvrPoseClientSensorsEnable(DvrPoseClient* client, bool enabled) {
- return PoseClient::FromC(client)->EnableSensors(enabled);
-}
-
-int dvrPoseClientDataCapture(DvrPoseClient* client,
- const DvrPoseDataCaptureRequest* request) {
- return PoseClient::FromC(client)->DataCapture(request);
-}
-
-int dvrPoseClientDataReaderDestroy(DvrPoseClient* client, uint64_t data_type) {
- return PoseClient::FromC(client)->DataReaderDestroy(data_type);
-}
-
-} // extern "C"
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 7619a50..0527c8a 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -937,6 +937,8 @@
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::GLES_1_IN_USE);
}
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
return c;
}
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 7b9782f..aaa8c18 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -82,6 +82,12 @@
mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
}
+void GpuService::setTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount) {
+ mGpuStats->insertTargetStatsArray(appPackageName, driverVersionCode, stats, values, valueCount);
+}
+
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index d7313d1..e7e0cba 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -56,6 +56,9 @@
int64_t driverLoadingTime) override;
void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value) override;
+ void setTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount) override;
void setUpdatableDriverPath(const std::string& driverPath) override;
std::string getUpdatableDriverPath() override;
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d033453..f06a045 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -175,29 +175,83 @@
void GpuStats::insertTargetStats(const std::string& appPackageName,
const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
- const uint64_t /*value*/) {
+ const uint64_t value) {
+ return insertTargetStatsArray(appPackageName, driverVersionCode, stats, &value, 1);
+}
+
+void GpuStats::insertTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount) {
ATRACE_CALL();
const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
std::lock_guard<std::mutex> lock(mLock);
registerStatsdCallbacksIfNeeded();
- if (!mAppStats.count(appStatsKey)) {
+
+ const auto foundApp = mAppStats.find(appStatsKey);
+ if (foundApp == mAppStats.end()) {
return;
}
- switch (stats) {
- case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
- mAppStats[appStatsKey].cpuVulkanInUse = true;
- break;
- case GpuStatsInfo::Stats::FALSE_PREROTATION:
- mAppStats[appStatsKey].falsePrerotation = true;
- break;
- case GpuStatsInfo::Stats::GLES_1_IN_USE:
- mAppStats[appStatsKey].gles1InUse = true;
- break;
- default:
- break;
+ GpuStatsAppInfo& targetAppStats = foundApp->second;
+
+ if (stats == GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION
+ || stats == GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION) {
+ // Handle extension arrays separately as we need to store a unique set of them
+ // in the stats vector. Storing in std::set<> is not efficient for serialization tasks.
+ std::vector<int32_t>& targetVec =
+ (stats == GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION) ?
+ targetAppStats.vulkanInstanceExtensions :
+ targetAppStats.vulkanDeviceExtensions;
+ const bool addAll = (targetVec.size() == 0);
+ targetVec.reserve(valueCount);
+
+ // Add new extensions into the set
+ for(uint32_t i = 0;
+ (i < valueCount) && (targetVec.size() < GpuStatsAppInfo::MAX_NUM_EXTENSIONS);
+ i++) {
+ const int32_t extVal = int32_t(values[i] & 0xFFFFFFFF);
+ if (addAll
+ || std::find(targetVec.cbegin(), targetVec.cend(), extVal) == targetVec.cend()) {
+ targetVec.push_back(extVal);
+ }
+ }
+ }
+ else {
+ // Handle other type of stats info events
+ for(uint32_t i = 0; i < valueCount; i++) {
+ const uint64_t value = values[i];
+ switch (stats) {
+ case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
+ targetAppStats.cpuVulkanInUse = true;
+ break;
+ case GpuStatsInfo::Stats::FALSE_PREROTATION:
+ targetAppStats.falsePrerotation = true;
+ break;
+ case GpuStatsInfo::Stats::GLES_1_IN_USE:
+ targetAppStats.gles1InUse = true;
+ break;
+ case GpuStatsInfo::Stats::CREATED_GLES_CONTEXT:
+ targetAppStats.createdGlesContext = true;
+ break;
+ case GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE:
+ targetAppStats.createdVulkanDevice = true;
+ break;
+ case GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION:
+ targetAppStats.vulkanApiVersion = uint32_t(value & 0xffffffff);
+ break;
+ case GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN:
+ targetAppStats.createdVulkanSwapchain = true;
+ break;
+ case GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED:
+ // Merge all requested feature bits together for this app
+ targetAppStats.vulkanDeviceFeaturesEnabled |= value;
+ break;
+ default:
+ break;
+ }
+ }
}
}
@@ -347,7 +401,14 @@
ele.second.cpuVulkanInUse,
ele.second.falsePrerotation,
ele.second.gles1InUse,
- ele.second.angleInUse);
+ ele.second.angleInUse,
+ ele.second.createdGlesContext,
+ ele.second.createdVulkanDevice,
+ ele.second.createdVulkanSwapchain,
+ ele.second.vulkanApiVersion,
+ ele.second.vulkanDeviceFeaturesEnabled,
+ ele.second.vulkanInstanceExtensions,
+ ele.second.vulkanDeviceExtensions);
}
}
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 2aba651..22c64db 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -41,11 +41,14 @@
// Insert target stats into app stats or potentially global stats as well.
void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value);
+ void insertTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount);
// dumpsys interface
void dump(const Vector<String16>& args, std::string* result);
// This limits the worst case number of loading times tracked.
- static const size_t MAX_NUM_LOADING_TIMES = 50;
+ static const size_t MAX_NUM_LOADING_TIMES = 16;
// Below limits the memory usage of GpuStats to be less than 10KB. This is
// the preferred number for statsd while maintaining nice data quality.
static const size_t MAX_NUM_APP_RECORDS = 100;
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 7ea2288..4ce533f 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -52,6 +52,13 @@
#define DRIVER_LOADING_TIME_2 789
#define DRIVER_LOADING_TIME_3 891
+constexpr uint64_t VULKAN_FEATURES_MASK = 0x600D;
+constexpr uint32_t VULKAN_API_VERSION = 0x400000;
+constexpr int32_t VULKAN_INSTANCE_EXTENSION_1 = 0x1234;
+constexpr int32_t VULKAN_INSTANCE_EXTENSION_2 = 0x8765;
+constexpr int32_t VULKAN_DEVICE_EXTENSION_1 = 0x9012;
+constexpr int32_t VULKAN_DEVICE_EXTENSION_2 = 0x3456;
+
enum InputCommand : int32_t {
DUMP_ALL = 0,
DUMP_GLOBAL = 1,
@@ -218,6 +225,24 @@
GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_GLES_CONTEXT, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ VULKAN_API_VERSION);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ VULKAN_FEATURES_MASK);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_1);
EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
}
@@ -233,10 +258,51 @@
GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_GLES_CONTEXT, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ VULKAN_API_VERSION);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ VULKAN_FEATURES_MASK);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_2);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_2);
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdGlesContext = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanDevice = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanSwapchain = 1"));
+ std::stringstream expectedResult;
+ expectedResult << "vulkanApiVersion = 0x" << std::hex << VULKAN_API_VERSION;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceFeaturesEnabled = 0x" << std::hex << VULKAN_FEATURES_MASK;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanInstanceExtensions: 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceExtensions: 0x" << std::hex << VULKAN_DEVICE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_DEVICE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
}
// Verify we always have the most recently used apps in mAppStats, even when we fill it.
@@ -260,11 +326,52 @@
GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_GLES_CONTEXT, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ VULKAN_API_VERSION);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ VULKAN_FEATURES_MASK);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_1);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_2);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_1);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_2);
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdGlesContext = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanDevice = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanSwapchain = 1"));
+ std::stringstream expectedResult;
+ expectedResult << "vulkanApiVersion = 0x" << std::hex << VULKAN_API_VERSION;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceFeaturesEnabled = 0x" << std::hex << VULKAN_FEATURES_MASK;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanInstanceExtensions: 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceExtensions: 0x" << std::hex << VULKAN_DEVICE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_DEVICE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
}
// mAppStats purges GpuStats::APP_RECORD_HEADROOM apps removed everytime it's filled up.
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index d1de551..b885435 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -60,7 +60,6 @@
name: "libinputflinger_sources",
srcs: [
"InputCommonConverter.cpp",
- "InputManager.cpp",
"InputProcessor.cpp",
"PreferStylusOverTouchBlocker.cpp",
"UnwantedInteractionBlocker.cpp",
@@ -116,6 +115,10 @@
"inputflinger_defaults",
"libinputflinger_defaults",
],
+ srcs: [
+ "InputManager.cpp",
+ // other sources are added via "defaults"
+ ],
cflags: [
// TODO(b/23084678): Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 4a0f2ec..3d7242e 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -15,6 +15,9 @@
"name": "inputflinger_tests"
},
{
+ "name": "libchrome-gestures_test"
+ },
+ {
"name": "libpalmrejection_test"
},
{
@@ -41,6 +44,7 @@
"include-filter": "android.view.cts.input",
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
+ "include-filter": "android.view.cts.TooltipTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
@@ -128,6 +132,7 @@
{
"include-filter": "android.view.cts.MotionEventTest",
"include-filter": "android.view.cts.PointerCaptureTest",
+ "include-filter": "android.view.cts.TooltipTest",
"include-filter": "android.view.cts.VerifyInputEventTest"
}
]
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index d210e9e..512cb6e 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -24,7 +24,7 @@
/* Specifies which events are to be canceled and why. */
struct CancelationOptions {
- enum Mode {
+ enum class Mode {
CANCEL_ALL_EVENTS = 0,
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index ec9701a..7bbfb95 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -166,7 +166,7 @@
repeatCount(repeatCount),
downTime(downTime),
syntheticRepeat(false),
- interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
+ interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN),
interceptKeyWakeupTime(0) {}
KeyEntry::~KeyEntry() {}
@@ -189,7 +189,7 @@
dispatchInProgress = false;
syntheticRepeat = false;
- interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
interceptKeyWakeupTime = 0;
}
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index f801912..3799814 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -140,11 +140,11 @@
bool syntheticRepeat; // set to true for synthetic key repeats
- enum InterceptKeyResult {
- INTERCEPT_KEY_RESULT_UNKNOWN,
- INTERCEPT_KEY_RESULT_SKIP,
- INTERCEPT_KEY_RESULT_CONTINUE,
- INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
+ enum class InterceptKeyResult {
+ UNKNOWN,
+ SKIP,
+ CONTINUE,
+ TRY_AGAIN_LATER,
};
InterceptKeyResult interceptKeyResult; // set based on the interception result
nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 87a4ff4..4aac377 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -60,7 +60,6 @@
using android::gui::TouchOcclusionMode;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
-using android::os::IInputConstants;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
@@ -149,21 +148,23 @@
}
bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
+ switch (MotionEvent::getActionMasked(action)) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
+ return pointerCount == 1;
case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return pointerCount >= 1;
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_SCROLL:
return true;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
- int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && index < pointerCount;
+ const int32_t index = MotionEvent::getActionIndex(action);
+ return index >= 0 && index < pointerCount && pointerCount > 1;
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
@@ -1033,8 +1034,8 @@
KeyEntry& pendingKey = static_cast<KeyEntry&>(*mPendingEvent);
if (pendingKey.keyCode == keyEntry.keyCode &&
pendingKey.interceptKeyResult ==
- KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
- pendingKey.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
+ pendingKey.interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
pendingKey.interceptKeyWakeupTime = 0;
needWake = true;
}
@@ -1169,17 +1170,18 @@
switch (entry.type) {
case EventEntry::Type::KEY: {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+ reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
break;
@@ -1334,7 +1336,7 @@
resetKeyRepeatLocked();
}
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset");
options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
return true;
@@ -1539,19 +1541,19 @@
}
// Handle case where the policy asked us to try again later last time.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
+ if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) {
if (currentTime < entry->interceptKeyWakeupTime) {
if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
*nextWakeupTime = entry->interceptKeyWakeupTime;
}
return false; // wait until next wakeup
}
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN;
entry->interceptKeyWakeupTime = 0;
}
// Give the policy a chance to intercept the key.
- if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
sp<IBinder> focusedWindowToken =
mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
@@ -1562,9 +1564,9 @@
postCommandLocked(std::move(command));
return false; // wait for the command to run
} else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
}
- } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+ } else if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::SKIP) {
if (*dropReason == DropReason::NOT_DROPPED) {
*dropReason = DropReason::POLICY;
}
@@ -1723,9 +1725,9 @@
return true;
}
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
- CancelationOptions::Mode mode(isPointerEvent
- ? CancelationOptions::CANCEL_POINTER_EVENTS
- : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+ CancelationOptions::Mode mode(
+ isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
+ : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
CancelationOptions options(mode, "input event injection failed");
synthesizeCancelationEventsForMonitorsLocked(options);
return true;
@@ -1736,7 +1738,7 @@
// Dispatch the motion.
if (conflictingPointerActions) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"conflicting pointer actions");
synthesizeCancelationEventsForAllConnectionsLocked(options);
}
@@ -1769,15 +1771,16 @@
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32
", policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
- entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
- entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
- entry.xPrecision, entry.yPrecision, entry.downTime);
+ prefix, entry.eventTime, entry.deviceId,
+ inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags,
+ MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags,
+ entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
+ entry.yPrecision, entry.downTime);
for (uint32_t i = 0; i < entry.pointerCount; i++) {
ALOGD(" Pointer %d: id=%d, toolType=%d, "
@@ -1836,7 +1839,7 @@
ALOGW("Canceling events for %s because it is unresponsive",
connection->inputChannel->getName().c_str());
if (connection->status == Connection::Status::NORMAL) {
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS,
"application not responding");
synthesizeCancelationEventsForConnectionLocked(connection, options);
}
@@ -2095,7 +2098,7 @@
bool isSplit = shouldSplitTouch(tempTouchState, entry);
const bool switchedDevice = (oldState != nullptr) &&
- (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source);
+ (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2103,6 +2106,7 @@
const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) {
@@ -2305,6 +2309,20 @@
entry.eventTime);
}
}
+
+ // Update the pointerIds for non-splittable when it received pointer down.
+ if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // If no split, we suppose all touched windows should receive pointer down.
+ const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
+ TouchedWindow& touchedWindow = tempTouchState.windows[i];
+ // Ignore drag window for it should just track one pointer.
+ if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+ continue;
+ }
+ touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
+ }
+ }
}
// Update dispatching for hover enter and exit.
@@ -2414,12 +2432,14 @@
if (info->displayId == displayId &&
windowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::IS_WALLPAPER)) {
+ BitSet32 pointerIds;
+ pointerIds.markBit(entry.pointerProperties[0].id);
tempTouchState.addOrUpdateWindow(windowHandle,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::
WINDOW_IS_PARTIALLY_OBSCURED |
InputTarget::Flags::DISPATCH_AS_IS,
- BitSet32(0), entry.eventTime);
+ pointerIds, entry.eventTime);
}
}
}
@@ -2479,17 +2499,6 @@
}
i += 1;
}
- } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
- // If no split, we suppose all touched windows should receive pointer down.
- const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
- TouchedWindow& touchedWindow = tempTouchState.windows[i];
- // Ignore drag window for it should just track one pointer.
- if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
- continue;
- }
- touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
- }
}
// Save changes unless the action was scroll in which case the temporary touch
@@ -2502,6 +2511,10 @@
}
}
+ if (tempTouchState.windows.empty()) {
+ mTouchStatesByDisplay.erase(displayId);
+ }
+
// Update hover state.
mLastHoverWindowHandle = newHoverWindowHandle;
@@ -2985,6 +2998,8 @@
connection->getInputChannelName().c_str(), eventEntry->id);
ATRACE_NAME(message.c_str());
}
+ LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK),
+ "No dispatch flags are set for %s", eventEntry->getDescription().c_str());
const bool wasEmpty = connection->outboundQueue.empty();
@@ -4043,10 +4058,9 @@
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
- if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
- args->pointerProperties)) {
- return;
- }
+ LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties),
+ "Invalid event: %s", args->dump().c_str());
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4795,10 +4809,13 @@
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
- if (mLastHoverWindowHandle &&
- std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
- windowHandles.end()) {
- mLastHoverWindowHandle = nullptr;
+ if (mLastHoverWindowHandle) {
+ const WindowInfo* lastHoverWindowInfo = mLastHoverWindowHandle->getInfo();
+ if (lastHoverWindowInfo->displayId == displayId &&
+ std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
+ windowHandles.end()) {
+ mLastHoverWindowHandle = nullptr;
+ }
}
std::optional<FocusResolver::FocusChanges> changes =
@@ -4821,7 +4838,7 @@
std::shared_ptr<InputChannel> touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
// Since we are about to drop the touch, cancel the events for the wallpaper as
@@ -4867,7 +4884,7 @@
std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(newWindowHandle->getToken());
if (inputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"touched window's orientation changed");
synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
}
@@ -4948,7 +4965,7 @@
getInputChannelLocked(oldFocusedWindowToken);
if (inputChannel != nullptr) {
CancelationOptions
- options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"The display which contains this window no longer has focus.");
options.displayId = ADISPLAY_ID_NONE;
synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
@@ -4968,10 +4985,6 @@
}
}
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -5002,10 +5015,6 @@
} else {
changed = false;
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
if (changed) {
@@ -5175,15 +5184,11 @@
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions
- options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"transferring touch focus from this window to another window");
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection);
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -5249,7 +5254,7 @@
ALOGD("Resetting and dropping all events (%s).", reason);
}
- CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
resetKeyRepeatLocked();
@@ -5430,9 +5435,7 @@
if (!mReplacedKeys.empty()) {
dump += INDENT "ReplacedKeys:\n";
- for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
- const KeyReplacement& replacement = pair.first;
- int32_t newKeyCode = pair.second;
+ for (const auto& [replacement, newKeyCode] : mReplacedKeys) {
dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
replacement.keyCode, replacement.deviceId, newKeyCode);
}
@@ -5676,7 +5679,7 @@
TouchState& state = *statePtr;
TouchedWindow& window = *windowPtr;
// Send cancel events to all the input channels we're stealing from.
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"input channel stole pointer stream");
options.deviceId = state.deviceId;
options.displayId = displayId;
@@ -5962,11 +5965,11 @@
} // acquire lock
if (delay < 0) {
- entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+ entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP;
} else if (delay == 0) {
- entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
} else {
- entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+ entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER;
entry.interceptKeyWakeupTime = now() + delay;
}
}
@@ -6076,7 +6079,7 @@
// Cancel the fallback key.
if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"application handled the original non-fallback key "
"or is no longer a foreground target, "
"canceling previously dispatched fallback key");
@@ -6153,7 +6156,7 @@
}
}
- CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"canceling fallback, policy no longer desires it");
options.keyCode = fallbackKeyCode;
synthesizeCancelationEventsForConnectionLocked(connection, options);
@@ -6306,7 +6309,7 @@
if (changes.oldFocus) {
std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
if (focusedInputChannel) {
- CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
@@ -6446,7 +6449,7 @@
{
std::scoped_lock _l(mLock);
ALOGD("Canceling all ongoing pointer gestures on all displays.");
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"cancel current touch");
synthesizeCancelationEventsForAllConnectionsLocked(options);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 047c628..563868d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -500,10 +500,10 @@
}
switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+ case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
+ case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
return true;
- case CancelationOptions::CANCEL_FALLBACK_EVENTS:
+ case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
default:
return false;
@@ -521,11 +521,11 @@
}
switch (options.mode) {
- case CancelationOptions::CANCEL_ALL_EVENTS:
+ case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
return true;
- case CancelationOptions::CANCEL_POINTER_EVENTS:
+ case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
- case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
+ case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
default:
return false;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index ee7da93..c21af9e 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -34,8 +34,7 @@
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> eventTime) {
- for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows[i];
+ for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
@@ -148,7 +147,8 @@
std::string TouchState::dump() const {
std::string out;
- out += StringPrintf("deviceId=%d, source=0x%08x\n", deviceId, source);
+ out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
+ inputEventSourceToString(source).c_str());
if (!windows.empty()) {
out += " Windows:\n";
for (size_t i = 0; i < windows.size(); i++) {
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 484b0d3..76dce63 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -125,9 +125,12 @@
/**
* Set the touch mode state.
- * Touch mode is a global state that apps may enter / exit based on specific
- * user interactions with input devices.
- * If true, the device is in touch mode.
+ * Touch mode is a per display state that apps may enter / exit based on specific user
+ * interactions with input devices. If <code>inTouchMode</code> is set to true, the display
+ * identified by <code>displayId</code> will be changed to touch mode. Performs a permission
+ * check if hasPermission is set to false.
+ *
+ * This method also enqueues a a TouchModeEntry message for dispatching.
*
* Returns true when changing touch mode state.
*/
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3b0f2ac..d2c940f 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -23,6 +23,7 @@
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
#include <stddef.h>
+#include <ui/Rotation.h>
#include <unistd.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -87,6 +88,9 @@
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
int32_t sw) = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/* Toggle Caps Lock */
@@ -187,6 +191,12 @@
// The set of disabled input devices (disabledDevices) has changed.
CHANGE_ENABLED_STATE = 1 << 9,
+ // The device type has been updated.
+ CHANGE_DEVICE_TYPE = 1 << 10,
+
+ // The keyboard layout association has changed.
+ CHANGE_KEYBOARD_LAYOUT_ASSOCIATION = 1 << 11,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -204,10 +214,18 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, uint8_t> portAssociations;
- // The associations between input device names and display unique ids.
+ // The associations between input device physical port locations and display unique ids.
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, std::string> uniqueIdAssociations;
+ // The associations between input device ports device types.
+ // This is used to determine which device type and source should be tied to which InputDevice.
+ std::unordered_map<std::string, std::string> deviceTypeAssociations;
+
+ // The map from the input device physical port location to the input device layout info.
+ // Can be used to determine the layout of the keyboard device.
+ std::unordered_map<std::string, KeyboardLayoutInfo> keyboardLayoutAssociations;
+
// The suggested display ID to show the cursor.
int32_t defaultPointerDisplayId;
@@ -322,7 +340,6 @@
std::optional<DisplayViewport> getDisplayViewportById(int32_t displayId) const;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
-
void dump(std::string& dump) const;
void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
@@ -395,7 +412,7 @@
/* Gets the affine calibration associated with the specified device. */
virtual TouchAffineTransformation getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) = 0;
/* Notifies the input reader policy that a stylus gesture has started. */
virtual void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) = 0;
};
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index f28dbf3..c46f905 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -116,6 +116,8 @@
NotifyMotionArgs(const NotifyMotionArgs& other);
+ NotifyMotionArgs& operator=(const android::NotifyMotionArgs&) = default;
+
bool operator==(const NotifyMotionArgs& rhs) const;
std::string dump() const;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 24168a1..f3b680b 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -29,6 +29,7 @@
"include",
"mapper",
"mapper/accumulator",
+ "mapper/gestures",
],
}
@@ -50,7 +51,9 @@
"mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
"mapper/SwitchInputMapper.cpp",
+ "mapper/TouchCursorInputMapperCommon.cpp",
"mapper/TouchInputMapper.cpp",
+ "mapper/TouchpadInputMapper.cpp",
"mapper/VibratorInputMapper.cpp",
"mapper/accumulator/CursorButtonAccumulator.cpp",
"mapper/accumulator/CursorScrollAccumulator.cpp",
@@ -58,6 +61,9 @@
"mapper/accumulator/MultiTouchMotionAccumulator.cpp",
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
"mapper/accumulator/TouchButtonAccumulator.cpp",
+ "mapper/gestures/GestureConverter.cpp",
+ "mapper/gestures/GesturesLogging.cpp",
+ "mapper/gestures/HardwareStateConverter.cpp",
],
}
@@ -69,22 +75,25 @@
"libcap",
"libcrypto",
"libcutils",
+ "libjsoncpp",
"liblog",
+ "libPlatformProperties",
"libstatslog",
"libutils",
],
static_libs: [
"libc++fs",
+ "libchrome-gestures",
"libui-types",
],
header_libs: [
"libbatteryservice_headers",
+ "libchrome-gestures_headers",
"libinputreader_headers",
],
target: {
android: {
shared_libs: [
- "libPlatformProperties",
"libinput",
],
},
@@ -97,6 +106,25 @@
},
}
+cc_library_static {
+ name: "libinputreader_static",
+ defaults: [
+ "inputflinger_defaults",
+ "libinputreader_defaults",
+ ],
+ shared_libs: [
+ "libinputflinger_base",
+ ],
+ export_header_lib_headers: [
+ "libbatteryservice_headers",
+ "libchrome-gestures_headers",
+ "libinputreader_headers",
+ ],
+ whole_static_libs: [
+ "libchrome-gestures",
+ ],
+}
+
cc_library_shared {
name: "libinputreader",
host_supported: true,
@@ -111,6 +139,7 @@
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
"libinputflinger_base",
+ "libjsoncpp",
],
export_header_lib_headers: [
"libinputreader_headers",
@@ -125,5 +154,6 @@
},
static_libs: [
"libc++fs",
+ "libchrome-gestures",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0aaef53..e26bc8c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -952,15 +952,19 @@
device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode);
switch (mapKeyRes) {
case OK:
- return outKeyCode;
+ break;
case NAME_NOT_FOUND:
// key character map doesn't re-map this scanCode, hence the keyCode remains the same
- return locationKeyCode;
+ outKeyCode = locationKeyCode;
+ break;
default:
ALOGW("Failed to get key code for key location: Key character map returned error %s",
statusToString(mapKeyRes).c_str());
- return AKEYCODE_UNKNOWN;
+ outKeyCode = AKEYCODE_UNKNOWN;
+ break;
}
+ // Remap if there is a Key remapping added to the KCM and return the remapped key
+ return device->getKeyCharacterMap()->applyKeyRemapping(outKeyCode);
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
@@ -1023,6 +1027,18 @@
return false;
}
+void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ return;
+ }
+ const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+ if (kcm) {
+ kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
std::scoped_lock _l(mLock);
@@ -1048,7 +1064,13 @@
if (status == NO_ERROR) {
if (kcm) {
- kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
+ // Remap keys based on user-defined key remappings and key behavior defined in the
+ // corresponding kcm file
+ *outKeycode = kcm->applyKeyRemapping(*outKeycode);
+
+ // Remap keys based on Key behavior defined in KCM file
+ std::tie(*outKeycode, *outMetaState) =
+ kcm->applyKeyBehavior(*outKeycode, metaState);
} else {
*outMetaState = metaState;
}
@@ -2215,6 +2237,10 @@
// a touch screen.
if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
+ if (device->propBitmask.test(INPUT_PROP_POINTER) &&
+ !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
+ device->classes |= InputDeviceClass::TOUCHPAD;
+ }
}
// Is this an old style single-touch driver?
} else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index e6ab872..13f40ee 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
+#include <android/sysprop/InputProperties.sysprop.h>
#include <ftl/flags.h>
#include "CursorInputMapper.h"
@@ -33,6 +34,7 @@
#include "SensorInputMapper.h"
#include "SingleTouchInputMapper.h"
#include "SwitchInputMapper.h"
+#include "TouchpadInputMapper.h"
#include "VibratorInputMapper.h"
using android::hardware::input::InputDeviceCountryCode;
@@ -208,7 +210,12 @@
}
// Touchscreens and touchpad devices.
- if (classes.test(InputDeviceClass::TOUCH_MT)) {
+ static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
+ sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+ if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
+ classes.test(InputDeviceClass::TOUCH_MT)) {
+ mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
+ } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
} else if (classes.test(InputDeviceClass::TOUCH)) {
mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
@@ -284,6 +291,9 @@
context.getConfiguration(&configuration);
mConfiguration.addAll(&configuration);
});
+
+ mAssociatedDeviceType =
+ getValueByKey(config->deviceTypeAssociations, mIdentifier.location);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
@@ -618,6 +628,12 @@
});
}
+void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
+ context.addKeyRemapping(fromKeyCode, toKeyCode);
+ });
+}
+
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f04a646..57f679c 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -650,6 +650,15 @@
return result;
}
+void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device != nullptr) {
+ device->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index e107d88..d2a7ced 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -22,6 +22,8 @@
#include <log/log.h>
#include <log/log_event_list.h>
+#include <unordered_map>
+
namespace android {
/**
* Log debug messages for each raw event received from the EventHub.
@@ -113,4 +115,14 @@
return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0;
}
+template <typename K, typename V>
+static inline std::optional<V> getValueByKey(const std::unordered_map<K, V>& map, K key) {
+ auto it = map.find(key);
+ std::optional<V> value = std::nullopt;
+ if (it != map.end()) {
+ value = it->second;
+ }
+ return value;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8e5f15f..8a844b2 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -94,7 +94,7 @@
/* The input device is a cursor device such as a trackball or mouse. */
CURSOR = 0x00000008,
- /* The input device is a multi-touch touchscreen. */
+ /* The input device is a multi-touch touchscreen or touchpad. */
TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
@@ -130,6 +130,9 @@
/* The input device has sysfs controllable lights */
LIGHT = 0x00008000,
+ /* The input device is a touchpad, requiring an on-screen cursor. */
+ TOUCHPAD = 0x00010000,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
VIRTUAL = 0x40000000,
@@ -259,6 +262,9 @@
virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const = 0;
@@ -457,6 +463,9 @@
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const override final;
+
status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 439123b..b173618 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -54,6 +54,7 @@
inline std::optional<std::string> getBluetoothAddress() const {
return mIdentifier.bluetoothAddress;
}
+ inline const std::string getLocation() const { return mIdentifier.location; }
inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
inline uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
@@ -65,6 +66,9 @@
inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
return mAssociatedDisplayUniqueId;
}
+ inline std::optional<std::string> getDeviceTypeAssociation() const {
+ return mAssociatedDeviceType;
+ }
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mAssociatedViewport;
}
@@ -114,6 +118,8 @@
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
@@ -178,6 +184,7 @@
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
std::optional<std::string> mAssociatedDisplayUniqueId;
+ std::optional<std::string> mAssociatedDeviceType;
std::optional<DisplayViewport> mAssociatedViewport;
bool mHasMic;
bool mDropUntilNextSync;
@@ -278,6 +285,10 @@
inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
+ inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
+ mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+ }
+
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
@@ -393,8 +404,9 @@
inline status_t enableDevice() { return mEventHub->enableDevice(mId); }
inline status_t disableDevice() { return mEventHub->disableDevice(mId); }
- inline const std::string getName() { return mDevice.getName(); }
+ inline const std::string getName() const { return mDevice.getName(); }
inline const std::string getDescriptor() { return mDevice.getDescriptor(); }
+ inline const std::string getLocation() { return mDevice.getLocation(); }
inline bool isExternal() { return mDevice.isExternal(); }
inline std::optional<uint8_t> getAssociatedDisplayPort() const {
return mDevice.getAssociatedDisplayPort();
@@ -402,6 +414,9 @@
inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
return mDevice.getAssociatedDisplayUniqueId();
}
+ inline std::optional<std::string> getDeviceTypeAssociation() const {
+ return mDevice.getDeviceTypeAssociation();
+ }
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mDevice.getAssociatedViewport();
}
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f2503a..e9c989a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -68,6 +68,8 @@
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
void toggleCapsLockState(int32_t deviceId) override;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index a1a2af9..13e4d0c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -227,7 +227,7 @@
mDisplayId = mPointerController->getDisplayId();
}
- mOrientation = DISPLAY_ORIENTATION_0;
+ mOrientation = ui::ROTATION_0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
// InputReader works in the un-rotated display coordinate space, so we don't need to do
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 20746e5..939cceb 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -22,6 +22,7 @@
#include <PointerControllerInterface.h>
#include <input/VelocityControl.h>
+#include <ui/Rotation.h>
namespace android {
@@ -115,7 +116,7 @@
// ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
// std::nullopt), all events will be ignored.
std::optional<int32_t> mDisplayId;
- int32_t mOrientation;
+ ui::Rotation mOrientation;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index da9413e..6f01449 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -20,11 +20,13 @@
#include "KeyboardInputMapper.h"
+#include <ui/Rotation.h>
+
namespace android {
// --- Static Definitions ---
-static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+static int32_t rotateKeyCode(int32_t keyCode, ui::Rotation orientation) {
static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
// key codes enumerated counter-clockwise with the original (unrotated) key first
// no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
@@ -42,11 +44,10 @@
AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
};
- LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation);
- if (orientation != DISPLAY_ORIENTATION_0) {
+ if (orientation != ui::ROTATION_0) {
for (const auto& rotation : KEYCODE_ROTATION_MAP) {
- if (rotation[DISPLAY_ORIENTATION_0] == keyCode) {
- return rotation[orientation];
+ if (rotation[static_cast<size_t>(ui::ROTATION_0)] == keyCode) {
+ return rotation[static_cast<size_t>(orientation)];
}
}
}
@@ -100,11 +101,11 @@
return mSource;
}
-int32_t KeyboardInputMapper::getOrientation() {
+ui::Rotation KeyboardInputMapper::getOrientation() {
if (mViewport) {
return mViewport->orientation;
}
- return DISPLAY_ORIENTATION_0;
+ return ui::ROTATION_0;
}
int32_t KeyboardInputMapper::getDisplayId() {
@@ -119,6 +120,10 @@
info->setKeyboardType(mKeyboardType);
info->setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
+
+ if (mKeyboardLayoutInfo) {
+ info->setKeyboardLayoutInfo(*mKeyboardLayoutInfo);
+ }
}
void KeyboardInputMapper::dump(std::string& dump) {
@@ -128,6 +133,12 @@
dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation());
dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
+ dump += INDENT3 "KeyboardLayoutInfo: ";
+ if (mKeyboardLayoutInfo) {
+ dump += mKeyboardLayoutInfo->languageTag + ", " + mKeyboardLayoutInfo->layoutType + "\n";
+ } else {
+ dump += "<not set>\n";
+ }
}
std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
@@ -157,6 +168,12 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mViewport = findViewport(config);
}
+
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION)) {
+ mKeyboardLayoutInfo =
+ getValueByKey(config->keyboardLayoutAssociations, getDeviceContext().getLocation());
+ }
+
return out;
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 11d5ad2..da5b8ee 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -58,6 +58,7 @@
uint32_t mSource{};
int32_t mKeyboardType{};
+ std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
std::vector<KeyDown> mKeyDowns{}; // keys that are down
int32_t mMetaState{};
@@ -82,7 +83,7 @@
void configureParameters();
void dumpParameters(std::string& dump) const;
- int32_t getOrientation();
+ ui::Rotation getOrientation();
int32_t getDisplayId();
[[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index ccff353..633efc6 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -16,10 +16,8 @@
#include "../Macros.h"
-#include "MultiTouchInputMapper.h"
-#if defined(__ANDROID__)
#include <android/sysprop/InputProperties.sysprop.h>
-#endif
+#include "MultiTouchInputMapper.h"
namespace android {
@@ -119,6 +117,18 @@
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
}
+ } else if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS && !mStylusMtToolSeen) {
+ mStylusMtToolSeen = true;
+ // The multi-touch device produced a stylus event with MT_TOOL_PEN. Dynamically
+ // re-configure this input device so that we add SOURCE_STYLUS if we haven't already.
+ // This is to cover the case where we cannot reliably detect whether a multi-touch
+ // device will ever produce stylus events when it is initially being configured.
+ if (!isFromSource(mSource, AINPUT_SOURCE_STYLUS)) {
+ // Add the stylus source immediately so that it is included in any events generated
+ // before we have a chance to re-configure the device.
+ mSource |= AINPUT_SOURCE_STYLUS;
+ bumpGeneration();
+ }
}
if (shouldSimulateStylusWithTouch() &&
outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
@@ -200,17 +210,13 @@
}
bool MultiTouchInputMapper::hasStylus() const {
- return mTouchButtonAccumulator.hasStylus() || shouldSimulateStylusWithTouch();
+ return mStylusMtToolSeen || mTouchButtonAccumulator.hasStylus() ||
+ shouldSimulateStylusWithTouch();
}
bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
static const bool SIMULATE_STYLUS_WITH_TOUCH =
-#if defined(__ANDROID__)
sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
-#else
- // Disable this developer feature where sysproperties are not available
- false;
-#endif
return SIMULATE_STYLUS_WITH_TOUCH &&
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index ddf9e80..5f8bccf 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -50,6 +50,8 @@
// Specifies the pointer id bits that are in use, and their associated tracking id.
BitSet32 mPointerIdBits;
int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+
+ bool mStylusMtToolSeen{false};
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 29a1bda..19a79d7 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -25,7 +25,7 @@
namespace android {
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext)
- : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) {
+ : InputMapper(deviceContext), mOrientation(ui::ROTATION_0) {
mSource = AINPUT_SOURCE_ROTARY_ENCODER;
}
@@ -73,7 +73,7 @@
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
- mOrientation = DISPLAY_ORIENTATION_0;
+ mOrientation = ui::ROTATION_0;
}
}
return out;
@@ -97,35 +97,34 @@
std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- PointerCoords pointerCoords;
- pointerCoords.clear();
-
- PointerProperties pointerProperties;
- pointerProperties.clear();
- pointerProperties.id = 0;
- pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
bool scrolled = scroll != 0;
- // This is not a pointer, so it's not associated with a display.
- int32_t displayId = ADISPLAY_ID_NONE;
-
- // Moving the rotary encoder should wake the device (if specified).
- uint32_t policyFlags = 0;
- if (scrolled && getDeviceContext().isExternal()) {
- policyFlags |= POLICY_FLAG_WAKE;
- }
-
- if (mOrientation == DISPLAY_ORIENTATION_180) {
- scroll = -scroll;
- }
-
// Send motion event.
if (scrolled) {
int32_t metaState = getContext()->getGlobalMetaState();
+ // This is not a pointer, so it's not associated with a display.
+ int32_t displayId = ADISPLAY_ID_NONE;
+
+ if (mOrientation == ui::ROTATION_180) {
+ scroll = -scroll;
+ }
+
+ PointerCoords pointerCoords;
+ pointerCoords.clear();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
+ PointerProperties pointerProperties;
+ pointerProperties.clear();
+ pointerProperties.id = 0;
+ pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
+
+ uint32_t policyFlags = 0;
+ if (getDeviceContext().isExternal()) {
+ policyFlags |= POLICY_FLAG_WAKE;
+ }
+
out.push_back(
NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index f4352e7..cb5fd88 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ui/Rotation.h>
+
#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
@@ -40,7 +42,7 @@
int32_t mSource;
float mScalingFactor;
- int32_t mOrientation;
+ ui::Rotation mOrientation;
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
};
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
new file mode 100644
index 0000000..c12e95d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#include <input/DisplayViewport.h>
+#include <stdint.h>
+#include <ui/Rotation.h>
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+namespace {
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+ std::list<NotifyArgs> out;
+ if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
+ (currentButtonState & buttonState)) ||
+ (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
+ !(currentButtonState & buttonState))) {
+ out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+ displayId, policyFlags, action, 0, keyCode, 0,
+ context->getGlobalMetaState(), when));
+ }
+ return out;
+}
+
+} // namespace
+
+ui::Rotation getInverseRotation(ui::Rotation orientation) {
+ switch (orientation) {
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
+ default:
+ return orientation;
+ }
+}
+
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) {
+ float temp;
+ switch (orientation) {
+ case ui::ROTATION_90:
+ temp = *deltaX;
+ *deltaX = *deltaY;
+ *deltaY = -temp;
+ break;
+
+ case ui::ROTATION_180:
+ *deltaX = -*deltaX;
+ *deltaY = -*deltaY;
+ break;
+
+ case ui::ROTATION_270:
+ temp = *deltaX;
+ *deltaX = -*deltaY;
+ *deltaY = temp;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool isPointerDown(int32_t buttonState) {
+ return buttonState &
+ (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState) {
+ std::list<NotifyArgs> out;
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+ return out;
+}
+
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+ const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+ nsecs_t lastEventTime) {
+ if (identifier.bus != BUS_BLUETOOTH) {
+ return {currentEventTime, readTime};
+ }
+
+ // Assume the fastest rate at which a Bluetooth touch device can report input events is one
+ // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
+ // will be separated by at least this amount.
+ constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+ // We define a maximum smoothing time delta so that we don't generate events too far into the
+ // future.
+ constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+ const nsecs_t smoothenedEventTime =
+ std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
+ currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+ // If we are modifying the event time, treat this event as a synthetically generated event for
+ // latency tracking purposes and use the event time as the read time (zero read latency).
+ const nsecs_t smoothenedReadTime =
+ smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
+ return {smoothenedEventTime, smoothenedReadTime};
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index d8a4d34..3023e68 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -18,6 +18,7 @@
#include <input/DisplayViewport.h>
#include <stdint.h>
+#include <ui/Rotation.h>
#include "EventHub.h"
#include "InputListener.h"
@@ -25,81 +26,18 @@
namespace android {
-// --- Static Definitions ---
+ui::Rotation getInverseRotation(ui::Rotation orientation);
-static int32_t getInverseRotation(int32_t orientation) {
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- return DISPLAY_ORIENTATION_270;
- case DISPLAY_ORIENTATION_270:
- return DISPLAY_ORIENTATION_90;
- default:
- return orientation;
- }
-}
-
-static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) {
- float temp;
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- temp = *deltaX;
- *deltaX = *deltaY;
- *deltaY = -temp;
- break;
-
- case DISPLAY_ORIENTATION_180:
- *deltaX = -*deltaX;
- *deltaY = -*deltaY;
- break;
-
- case DISPLAY_ORIENTATION_270:
- temp = *deltaX;
- *deltaX = -*deltaY;
- *deltaY = temp;
- break;
-
- default:
- break;
- }
-}
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY);
// Returns true if the pointer should be reported as being down given the specified
// button states. This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
- return buttonState &
- (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY);
-}
+bool isPointerDown(int32_t buttonState);
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKey(
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
- std::list<NotifyArgs> out;
- if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
- (currentButtonState & buttonState)) ||
- (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
- !(currentButtonState & buttonState))) {
- out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
- displayId, policyFlags, action, 0, keyCode, 0,
- context->getGlobalMetaState(), when));
- }
- return out;
-}
-
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKeys(
- InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t lastButtonState, int32_t currentButtonState) {
- std::list<NotifyArgs> out;
- out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
- policyFlags, lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
- policyFlags, lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
- return out;
-}
+ int32_t lastButtonState, int32_t currentButtonState);
// For devices connected over Bluetooth, although they may produce events at a consistent rate,
// the events might end up reaching Android in a "batched" manner through the Bluetooth
@@ -110,28 +48,8 @@
// coordinates result in extremely large instantaneous velocities, which can negatively impact
// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps
// differ by at least a minimum delta value.
-static std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
- nsecs_t lastEventTime) {
- if (identifier.bus != BUS_BLUETOOTH) {
- return {currentEventTime, readTime};
- }
-
- // Assume the fastest rate at which a Bluetooth touch device can report input events is one
- // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
- // will be separated by at least this amount.
- constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
- // We define a maximum smoothing time delta so that we don't generate events too far into the
- // future.
- constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
- const nsecs_t smoothenedEventTime =
- std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
- currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
- // If we are modifying the event time, treat this event as a synthetically generated event for
- // latency tracking purposes and use the event time as the read time (zero read latency).
- const nsecs_t smoothenedReadTime =
- smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
- return {smoothenedEventTime, smoothenedReadTime};
-}
+ nsecs_t lastEventTime);
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index bc55fe5..160f9eb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -27,6 +27,7 @@
#include "CursorScrollAccumulator.h"
#include "TouchButtonAccumulator.h"
#include "TouchCursorInputMapperCommon.h"
+#include "ui/Rotation.h"
namespace android {
@@ -42,6 +43,19 @@
static const DisplayViewport kUninitializedViewport;
+static std::string toString(const Rect& rect) {
+ return base::StringPrintf("Rect{%d, %d, %d, %d}", rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static std::string toString(const ui::Size& size) {
+ return base::StringPrintf("%dx%d", size.width, size.height);
+}
+
+static bool isPointInRect(const Rect& rect, int32_t x, int32_t y) {
+ // Consider all four sides as "inclusive".
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
+}
+
template <typename T>
inline static void swap(T& a, T& b) {
T temp = a;
@@ -67,6 +81,31 @@
return value >= 8 ? value - 16 : value;
}
+static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo(
+ const DisplayViewport& viewport, ui::Rotation naturalOrientation) {
+ ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight};
+ if (naturalOrientation == ui::ROTATION_90 || naturalOrientation == ui::ROTATION_270) {
+ std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height);
+ }
+
+ ui::Transform rotate(ui::Transform::toRotationFlags(naturalOrientation),
+ rotatedDisplaySize.width, rotatedDisplaySize.height);
+
+ Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight,
+ viewport.physicalBottom};
+ physicalFrame = rotate.transform(physicalFrame);
+
+ LOG_ALWAYS_FATAL_IF(!physicalFrame.isValid());
+ if (physicalFrame.isEmpty()) {
+ ALOGE("Viewport is not set properly: %s", viewport.toString().c_str());
+ physicalFrame.right =
+ physicalFrame.left + (physicalFrame.width() == 0 ? 1 : physicalFrame.width());
+ physicalFrame.bottom =
+ physicalFrame.top + (physicalFrame.height() == 0 ? 1 : physicalFrame.height());
+ }
+ return {rotatedDisplaySize, physicalFrame};
+}
+
// --- RawPointerData ---
void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
@@ -93,13 +132,7 @@
mTouchButtonAccumulator(deviceContext),
mSource(0),
mDeviceMode(DeviceMode::DISABLED),
- mDisplayWidth(-1),
- mDisplayHeight(-1),
- mPhysicalWidth(-1),
- mPhysicalHeight(-1),
- mPhysicalLeft(0),
- mPhysicalTop(0),
- mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
+ mInputDeviceOrientation(ui::ROTATION_0) {}
TouchInputMapper::~TouchInputMapper() {}
@@ -359,49 +392,26 @@
}
}
- if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
- // The device is a touch screen.
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
- // The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- } else {
- // The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- }
+ configureDeviceType();
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
- std::string deviceTypeString;
- if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType",
- deviceTypeString)) {
- if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
- } else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- } else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
- }
- }
-
mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
- mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+ mParameters.orientation = ui::ROTATION_0;
std::string orientationString;
if (getDeviceContext().getConfiguration().tryGetProperty("touch.orientation",
orientationString)) {
if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
} else if (orientationString == "ORIENTATION_90") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+ mParameters.orientation = ui::ROTATION_90;
} else if (orientationString == "ORIENTATION_180") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+ mParameters.orientation = ui::ROTATION_180;
} else if (orientationString == "ORIENTATION_270") {
- mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+ mParameters.orientation = ui::ROTATION_270;
} else if (orientationString != "ORIENTATION_0") {
ALOGW("Invalid value for touch.orientation: '%s'", orientationString.c_str());
}
@@ -411,7 +421,9 @@
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware ||
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
- mParameters.deviceType == Parameters::DeviceType::POINTER) {
+ mParameters.deviceType == Parameters::DeviceType::POINTER ||
+ (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
+ getDeviceContext().getAssociatedViewport())) {
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
@@ -440,6 +452,34 @@
mParameters.enableForInactiveViewport);
}
+void TouchInputMapper::configureDeviceType() {
+ if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
+ // The device is a touch screen.
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
+ // The device is a pointing device like a track pad.
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ } else {
+ // The device is a touch pad of unknown purpose.
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ }
+
+ // Type association takes precedence over the device type found in the idc file.
+ std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
+ if (deviceTypeString.empty()) {
+ getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType", deviceTypeString);
+ }
+ if (deviceTypeString == "touchScreen") {
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (deviceTypeString == "touchNavigation") {
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
+ } else if (deviceTypeString == "pointer") {
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ } else if (deviceTypeString != "default" && deviceTypeString != "") {
+ ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
+ }
+}
+
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
@@ -564,7 +604,7 @@
}
// Size of diagonal axis.
- const float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight);
+ const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height);
// Size factors.
if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
@@ -647,8 +687,8 @@
void TouchInputMapper::initializeOrientedRanges() {
// Configure X and Y factors.
- mXScale = float(mDisplayWidth) / mRawPointerAxes.getRawWidth();
- mYScale = float(mDisplayHeight) / mRawPointerAxes.getRawHeight();
+ mXScale = float(mDisplayBounds.width) / mRawPointerAxes.getRawWidth();
+ mYScale = float(mDisplayBounds.height) / mRawPointerAxes.getRawHeight();
mXPrecision = 1.0f / mXScale;
mYPrecision = 1.0f / mYScale;
@@ -778,19 +818,19 @@
// Note that the maximum value reported is an inclusive maximum value so it is one
// unit less than the total width or height of the display.
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_90:
+ case ui::ROTATION_270:
mOrientedXPrecision = mYPrecision;
mOrientedYPrecision = mXPrecision;
mOrientedRanges.x.min = 0;
- mOrientedRanges.x.max = mDisplayHeight - 1;
+ mOrientedRanges.x.max = mDisplayBounds.height - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
mOrientedRanges.y.min = 0;
- mOrientedRanges.y.max = mDisplayWidth - 1;
+ mOrientedRanges.y.max = mDisplayBounds.width - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -801,13 +841,13 @@
mOrientedYPrecision = mYPrecision;
mOrientedRanges.x.min = 0;
- mOrientedRanges.x.max = mDisplayWidth - 1;
+ mOrientedRanges.x.max = mDisplayBounds.width - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
mOrientedRanges.y.min = 0;
- mOrientedRanges.y.max = mDisplayHeight - 1;
+ mOrientedRanges.y.max = mDisplayBounds.height - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -868,8 +908,7 @@
}
// Raw width and height in the natural orientation.
- const int32_t rawWidth = mRawPointerAxes.getRawWidth();
- const int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
const int32_t rawXResolution = mRawPointerAxes.x.resolution;
const int32_t rawYResolution = mRawPointerAxes.y.resolution;
// Calculate the mean resolution when both x and y resolution are set, otherwise set it to 0.
@@ -885,67 +924,16 @@
mViewport = newViewport;
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
- // Convert rotated viewport to the natural orientation.
- int32_t naturalPhysicalWidth, naturalPhysicalHeight;
- int32_t naturalPhysicalLeft, naturalPhysicalTop;
- int32_t naturalDeviceWidth, naturalDeviceHeight;
+ const auto oldDisplayBounds = mDisplayBounds;
// Apply the inverse of the input device orientation so that the input device is
// configured in the same orientation as the viewport. The input device orientation will
// be re-applied by mInputDeviceOrientation.
- const int32_t naturalDeviceOrientation =
- (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
- switch (naturalDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalPhysicalTop = mViewport.physicalLeft;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_180:
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
- naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- case DISPLAY_ORIENTATION_270:
- naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalLeft = mViewport.physicalTop;
- naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
- naturalDeviceWidth = mViewport.deviceHeight;
- naturalDeviceHeight = mViewport.deviceWidth;
- break;
- case DISPLAY_ORIENTATION_0:
- default:
- naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
- naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
- naturalPhysicalLeft = mViewport.physicalLeft;
- naturalPhysicalTop = mViewport.physicalTop;
- naturalDeviceWidth = mViewport.deviceWidth;
- naturalDeviceHeight = mViewport.deviceHeight;
- break;
- }
+ const ui::Rotation naturalDeviceOrientation =
+ mViewport.orientation - mParameters.orientation;
- if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) {
- ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str());
- naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight;
- naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth;
- }
-
- mPhysicalWidth = naturalPhysicalWidth;
- mPhysicalHeight = naturalPhysicalHeight;
- mPhysicalLeft = naturalPhysicalLeft;
- mPhysicalTop = naturalPhysicalTop;
-
- const int32_t oldDisplayWidth = mDisplayWidth;
- const int32_t oldDisplayHeight = mDisplayHeight;
- mDisplayWidth = naturalDeviceWidth;
- mDisplayHeight = naturalDeviceHeight;
+ std::tie(mDisplayBounds, mPhysicalFrameInDisplay) =
+ getNaturalDisplayInfo(mViewport, naturalDeviceOrientation);
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
@@ -953,26 +941,19 @@
// when the display rotation is applied later as a part of the per-window transform, we
// get the expected screen coordinates.
mInputDeviceOrientation = mParameters.orientationAware
- ? DISPLAY_ORIENTATION_0
+ ? ui::ROTATION_0
: getInverseRotation(mViewport.orientation);
// For orientation-aware devices that work in the un-rotated coordinate space, the
// viewport update should be skipped if it is only a change in the orientation.
skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware &&
- mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight &&
- viewportOrientationChanged;
+ mDisplayBounds == oldDisplayBounds && viewportOrientationChanged;
// Apply the input device orientation for the device.
- mInputDeviceOrientation =
- (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+ mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
} else {
- mPhysicalWidth = rawWidth;
- mPhysicalHeight = rawHeight;
- mPhysicalLeft = 0;
- mPhysicalTop = 0;
-
- mDisplayWidth = rawWidth;
- mDisplayHeight = rawHeight;
- mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
+ mDisplayBounds = rawSize;
+ mPhysicalFrameInDisplay = Rect{mDisplayBounds};
+ mInputDeviceOrientation = ui::ROTATION_0;
}
}
@@ -1003,9 +984,9 @@
}
if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
- ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
+ ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, "
"display id %d",
- getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight,
+ getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(),
mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
configureVirtualKeys();
@@ -1017,8 +998,8 @@
if (mDeviceMode == DeviceMode::POINTER) {
// Compute pointer gesture detection parameters.
- float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight);
+ float rawDiagonal = hypotf(rawSize.width, rawSize.height);
+ float displayDiagonal = hypotf(mDisplayBounds.width, mDisplayBounds.height);
// Scale movements such that one whole swipe of the touch pad covers a
// given area relative to the diagonal size of the display when no acceleration
@@ -1054,12 +1035,8 @@
void TouchInputMapper::dumpDisplay(std::string& dump) {
dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
- dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth);
- dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight);
- dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
- dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
- dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
- dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
+ dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str());
+ dump += StringPrintf(INDENT3 "PhysicalFrame: %s\n", toString(mPhysicalFrameInDisplay).c_str());
dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
}
@@ -1098,17 +1075,17 @@
int32_t halfWidth = virtualKeyDefinition.width / 2;
int32_t halfHeight = virtualKeyDefinition.height / 2;
- virtualKey.hitLeft =
- (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth +
+ virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth /
+ mDisplayBounds.width +
touchScreenLeft;
- virtualKey.hitRight =
- (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth +
+ virtualKey.hitRight = (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth /
+ mDisplayBounds.width +
touchScreenLeft;
- virtualKey.hitTop =
- (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight +
+ virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
+ mDisplayBounds.height +
touchScreenTop;
- virtualKey.hitBottom =
- (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight +
+ virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
+ mDisplayBounds.height +
touchScreenTop;
mVirtualKeys.push_back(virtualKey);
}
@@ -2377,7 +2354,7 @@
float left, top, right, bottom;
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
@@ -2388,7 +2365,7 @@
(mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
@@ -2399,7 +2376,7 @@
(mOrientedRanges.orientation->max - mOrientedRanges.orientation->min);
}
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
@@ -3763,15 +3740,12 @@
PointerCoords pointerCoords[MAX_POINTERS];
PointerProperties pointerProperties[MAX_POINTERS];
uint32_t pointerCount = 0;
- bool stylusToolFound = false;
while (!idBits.isEmpty()) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = idToIndex[id];
pointerProperties[pointerCount].copyFrom(properties[index]);
pointerCoords[pointerCount].copyFrom(coords[index]);
- stylusToolFound |= isStylusToolType(pointerProperties[pointerCount].toolType);
-
if (changedId >= 0 && id == uint32_t(changedId)) {
action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
@@ -3803,12 +3777,6 @@
if (mDeviceMode == DeviceMode::POINTER) {
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
}
- if (stylusToolFound) {
- // Dynamically add the stylus source when there's a stylus tool being used to cover the case
- // where we cannot reliably detect whether a multi-touch device will ever produce stylus
- // events when it is initially being configured.
- source |= AINPUT_SOURCE_STYLUS;
- }
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
@@ -3842,19 +3810,19 @@
// 180 - reverse x, y.
// 270 - swap x/y and reverse x.
switch (mInputDeviceOrientation) {
- case DISPLAY_ORIENTATION_0:
+ case ui::ROTATION_0:
x = xScaled;
y = yScaled;
break;
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
y = xScaledMax;
x = yScaled;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = xScaledMax;
y = yScaledMax;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
y = xScaled;
x = yScaledMax;
break;
@@ -3868,9 +3836,8 @@
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
- xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
- yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
+ isPointInRect(mPhysicalFrameInDisplay, xScaled, yScaled);
}
const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 788ec58..50a7ea3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <ui/Rotation.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
@@ -218,15 +219,7 @@
bool associatedDisplayIsExternal;
bool orientationAware;
- enum class Orientation : int32_t {
- ORIENTATION_0 = DISPLAY_ORIENTATION_0,
- ORIENTATION_90 = DISPLAY_ORIENTATION_90,
- ORIENTATION_180 = DISPLAY_ORIENTATION_180,
- ORIENTATION_270 = DISPLAY_ORIENTATION_270,
-
- ftl_last = ORIENTATION_270
- };
- Orientation orientation;
+ ui::Rotation orientation;
bool hasButtonUnderPad;
std::string uniqueDisplayId;
@@ -412,22 +405,19 @@
// The components of the viewport are specified in the display's rotated orientation.
DisplayViewport mViewport;
- // The width and height are obtained from the viewport and are specified
- // in the natural orientation.
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
+ // We refer to the display as being in the "natural orientation" when there is no rotation
+ // applied. The display size obtained from the viewport in the natural orientation.
+ // Always starts at (0, 0).
+ ui::Size mDisplayBounds{ui::kInvalidSize};
- // The physical frame is the rectangle in the display's coordinate space that maps to the
+ // The physical frame is the rectangle in the natural display's coordinate space that maps to
// the logical display frame.
- int32_t mPhysicalWidth;
- int32_t mPhysicalHeight;
- int32_t mPhysicalLeft;
- int32_t mPhysicalTop;
+ Rect mPhysicalFrameInDisplay{Rect::INVALID_RECT};
// The orientation of the input device relative to that of the display panel. It specifies
// the rotation of the input device coordinates required to produce the display panel
// orientation, so it will depend on whether the device is orientation aware.
- int32_t mInputDeviceOrientation;
+ ui::Rotation mInputDeviceOrientation;
// Translation and scaling factors, orientation-independent.
float mXScale;
@@ -824,6 +814,8 @@
static void assignPointerIds(const RawState& last, RawState& current);
void rotateAndScale(float& x, float& y) const;
+
+ void configureDeviceType();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
new file mode 100644
index 0000000..c563dba
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../Macros.h"
+
+#include <android/input.h>
+#include <linux/input-event-codes.h>
+#include <log/log_main.h>
+#include "TouchCursorInputMapperCommon.h"
+#include "TouchpadInputMapper.h"
+
+namespace android {
+
+namespace {
+
+short getMaxTouchCount(const InputDeviceContext& context) {
+ if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
+ if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
+ if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
+ if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
+ if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
+ return 0;
+}
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+ HardwareProperties props;
+ RawAbsoluteAxisInfo absMtPositionX;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ props.left = absMtPositionX.minValue;
+ props.right = absMtPositionX.maxValue;
+ props.res_x = absMtPositionX.resolution;
+
+ RawAbsoluteAxisInfo absMtPositionY;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ props.top = absMtPositionY.minValue;
+ props.bottom = absMtPositionY.maxValue;
+ props.res_y = absMtPositionY.resolution;
+
+ RawAbsoluteAxisInfo absMtOrientation;
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+ props.orientation_minimum = absMtOrientation.minValue;
+ props.orientation_maximum = absMtOrientation.maxValue;
+
+ RawAbsoluteAxisInfo absMtSlot;
+ context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+ props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ props.max_touch_cnt = getMaxTouchCount(context);
+
+ // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+ // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+ // that did this, so assume false.
+ props.supports_t5r2 = false;
+
+ props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+ props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+ // Mouse-only properties, which will always be false.
+ props.has_wheel = false;
+ props.wheel_is_hi_res = false;
+
+ // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+ // are haptic.
+ props.is_haptic_pad = false;
+ return props;
+}
+
+void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
+ TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
+ mapper->consumeGesture(gesture);
+}
+
+} // namespace
+
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
+ : InputMapper(deviceContext),
+ mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
+ mPointerController(getContext()->getPointerController(getDeviceId())),
+ mStateConverter(deviceContext),
+ mGestureConverter(*getContext(), getDeviceId()) {
+ mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
+ mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+ // Even though we don't explicitly delete copy/move semantics, it's safe to
+ // give away a pointer to TouchpadInputMapper here because
+ // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
+ // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
+ mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
+ // TODO(b/251196347): set a property provider, so we can change gesture properties.
+ // TODO(b/251196347): set a timer provider, so the library can use timers.
+}
+
+TouchpadInputMapper::~TouchpadInputMapper() {
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+}
+
+uint32_t TouchpadInputMapper::getSources() const {
+ return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
+ mStateConverter.reset();
+ mGestureConverter.reset();
+ return InputMapper::reset(when);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+ std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
+ if (state) {
+ return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
+ } else {
+ return {};
+ }
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
+ SelfContainedHardwareState schs) {
+ mProcessing = true;
+ mGestureInterpreter->PushHardwareState(&schs.state);
+ mProcessing = false;
+
+ return processGestures(when, readTime);
+}
+
+void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
+ ALOGD("Gesture ready: %s", gesture->String().c_str());
+ if (!mProcessing) {
+ ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
+ return;
+ }
+ mGesturesToProcess.push_back(*gesture);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out = {};
+ for (Gesture& gesture : mGesturesToProcess) {
+ out += mGestureConverter.handleGesture(when, readTime, gesture);
+ }
+ mGesturesToProcess.clear();
+ return out;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
new file mode 100644
index 0000000..b3bc831
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <PointerControllerInterface.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputMapper.h"
+#include "NotifyArgs.h"
+#include "gestures/GestureConverter.h"
+#include "gestures/HardwareStateConverter.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+class TouchpadInputMapper : public InputMapper {
+public:
+ explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
+ ~TouchpadInputMapper();
+
+ uint32_t getSources() const override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+
+ void consumeGesture(const Gesture* gesture);
+
+private:
+ [[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
+ SelfContainedHardwareState schs);
+ [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
+
+ std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
+ mGestureInterpreter;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
+
+ HardwareStateConverter mStateConverter;
+ GestureConverter mGestureConverter;
+
+ bool mProcessing = false;
+ std::vector<Gesture> mGesturesToProcess;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 2d7d73b..153236c 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -25,7 +25,7 @@
clearButtons();
}
-void CursorButtonAccumulator::reset(InputDeviceContext& deviceContext) {
+void CursorButtonAccumulator::reset(const InputDeviceContext& deviceContext) {
mBtnLeft = deviceContext.isKeyPressed(BTN_LEFT);
mBtnRight = deviceContext.isKeyPressed(BTN_RIGHT);
mBtnMiddle = deviceContext.isKeyPressed(BTN_MIDDLE);
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index ed4c789..6960644 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -27,11 +27,19 @@
class CursorButtonAccumulator {
public:
CursorButtonAccumulator();
- void reset(InputDeviceContext& deviceContext);
+ void reset(const InputDeviceContext& deviceContext);
void process(const RawEvent* rawEvent);
uint32_t getButtonState() const;
+ inline bool isLeftPressed() const { return mBtnLeft; }
+ inline bool isRightPressed() const { return mBtnRight; }
+ inline bool isMiddlePressed() const { return mBtnMiddle; }
+ inline bool isBackPressed() const { return mBtnBack; }
+ inline bool isSidePressed() const { return mBtnSide; }
+ inline bool isForwardPressed() const { return mBtnForward; }
+ inline bool isExtraPressed() const { return mBtnExtra; }
+ inline bool isTaskPressed() const { return mBtnTask; }
private:
bool mBtnLeft;
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index 8746729..f6a42bd 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -26,8 +26,8 @@
MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
: mCurrentSlot(-1), mUsingSlotsProtocol(false) {}
-void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
- bool usingSlotsProtocol) {
+void MultiTouchMotionAccumulator::configure(const InputDeviceContext& deviceContext,
+ size_t slotCount, bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
mSlots = std::vector<Slot>(slotCount);
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 62bc780..3c1a2a9 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -72,7 +72,8 @@
MultiTouchMotionAccumulator();
- void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
+ void configure(const InputDeviceContext& deviceContext, size_t slotCount,
+ bool usingSlotsProtocol);
void process(const RawEvent* rawEvent);
void finishSync();
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index bc23a8e..6601702 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -48,6 +48,7 @@
mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+ mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP);
mHidUsageAccumulator.reset();
}
@@ -100,6 +101,9 @@
case BTN_TOOL_QUADTAP:
mBtnToolQuadTap = rawEvent->value;
break;
+ case BTN_TOOL_QUINTTAP:
+ mBtnToolQuintTap = rawEvent->value;
+ break;
default:
processMappedKey(rawEvent->code, rawEvent->value);
}
@@ -147,7 +151,8 @@
if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
return AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+ if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap ||
+ mBtnToolQuintTap) {
return AMOTION_EVENT_TOOL_TYPE_FINGER;
}
return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -156,7 +161,7 @@
bool TouchButtonAccumulator::isToolActive() const {
return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
- mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+ mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap;
}
bool TouchButtonAccumulator::isHovering() const {
@@ -171,4 +176,15 @@
return mHaveBtnTouch;
}
+int TouchButtonAccumulator::getTouchCount() const {
+ if (mBtnTouch) {
+ if (mBtnToolQuintTap) return 5;
+ if (mBtnToolQuadTap) return 4;
+ if (mBtnToolTripleTap) return 3;
+ if (mBtnToolDoubleTap) return 2;
+ if (mBtnToolFinger) return 1;
+ }
+ return 0;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index c2de23c..c2aa2ad 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -27,7 +27,7 @@
/* Keeps track of the state of touch, stylus and tool buttons. */
class TouchButtonAccumulator {
public:
- explicit TouchButtonAccumulator(InputDeviceContext& deviceContext)
+ explicit TouchButtonAccumulator(const InputDeviceContext& deviceContext)
: mDeviceContext(deviceContext){};
void configure();
@@ -41,6 +41,7 @@
bool isHovering() const;
bool hasStylus() const;
bool hasButtonTouch() const;
+ int getTouchCount() const;
private:
bool mHaveBtnTouch{};
@@ -60,10 +61,11 @@
bool mBtnToolDoubleTap{};
bool mBtnToolTripleTap{};
bool mBtnToolQuadTap{};
+ bool mBtnToolQuintTap{};
HidUsageAccumulator mHidUsageAccumulator{};
- InputDeviceContext& mDeviceContext;
+ const InputDeviceContext& mDeviceContext;
void processMappedKey(int32_t scanCode, bool down);
};
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
new file mode 100644
index 0000000..23216d3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gestures/GestureConverter.h"
+
+#include <android/input.h>
+
+#include "TouchCursorInputMapperCommon.h"
+#include "input/Input.h"
+
+namespace android {
+
+namespace {
+
+uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
+ switch (gesturesButton) {
+ case GESTURES_BUTTON_LEFT:
+ return AMOTION_EVENT_BUTTON_PRIMARY;
+ case GESTURES_BUTTON_MIDDLE:
+ return AMOTION_EVENT_BUTTON_TERTIARY;
+ case GESTURES_BUTTON_RIGHT:
+ return AMOTION_EVENT_BUTTON_SECONDARY;
+ case GESTURES_BUTTON_BACK:
+ return AMOTION_EVENT_BUTTON_BACK;
+ case GESTURES_BUTTON_FORWARD:
+ return AMOTION_EVENT_BUTTON_FORWARD;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
+GestureConverter::GestureConverter(InputReaderContext& readerContext, int32_t deviceId)
+ : mDeviceId(deviceId),
+ mReaderContext(readerContext),
+ mPointerController(readerContext.getPointerController(deviceId)) {}
+
+void GestureConverter::reset() {
+ mButtonState = 0;
+}
+
+std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ switch (gesture.type) {
+ case kGestureTypeMove:
+ return {handleMove(when, readTime, gesture)};
+ case kGestureTypeButtonsChange:
+ return handleButtonsChange(when, readTime, gesture);
+ default:
+ // TODO(b/251196347): handle more gesture types.
+ return {};
+ }
+}
+
+NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
+ const bool down = isPointerDown(mButtonState);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
+
+ const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
+ return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
+ /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
+}
+
+std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ std::list<NotifyArgs> out = {};
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+ const uint32_t buttonsPressed = gesture.details.buttons.down;
+ bool pointerDown = isPointerDown(mButtonState) ||
+ buttonsPressed &
+ (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+
+ uint32_t newButtonState = mButtonState;
+ std::list<NotifyArgs> pressEvents = {};
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsPressed & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState |= actionButton;
+ pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, newButtonState,
+ /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ }
+ if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
+ mDownTime = when;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ out.splice(out.end(), pressEvents);
+
+ // The same button may be in both down and up in the same gesture, in which case we should treat
+ // it as having gone down and then up. So, we treat a single button change gesture as two state
+ // changes: a set of buttons going down, followed by a set of buttons going up.
+ mButtonState = newButtonState;
+
+ const uint32_t buttonsReleased = gesture.details.buttons.up;
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsReleased & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState &= ~actionButton;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ actionButton, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ }
+ if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
+ newButtonState, /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ mButtonState = newButtonState;
+ return out;
+}
+
+NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ int32_t actionButton, int32_t buttonState,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords,
+ float xCursorPosition, float yCursorPosition) {
+ // TODO(b/260226362): consider what the appropriate source for these events is.
+ const uint32_t source = AINPUT_SOURCE_MOUSE;
+
+ return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, source,
+ mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
+ /* actionButton= */ actionButton, /* flags= */ 0,
+ mReaderContext.getGlobalMetaState(), buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties, pointerCoords,
+ /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
+ yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
new file mode 100644
index 0000000..dc11f24
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+
+#include <PointerControllerInterface.h>
+#include <utils/Timers.h>
+
+#include "InputReaderContext.h"
+#include "NotifyArgs.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+// Converts Gesture structs from the gestures library into NotifyArgs and the appropriate
+// PointerController calls.
+class GestureConverter {
+public:
+ GestureConverter(InputReaderContext& readerContext, int32_t deviceId);
+
+ void reset();
+
+ [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+
+private:
+ NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+
+ NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ int32_t actionButton, int32_t buttonState,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xCursorPosition,
+ float yCursorPosition);
+
+ const int32_t mDeviceId;
+ InputReaderContext& mReaderContext;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
+
+ // The current button state according to the gestures library, but converted into MotionEvent
+ // button values (AMOTION_EVENT_BUTTON_...).
+ uint32_t mButtonState = 0;
+ nsecs_t mDownTime = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
new file mode 100644
index 0000000..81b4968
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef LOG_TAG
+#define LOG_TAG "Gestures"
+#endif
+
+#include <stdio.h>
+
+#include <log/log.h>
+
+#include "include/gestures.h"
+
+extern "C" {
+
+void gestures_log(int verb, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ if (verb == GESTURES_LOG_ERROR) {
+ LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
+ } else if (verb == GESTURES_LOG_INFO) {
+ LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
+ } else {
+ LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+ }
+ va_end(args);
+}
+}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
new file mode 100644
index 0000000..2e175b8
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gestures/HardwareStateConverter.h"
+
+#include <chrono>
+#include <vector>
+
+#include <linux/input-event-codes.h>
+
+namespace android {
+
+HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext)
+ : mDeviceContext(deviceContext), mTouchButtonAccumulator(deviceContext) {
+ RawAbsoluteAxisInfo slotAxisInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+ ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+ "properly.",
+ deviceContext.getName().c_str());
+ }
+ mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
+ mTouchButtonAccumulator.configure();
+}
+
+std::optional<SelfContainedHardwareState> HardwareStateConverter::processRawEvent(
+ const RawEvent* rawEvent) {
+ std::optional<SelfContainedHardwareState> out;
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out = produceHardwareState(rawEvent->when);
+ mMotionAccumulator.finishSync();
+ mMscTimestamp = 0;
+ }
+ if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+ mMscTimestamp = rawEvent->value;
+ }
+ mCursorButtonAccumulator.process(rawEvent);
+ mMotionAccumulator.process(rawEvent);
+ mTouchButtonAccumulator.process(rawEvent);
+ return out;
+}
+
+SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t when) {
+ SelfContainedHardwareState schs;
+ // The gestures library uses doubles to represent timestamps in seconds.
+ schs.state.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+ schs.state.msc_timestamp =
+ std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();
+
+ schs.state.buttons_down = 0;
+ if (mCursorButtonAccumulator.isLeftPressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_LEFT;
+ }
+ if (mCursorButtonAccumulator.isMiddlePressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_MIDDLE;
+ }
+ if (mCursorButtonAccumulator.isRightPressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_RIGHT;
+ }
+ if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_BACK;
+ }
+ if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_FORWARD;
+ }
+
+ schs.fingers.clear();
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+ if (slot.isInUse()) {
+ FingerState& fingerState = schs.fingers.emplace_back();
+ fingerState = {};
+ fingerState.touch_major = slot.getTouchMajor();
+ fingerState.touch_minor = slot.getTouchMinor();
+ fingerState.width_major = slot.getToolMajor();
+ fingerState.width_minor = slot.getToolMinor();
+ fingerState.pressure = slot.getPressure();
+ fingerState.orientation = slot.getOrientation();
+ fingerState.position_x = slot.getX();
+ fingerState.position_y = slot.getY();
+ fingerState.tracking_id = slot.getTrackingId();
+ }
+ }
+ schs.state.fingers = schs.fingers.data();
+ schs.state.finger_cnt = schs.fingers.size();
+ schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+ return schs;
+}
+
+void HardwareStateConverter::reset() {
+ mCursorButtonAccumulator.reset(mDeviceContext);
+ mTouchButtonAccumulator.reset();
+ mMscTimestamp = 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
new file mode 100644
index 0000000..8831299
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <utils/Timers.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have
+// to worry about where that memory is allocated.
+struct SelfContainedHardwareState {
+ HardwareState state;
+ std::vector<FingerState> fingers;
+};
+
+// Converts RawEvents into the HardwareState structs used by the gestures library.
+class HardwareStateConverter {
+public:
+ HardwareStateConverter(const InputDeviceContext& deviceContext);
+
+ std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
+ void reset();
+
+private:
+ SelfContainedHardwareState produceHardwareState(nsecs_t when);
+
+ const InputDeviceContext& mDeviceContext;
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ MultiTouchMotionAccumulator mMotionAccumulator;
+ TouchButtonAccumulator mTouchButtonAccumulator;
+ int32_t mMscTimestamp = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index b6d0709..58a5c31 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -40,12 +40,20 @@
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
+ "FakeEventHub.cpp",
+ "FakeInputReaderPolicy.cpp",
+ "FakePointerController.cpp",
"FocusResolver_test.cpp",
+ "GestureConverter_test.cpp",
+ "HardwareStateConverter_test.cpp",
+ "InputMapperTest.cpp",
"InputProcessor_test.cpp",
"InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
+ "NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
"TestInputListener.cpp",
"UinputDevice.cpp",
@@ -81,9 +89,6 @@
"libc++fs",
"libgmock",
],
- shared_libs: [
- "libinputreader",
- ],
require_root: true,
test_options: {
unit_test: true,
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
new file mode 100644
index 0000000..289a780
--- /dev/null
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FakeEventHub.h"
+
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "TestConstants.h"
+
+namespace android {
+
+const std::string FakeEventHub::BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
+
+FakeEventHub::~FakeEventHub() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+}
+
+void FakeEventHub::addDevice(int32_t deviceId, const std::string& name,
+ ftl::Flags<InputDeviceClass> classes, int bus) {
+ Device* device = new Device(classes);
+ device->identifier.name = name;
+ device->identifier.bus = bus;
+ mDevices.add(deviceId, device);
+
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+}
+
+void FakeEventHub::removeDevice(int32_t deviceId) {
+ delete mDevices.valueFor(deviceId);
+ mDevices.removeItem(deviceId);
+
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+}
+
+bool FakeEventHub::isDeviceEnabled(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return false;
+ }
+ return device->enabled;
+}
+
+status_t FakeEventHub::enableDevice(int32_t deviceId) {
+ status_t result;
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
+ return OK;
+ }
+ result = device->enable();
+ return result;
+}
+
+status_t FakeEventHub::disableDevice(int32_t deviceId) {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return BAD_VALUE;
+ }
+ if (!device->enabled) {
+ ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
+ return OK;
+ }
+ return device->disable();
+}
+
+void FakeEventHub::finishDeviceScan() {
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+}
+
+void FakeEventHub::addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
+ getDevice(deviceId)->configuration.addProperty(key, value);
+}
+
+void FakeEventHub::addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
+ getDevice(deviceId)->configuration.addAll(configuration);
+}
+
+void FakeEventHub::addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue,
+ int flat, int fuzz, int resolution) {
+ Device* device = getDevice(deviceId);
+
+ RawAbsoluteAxisInfo info;
+ info.valid = true;
+ info.minValue = minValue;
+ info.maxValue = maxValue;
+ info.flat = flat;
+ info.fuzz = fuzz;
+ info.resolution = resolution;
+ device->absoluteAxes.add(axis, info);
+}
+
+void FakeEventHub::addRelativeAxis(int32_t deviceId, int32_t axis) {
+ getDevice(deviceId)->relativeAxes.add(axis, true);
+}
+
+void FakeEventHub::setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
+ getDevice(deviceId)->keyCodeStates.replaceValueFor(keyCode, state);
+}
+
+void FakeEventHub::setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
+ getDevice(deviceId)->countryCode = countryCode;
+}
+
+void FakeEventHub::setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
+ getDevice(deviceId)->scanCodeStates.replaceValueFor(scanCode, state);
+}
+
+void FakeEventHub::setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
+ getDevice(deviceId)->switchStates.replaceValueFor(switchCode, state);
+}
+
+void FakeEventHub::setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
+ getDevice(deviceId)->absoluteAxisValue.replaceValueFor(axis, value);
+}
+
+void FakeEventHub::addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+ uint32_t flags) {
+ Device* device = getDevice(deviceId);
+ KeyInfo info;
+ info.keyCode = keyCode;
+ info.flags = flags;
+ if (scanCode) {
+ device->keysByScanCode.add(scanCode, info);
+ }
+ if (usageCode) {
+ device->keysByUsageCode.add(usageCode, info);
+ }
+}
+
+void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
+ getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
+void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ Device* device = getDevice(deviceId);
+ device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
+void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
+ getDevice(deviceId)->leds.add(led, initialState);
+}
+
+void FakeEventHub::addSensorAxis(int32_t deviceId, int32_t absCode,
+ InputDeviceSensorType sensorType, int32_t sensorDataIndex) {
+ SensorInfo info;
+ info.sensorType = sensorType;
+ info.sensorDataIndex = sensorDataIndex;
+ getDevice(deviceId)->sensorsByAbsCode.emplace(absCode, info);
+}
+
+void FakeEventHub::setMscEvent(int32_t deviceId, int32_t mscEvent) {
+ typename BitArray<MSC_MAX>::Buffer buffer;
+ buffer[mscEvent / 32] = 1 << mscEvent % 32;
+ getDevice(deviceId)->mscBitmask.loadFromBuffer(buffer);
+}
+
+void FakeEventHub::addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
+ mRawLightInfos.emplace(rawId, std::move(info));
+}
+
+void FakeEventHub::fakeLightBrightness(int32_t rawId, int32_t brightness) {
+ mLightBrightness.emplace(rawId, brightness);
+}
+
+void FakeEventHub::fakeLightIntensities(int32_t rawId,
+ const std::unordered_map<LightColor, int32_t> intensities) {
+ mLightIntensities.emplace(rawId, std::move(intensities));
+}
+
+bool FakeEventHub::getLedState(int32_t deviceId, int32_t led) {
+ return getDevice(deviceId)->leds.valueFor(led);
+}
+
+std::vector<std::string>& FakeEventHub::getExcludedDevices() {
+ return mExcludedDevices;
+}
+
+void FakeEventHub::addVirtualKeyDefinition(int32_t deviceId,
+ const VirtualKeyDefinition& definition) {
+ getDevice(deviceId)->virtualKeys.push_back(definition);
+}
+
+void FakeEventHub::enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type,
+ int32_t code, int32_t value) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = deviceId;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ mEvents.push_back(event);
+
+ if (type == EV_ABS) {
+ setAbsoluteAxisValue(deviceId, code, value);
+ }
+}
+
+void FakeEventHub::setVideoFrames(
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames) {
+ mVideoFrames = std::move(videoFrames);
+}
+
+void FakeEventHub::assertQueueIsEmpty() {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ const bool queueIsEmpty =
+ mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
+ [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
+ if (!queueIsEmpty) {
+ FAIL() << "Timed out waiting for EventHub queue to be emptied.";
+ }
+}
+
+FakeEventHub::Device* FakeEventHub::getDevice(int32_t deviceId) const {
+ ssize_t index = mDevices.indexOfKey(deviceId);
+ return index >= 0 ? mDevices.valueAt(index) : nullptr;
+}
+
+ftl::Flags<InputDeviceClass> FakeEventHub::getDeviceClasses(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->classes : ftl::Flags<InputDeviceClass>(0);
+}
+
+InputDeviceIdentifier FakeEventHub::getDeviceIdentifier(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->identifier : InputDeviceIdentifier();
+}
+
+int32_t FakeEventHub::getDeviceControllerNumber(int32_t) const {
+ return 0;
+}
+
+void FakeEventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ *outConfiguration = device->configuration;
+ }
+}
+
+status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->absoluteAxes.indexOfKey(axis);
+ if (index >= 0) {
+ *outAxisInfo = device->absoluteAxes.valueAt(index);
+ return OK;
+ }
+ }
+ outAxisInfo->clear();
+ return -1;
+}
+
+bool FakeEventHub::hasRelativeAxis(int32_t deviceId, int axis) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return device->relativeAxes.indexOfKey(axis) >= 0;
+ }
+ return false;
+}
+
+bool FakeEventHub::hasInputProperty(int32_t, int) const {
+ return false;
+}
+
+bool FakeEventHub::hasMscEvent(int32_t deviceId, int mscEvent) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
+ }
+ return false;
+}
+
+status_t FakeEventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
+ int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
+ uint32_t* outFlags) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ const KeyInfo* key = getKey(device, scanCode, usageCode);
+ if (key) {
+ if (outKeycode) {
+ auto it = device->keyRemapping.find(key->keyCode);
+ *outKeycode = it != device->keyRemapping.end() ? it->second : key->keyCode;
+ }
+ if (outFlags) {
+ *outFlags = key->flags;
+ }
+ if (outMetaState) {
+ *outMetaState = metaState;
+ }
+ return OK;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
+const FakeEventHub::KeyInfo* FakeEventHub::getKey(Device* device, int32_t scanCode,
+ int32_t usageCode) const {
+ if (usageCode) {
+ ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
+ if (index >= 0) {
+ return &device->keysByUsageCode.valueAt(index);
+ }
+ }
+ if (scanCode) {
+ ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+ if (index >= 0) {
+ return &device->keysByScanCode.valueAt(index);
+ }
+ }
+ return nullptr;
+}
+
+status_t FakeEventHub::mapAxis(int32_t, int32_t, AxisInfo*) const {
+ return NAME_NOT_FOUND;
+}
+
+base::Result<std::pair<InputDeviceSensorType, int32_t>> FakeEventHub::mapSensor(
+ int32_t deviceId, int32_t absCode) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return Errorf("Sensor device not found.");
+ }
+ auto it = device->sensorsByAbsCode.find(absCode);
+ if (it == device->sensorsByAbsCode.end()) {
+ return Errorf("Sensor map not found.");
+ }
+ const SensorInfo& info = it->second;
+ return std::make_pair(info.sensorType, info.sensorDataIndex);
+}
+
+void FakeEventHub::setExcludedDevices(const std::vector<std::string>& devices) {
+ mExcludedDevices = devices;
+}
+
+std::vector<RawEvent> FakeEventHub::getEvents(int) {
+ std::scoped_lock lock(mLock);
+
+ std::vector<RawEvent> buffer;
+ std::swap(buffer, mEvents);
+
+ mEventsCondition.notify_all();
+ return buffer;
+}
+
+std::vector<TouchVideoFrame> FakeEventHub::getVideoFrames(int32_t deviceId) {
+ auto it = mVideoFrames.find(deviceId);
+ if (it != mVideoFrames.end()) {
+ std::vector<TouchVideoFrame> frames = std::move(it->second);
+ mVideoFrames.erase(deviceId);
+ return frames;
+ }
+ return {};
+}
+
+int32_t FakeEventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
+ if (index >= 0) {
+ return device->scanCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+InputDeviceCountryCode FakeEventHub::getCountryCode(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->countryCode : InputDeviceCountryCode::INVALID;
+}
+
+int32_t FakeEventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
+ if (index >= 0) {
+ return device->keyCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t FakeEventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->switchStates.indexOfKey(sw);
+ if (index >= 0) {
+ return device->switchStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+status_t FakeEventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+ int32_t* outValue) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
+ if (index >= 0) {
+ *outValue = device->absoluteAxisValue.valueAt(index);
+ return OK;
+ }
+ }
+ *outValue = 0;
+ return -1;
+}
+
+int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return AKEYCODE_UNKNOWN;
+ }
+ auto it = device->keyCodeMapping.find(locationKeyCode);
+ return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
+}
+
+// Return true if the device has non-empty key layout.
+bool FakeEventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+ uint8_t* outFlags) const {
+ Device* device = getDevice(deviceId);
+ if (!device) return false;
+
+ bool result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
+ for (size_t i = 0; i < keyCodes.size(); i++) {
+ for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
+ if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ }
+ }
+ for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+ if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+ return result;
+}
+
+bool FakeEventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
+ return index >= 0;
+ }
+ return false;
+}
+
+bool FakeEventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return false;
+ }
+ for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
+ if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
+ return true;
+ }
+ }
+ for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+ if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FakeEventHub::hasLed(int32_t deviceId, int32_t led) const {
+ Device* device = getDevice(deviceId);
+ return device && device->leds.indexOfKey(led) >= 0;
+}
+
+void FakeEventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->leds.indexOfKey(led);
+ if (index >= 0) {
+ device->leds.replaceValueAt(led, on);
+ } else {
+ ADD_FAILURE() << "Attempted to set the state of an LED that the EventHub declared "
+ "was not present. led="
+ << led;
+ }
+ }
+}
+
+void FakeEventHub::getVirtualKeyDefinitions(
+ int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+ outVirtualKeys.clear();
+
+ Device* device = getDevice(deviceId);
+ if (device) {
+ outVirtualKeys = device->virtualKeys;
+ }
+}
+
+const std::shared_ptr<KeyCharacterMap> FakeEventHub::getKeyCharacterMap(int32_t) const {
+ return nullptr;
+}
+
+bool FakeEventHub::setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) {
+ return false;
+}
+
+std::vector<int32_t> FakeEventHub::getVibratorIds(int32_t deviceId) const {
+ return mVibrators;
+}
+
+std::optional<int32_t> FakeEventHub::getBatteryCapacity(int32_t, int32_t) const {
+ return BATTERY_CAPACITY;
+}
+
+std::optional<int32_t> FakeEventHub::getBatteryStatus(int32_t, int32_t) const {
+ return BATTERY_STATUS;
+}
+
+std::vector<int32_t> FakeEventHub::getRawBatteryIds(int32_t deviceId) const {
+ return {DEFAULT_BATTERY};
+}
+
+std::optional<RawBatteryInfo> FakeEventHub::getRawBatteryInfo(int32_t deviceId,
+ int32_t batteryId) const {
+ if (batteryId != DEFAULT_BATTERY) return {};
+ static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
+ .name = "default battery",
+ .flags = InputBatteryClass::CAPACITY,
+ .path = BATTERY_DEVPATH};
+ return BATTERY_INFO;
+}
+
+std::vector<int32_t> FakeEventHub::getRawLightIds(int32_t deviceId) const {
+ std::vector<int32_t> ids;
+ for (const auto& [rawId, info] : mRawLightInfos) {
+ ids.push_back(rawId);
+ }
+ return ids;
+}
+
+std::optional<RawLightInfo> FakeEventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) const {
+ auto it = mRawLightInfos.find(lightId);
+ if (it == mRawLightInfos.end()) {
+ return std::nullopt;
+ }
+ return it->second;
+}
+
+void FakeEventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) {
+ mLightBrightness.emplace(lightId, brightness);
+}
+
+void FakeEventHub::setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) {
+ mLightIntensities.emplace(lightId, intensities);
+};
+
+std::optional<int32_t> FakeEventHub::getLightBrightness(int32_t deviceId, int32_t lightId) const {
+ auto lightIt = mLightBrightness.find(lightId);
+ if (lightIt == mLightBrightness.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+}
+
+std::optional<std::unordered_map<LightColor, int32_t>> FakeEventHub::getLightIntensities(
+ int32_t deviceId, int32_t lightId) const {
+ auto lightIt = mLightIntensities.find(lightId);
+ if (lightIt == mLightIntensities.end()) {
+ return std::nullopt;
+ }
+ return lightIt->second;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
new file mode 100644
index 0000000..fb3c859
--- /dev/null
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <EventHub.h>
+#include <InputDevice.h>
+#include <ftl/flags.h>
+#include <input/PropertyMap.h>
+#include <input/VirtualKeyMap.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
+using android::hardware::input::InputDeviceCountryCode;
+
+namespace android {
+
+class FakeEventHub : public EventHubInterface {
+ struct KeyInfo {
+ int32_t keyCode;
+ uint32_t flags;
+ };
+
+ struct SensorInfo {
+ InputDeviceSensorType sensorType;
+ int32_t sensorDataIndex;
+ };
+
+ struct Device {
+ InputDeviceIdentifier identifier;
+ ftl::Flags<InputDeviceClass> classes;
+ PropertyMap configuration;
+ KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
+ KeyedVector<int, bool> relativeAxes;
+ KeyedVector<int32_t, int32_t> keyCodeStates;
+ KeyedVector<int32_t, int32_t> scanCodeStates;
+ KeyedVector<int32_t, int32_t> switchStates;
+ KeyedVector<int32_t, int32_t> absoluteAxisValue;
+ KeyedVector<int32_t, KeyInfo> keysByScanCode;
+ KeyedVector<int32_t, KeyInfo> keysByUsageCode;
+ std::unordered_map<int32_t, int32_t> keyRemapping;
+ KeyedVector<int32_t, bool> leds;
+ // fake mapping which would normally come from keyCharacterMap
+ std::unordered_map<int32_t, int32_t> keyCodeMapping;
+ std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
+ BitArray<MSC_MAX> mscBitmask;
+ std::vector<VirtualKeyDefinition> virtualKeys;
+ bool enabled;
+ InputDeviceCountryCode countryCode;
+
+ status_t enable() {
+ enabled = true;
+ return OK;
+ }
+
+ status_t disable() {
+ enabled = false;
+ return OK;
+ }
+
+ explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
+ };
+
+ std::mutex mLock;
+ std::condition_variable mEventsCondition;
+
+ KeyedVector<int32_t, Device*> mDevices;
+ std::vector<std::string> mExcludedDevices;
+ std::vector<RawEvent> mEvents GUARDED_BY(mLock);
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
+ std::vector<int32_t> mVibrators = {0, 1};
+ std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
+ // Simulates a device light brightness, from light id to light brightness.
+ std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
+ // Simulates a device light intensities, from light id to light intensities map.
+ std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
+ mLightIntensities;
+
+public:
+ static constexpr int32_t DEFAULT_BATTERY = 1;
+ static constexpr int32_t BATTERY_STATUS = 4;
+ static constexpr int32_t BATTERY_CAPACITY = 66;
+ static const std::string BATTERY_DEVPATH;
+
+ virtual ~FakeEventHub();
+ FakeEventHub() {}
+
+ void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes,
+ int bus = 0);
+ void removeDevice(int32_t deviceId);
+
+ bool isDeviceEnabled(int32_t deviceId) const override;
+ status_t enableDevice(int32_t deviceId) override;
+ status_t disableDevice(int32_t deviceId) override;
+
+ void finishDeviceScan();
+
+ void addConfigurationProperty(int32_t deviceId, const char* key, const char* value);
+ void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration);
+
+ void addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat,
+ int fuzz, int resolution = 0);
+ void addRelativeAxis(int32_t deviceId, int32_t axis);
+ void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value);
+
+ void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode);
+
+ void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state);
+ void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state);
+ void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state);
+
+ void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
+ uint32_t flags);
+ void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
+ void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
+
+ void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
+ int32_t sensorDataIndex);
+
+ void setMscEvent(int32_t deviceId, int32_t mscEvent);
+
+ void addLed(int32_t deviceId, int32_t led, bool initialState);
+ void addRawLightInfo(int32_t rawId, RawLightInfo&& info);
+ void fakeLightBrightness(int32_t rawId, int32_t brightness);
+ void fakeLightIntensities(int32_t rawId,
+ const std::unordered_map<LightColor, int32_t> intensities);
+ bool getLedState(int32_t deviceId, int32_t led);
+
+ std::vector<std::string>& getExcludedDevices();
+
+ void setVideoFrames(
+ std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames);
+
+ void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
+ int32_t value);
+ void assertQueueIsEmpty();
+
+private:
+ Device* getDevice(int32_t deviceId) const;
+
+ ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override;
+ InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
+ int32_t getDeviceControllerNumber(int32_t) const override;
+ void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
+ status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const override;
+ bool hasRelativeAxis(int32_t deviceId, int axis) const override;
+ bool hasInputProperty(int32_t, int) const override;
+ bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+ status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+ int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override;
+ const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const;
+
+ status_t mapAxis(int32_t, int32_t, AxisInfo*) const override;
+ base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+ int32_t deviceId, int32_t absCode) const override;
+ void setExcludedDevices(const std::vector<std::string>& devices) override;
+ std::vector<RawEvent> getEvents(int) override;
+ std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
+ int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+ InputDeviceCountryCode getCountryCode(int32_t deviceId) const override;
+ int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+ int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+ status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const override;
+ int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
+
+ // Return true if the device has non-empty key layout.
+ bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
+ uint8_t* outFlags) const override;
+ bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+ bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override;
+ bool hasLed(int32_t deviceId, int32_t led) const override;
+ void setLedState(int32_t deviceId, int32_t led, bool on) override;
+ void getVirtualKeyDefinitions(int32_t deviceId,
+ std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
+ const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override;
+ bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override;
+
+ void vibrate(int32_t, const VibrationElement&) override {}
+ void cancelVibrate(int32_t) override {}
+ std::vector<int32_t> getVibratorIds(int32_t deviceId) const override;
+
+ std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override;
+ std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override;
+ std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override;
+ std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
+ int32_t batteryId) const override;
+
+ std::vector<int32_t> getRawLightIds(int32_t deviceId) const override;
+ std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override;
+ void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override;
+ void setLightIntensities(int32_t deviceId, int32_t lightId,
+ std::unordered_map<LightColor, int32_t> intensities) override;
+ std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override;
+ std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
+ int32_t deviceId, int32_t lightId) const override;
+
+ void dump(std::string&) const override {}
+ void monitor() const override {}
+ void requestReopenDevices() override {}
+ void wake() override {}
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
new file mode 100644
index 0000000..f755356
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FakeInputReaderPolicy.h"
+
+#include <android-base/thread_annotations.h>
+#include <gtest/gtest.h>
+
+#include "TestConstants.h"
+#include "ui/Rotation.h"
+
+namespace android {
+
+void FakeInputReaderPolicy::assertInputDevicesChanged() {
+ waitForInputDevices([](bool devicesChanged) {
+ if (!devicesChanged) {
+ FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+ }
+ });
+}
+
+void FakeInputReaderPolicy::assertInputDevicesNotChanged() {
+ waitForInputDevices([](bool devicesChanged) {
+ if (devicesChanged) {
+ FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+ }
+ });
+}
+
+void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mStylusGestureNotified);
+ ASSERT_EQ(deviceId, *mStylusGestureNotified);
+ mStylusGestureNotified.reset();
+}
+
+void FakeInputReaderPolicy::assertStylusGestureNotNotified() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mStylusGestureNotified);
+}
+
+void FakeInputReaderPolicy::clearViewports() {
+ mViewports.clear();
+ mConfig.setDisplayViewports(mViewports);
+}
+
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByUniqueId(
+ const std::string& uniqueId) const {
+ return mConfig.getDisplayViewportByUniqueId(uniqueId);
+}
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByType(
+ ViewportType type) const {
+ return mConfig.getDisplayViewportByType(type);
+}
+
+std::optional<DisplayViewport> FakeInputReaderPolicy::getDisplayViewportByPort(
+ uint8_t displayPort) const {
+ return mConfig.getDisplayViewportByPort(displayPort);
+}
+
+void FakeInputReaderPolicy::addDisplayViewport(DisplayViewport viewport) {
+ mViewports.push_back(std::move(viewport));
+ mConfig.setDisplayViewports(mViewports);
+}
+
+void FakeInputReaderPolicy::addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType type) {
+ const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+
+ addDisplayViewport(v);
+}
+
+bool FakeInputReaderPolicy::updateViewport(const DisplayViewport& viewport) {
+ size_t count = mViewports.size();
+ for (size_t i = 0; i < count; i++) {
+ const DisplayViewport& currentViewport = mViewports[i];
+ if (currentViewport.displayId == viewport.displayId) {
+ mViewports[i] = viewport;
+ mConfig.setDisplayViewports(mViewports);
+ return true;
+ }
+ }
+ // no viewport found.
+ return false;
+}
+
+void FakeInputReaderPolicy::addExcludedDeviceName(const std::string& deviceName) {
+ mConfig.excludedDeviceNames.push_back(deviceName);
+}
+
+void FakeInputReaderPolicy::addInputPortAssociation(const std::string& inputPort,
+ uint8_t displayPort) {
+ mConfig.portAssociations.insert({inputPort, displayPort});
+}
+
+void FakeInputReaderPolicy::addDeviceTypeAssociation(const std::string& inputPort,
+ const std::string& type) {
+ mConfig.deviceTypeAssociations.insert({inputPort, type});
+}
+
+void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId) {
+ mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+}
+
+void FakeInputReaderPolicy::addKeyboardLayoutAssociation(const std::string& inputUniqueId,
+ const KeyboardLayoutInfo& layoutInfo) {
+ mConfig.keyboardLayoutAssociations.insert({inputUniqueId, layoutInfo});
+}
+
+void FakeInputReaderPolicy::addDisabledDevice(int32_t deviceId) {
+ mConfig.disabledDevices.insert(deviceId);
+}
+
+void FakeInputReaderPolicy::removeDisabledDevice(int32_t deviceId) {
+ mConfig.disabledDevices.erase(deviceId);
+}
+
+void FakeInputReaderPolicy::setPointerController(
+ std::shared_ptr<FakePointerController> controller) {
+ mPointerController = std::move(controller);
+}
+
+const InputReaderConfiguration* FakeInputReaderPolicy::getReaderConfiguration() const {
+ return &mConfig;
+}
+
+const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const {
+ return mInputDevices;
+}
+
+TouchAffineTransformation FakeInputReaderPolicy::getTouchAffineTransformation(
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
+ return transform;
+}
+
+void FakeInputReaderPolicy::setTouchAffineTransformation(const TouchAffineTransformation t) {
+ transform = t;
+}
+
+PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) {
+ mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+ return mConfig.pointerCaptureRequest;
+}
+
+void FakeInputReaderPolicy::setShowTouches(bool enabled) {
+ mConfig.showTouches = enabled;
+}
+
+void FakeInputReaderPolicy::setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+ mConfig.defaultPointerDisplayId = pointerDisplayId;
+}
+
+void FakeInputReaderPolicy::setPointerGestureEnabled(bool enabled) {
+ mConfig.pointerGesturesEnabled = enabled;
+}
+
+float FakeInputReaderPolicy::getPointerGestureMovementSpeedRatio() {
+ return mConfig.pointerGestureMovementSpeedRatio;
+}
+
+float FakeInputReaderPolicy::getPointerGestureZoomSpeedRatio() {
+ return mConfig.pointerGestureZoomSpeedRatio;
+}
+
+void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) {
+ mConfig.pointerVelocityControlParameters = params;
+ mConfig.wheelVelocityControlParameters = params;
+}
+
+void FakeInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) {
+ *outConfig = mConfig;
+}
+
+std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointerController(
+ int32_t /*deviceId*/) {
+ return mPointerController;
+}
+
+void FakeInputReaderPolicy::notifyInputDevicesChanged(
+ const std::vector<InputDeviceInfo>& inputDevices) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mInputDevices = inputDevices;
+ mInputDevicesChanged = true;
+ mDevicesChangedCondition.notify_all();
+}
+
+std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) {
+ return nullptr;
+}
+
+std::string FakeInputReaderPolicy::getDeviceAlias(const InputDeviceIdentifier&) {
+ return "";
+}
+
+void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+ std::unique_lock<std::mutex> lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ const bool devicesChanged =
+ mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mInputDevicesChanged;
+ });
+ ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
+ mInputDevicesChanged = false;
+}
+
+void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) {
+ std::scoped_lock<std::mutex> lock(mLock);
+ mStylusGestureNotified = deviceId;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
new file mode 100644
index 0000000..862ff0b
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <InputDevice.h>
+#include <InputReaderBase.h>
+
+#include "FakePointerController.h"
+#include "input/DisplayViewport.h"
+#include "input/InputDevice.h"
+
+namespace android {
+
+class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+protected:
+ virtual ~FakeInputReaderPolicy() {}
+
+public:
+ FakeInputReaderPolicy() {}
+
+ void assertInputDevicesChanged();
+ void assertInputDevicesNotChanged();
+ void assertStylusGestureNotified(int32_t deviceId);
+ void assertStylusGestureNotNotified();
+
+ virtual void clearViewports();
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const;
+ void addDisplayViewport(DisplayViewport viewport);
+ void addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType type);
+ bool updateViewport(const DisplayViewport& viewport);
+ void addExcludedDeviceName(const std::string& deviceName);
+ void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort);
+ void addDeviceTypeAssociation(const std::string& inputPort, const std::string& type);
+ void addInputUniqueIdAssociation(const std::string& inputUniqueId,
+ const std::string& displayUniqueId);
+ void addKeyboardLayoutAssociation(const std::string& inputUniqueId,
+ const KeyboardLayoutInfo& layoutInfo);
+ void addDisabledDevice(int32_t deviceId);
+ void removeDisabledDevice(int32_t deviceId);
+ void setPointerController(std::shared_ptr<FakePointerController> controller);
+ const InputReaderConfiguration* getReaderConfiguration() const;
+ const std::vector<InputDeviceInfo>& getInputDevices() const;
+ TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+ ui::Rotation surfaceRotation);
+ void setTouchAffineTransformation(const TouchAffineTransformation t);
+ PointerCaptureRequest setPointerCapture(bool enabled);
+ void setShowTouches(bool enabled);
+ void setDefaultPointerDisplayId(int32_t pointerDisplayId);
+ void setPointerGestureEnabled(bool enabled);
+ float getPointerGestureMovementSpeedRatio();
+ float getPointerGestureZoomSpeedRatio();
+ void setVelocityControlParams(const VelocityControlParameters& params);
+
+private:
+ void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(
+ int32_t /*deviceId*/) override;
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+ std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier&) override;
+ std::string getDeviceAlias(const InputDeviceIdentifier&) override;
+ void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
+ void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
+
+ std::mutex mLock;
+ std::condition_variable mDevicesChangedCondition;
+
+ InputReaderConfiguration mConfig;
+ std::shared_ptr<FakePointerController> mPointerController;
+ std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+ bool mInputDevicesChanged GUARDED_BY(mLock){false};
+ std::vector<DisplayViewport> mViewports;
+ TouchAffineTransformation transform;
+ std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
+
+ uint32_t mNextPointerCaptureSequenceNumber{0};
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
new file mode 100644
index 0000000..ab7879f
--- /dev/null
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FakePointerController.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+void FakePointerController::setBounds(float minX, float minY, float maxX, float maxY) {
+ mHaveBounds = true;
+ mMinX = minX;
+ mMinY = minY;
+ mMaxX = maxX;
+ mMaxY = maxY;
+}
+
+const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() {
+ return mSpotsByDisplay;
+}
+
+void FakePointerController::setPosition(float x, float y) {
+ mX = x;
+ mY = y;
+}
+
+void FakePointerController::setButtonState(int32_t buttonState) {
+ mButtonState = buttonState;
+}
+
+int32_t FakePointerController::getButtonState() const {
+ return mButtonState;
+}
+
+void FakePointerController::getPosition(float* outX, float* outY) const {
+ *outX = mX;
+ *outY = mY;
+}
+
+int32_t FakePointerController::getDisplayId() const {
+ return mDisplayId;
+}
+
+void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
+ mDisplayId = viewport.displayId;
+}
+
+void FakePointerController::assertPosition(float x, float y) {
+ float actualX, actualY;
+ getPosition(&actualX, &actualY);
+ ASSERT_NEAR(x, actualX, 1);
+ ASSERT_NEAR(y, actualY, 1);
+}
+
+bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
+ float* outMaxY) const {
+ *outMinX = mMinX;
+ *outMinY = mMinY;
+ *outMaxX = mMaxX;
+ *outMaxY = mMaxY;
+ return mHaveBounds;
+}
+
+void FakePointerController::move(float deltaX, float deltaY) {
+ mX += deltaX;
+ if (mX < mMinX) mX = mMinX;
+ if (mX > mMaxX) mX = mMaxX;
+ mY += deltaY;
+ if (mY < mMinY) mY = mMinY;
+ if (mY > mMaxY) mY = mMaxY;
+}
+
+void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+ int32_t displayId) {
+ std::vector<int32_t> newSpots;
+ // Add spots for fingers that are down.
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ newSpots.push_back(id);
+ }
+
+ mSpotsByDisplay[displayId] = newSpots;
+}
+
+void FakePointerController::clearSpots() {
+ mSpotsByDisplay.clear();
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
new file mode 100644
index 0000000..d10cbcd
--- /dev/null
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <PointerControllerInterface.h>
+#include <gui/constants.h>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <utils/BitSet.h>
+
+namespace android {
+
+class FakePointerController : public PointerControllerInterface {
+public:
+ virtual ~FakePointerController() {}
+
+ void setBounds(float minX, float minY, float maxX, float maxY);
+ const std::map<int32_t, std::vector<int32_t>>& getSpots();
+
+ void setPosition(float x, float y) override;
+ void setButtonState(int32_t buttonState) override;
+ int32_t getButtonState() const override;
+ void getPosition(float* outX, float* outY) const override;
+ int32_t getDisplayId() const override;
+ void setDisplayViewport(const DisplayViewport& viewport) override;
+
+ void assertPosition(float x, float y);
+
+private:
+ bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override;
+ void move(float deltaX, float deltaY) override;
+ void fade(Transition) override {}
+ void unfade(Transition) override {}
+ void setPresentation(Presentation) override {}
+ void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+ int32_t displayId) override;
+ void clearSpots() override;
+
+ bool mHaveBounds{false};
+ float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
+ float mX{0}, mY{0};
+ int32_t mButtonState{0};
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+
+ std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
new file mode 100644
index 0000000..91efd1a
--- /dev/null
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <EventHub.h>
+#include <gestures/GestureConverter.h>
+#include <gtest/gtest.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "FakePointerController.h"
+#include "InstrumentedInputReader.h"
+#include "NotifyArgs.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+#include "TestInputListenerMatchers.h"
+#include "include/gestures.h"
+
+namespace android {
+
+using testing::AllOf;
+
+class GestureConverterTest : public testing::Test {
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2;
+ static constexpr float POINTER_X = 100;
+ static constexpr float POINTER_Y = 200;
+
+ void SetUp() {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ *mFakeListener);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(POINTER_X, POINTER_Y);
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<FakePointerController> mFakePointerController;
+};
+
+TEST_F(GestureConverterTest, Move) {
+ GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithButtonState(0),
+ WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210));
+}
+
+TEST_F(GestureConverterTest, ButtonsChange) {
+ GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+
+ // Press left and right buttons at once
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ ASSERT_EQ(3u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+
+ // Then release the left button
+ Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+
+ // Finally release the right button
+ Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, DragWithButton) {
+ GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+
+ // Press the button
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE,
+ /* is_tap= */ false);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+
+ // Move
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210));
+
+ // Release the button
+ Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
new file mode 100644
index 0000000..7921881
--- /dev/null
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <EventHub.h>
+#include <gestures/HardwareStateConverter.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class HardwareStateConverterTest : public testing::Test {
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ void SetUp() {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ *mFakeListener);
+ mDevice = newDevice();
+
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, 7, 0, 0, 0);
+ }
+
+ std::shared_ptr<InputDevice> newDevice() {
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ identifier.bus = 0;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, /* generation= */ 2,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD,
+ identifier.bus);
+ mReader->loopOnce();
+ return device;
+ }
+
+ void processAxis(HardwareStateConverter& conv, nsecs_t when, int32_t type, int32_t code,
+ int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::optional<SelfContainedHardwareState> schs = conv.processRawEvent(&event);
+ EXPECT_FALSE(schs.has_value());
+ }
+
+ std::optional<SelfContainedHardwareState> processSync(HardwareStateConverter& conv,
+ nsecs_t when) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ return conv.processRawEvent(&event);
+ }
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+};
+
+TEST_F(HardwareStateConverterTest, OneFinger) {
+ const nsecs_t time = 1500000000;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
+ processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 42);
+ processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 2);
+
+ processAxis(conv, time, EV_ABS, ABS_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_PRESSURE, 42);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, time, EV_KEY, BTN_TOOL_FINGER, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ const HardwareState& state = schs->state;
+ EXPECT_NEAR(1.5, state.timestamp, EPSILON);
+ EXPECT_EQ(0, state.buttons_down);
+ EXPECT_EQ(1, state.touch_cnt);
+
+ ASSERT_EQ(1, state.finger_cnt);
+ const FingerState& finger = state.fingers[0];
+ EXPECT_EQ(123, finger.tracking_id);
+ EXPECT_NEAR(50, finger.position_x, EPSILON);
+ EXPECT_NEAR(100, finger.position_y, EPSILON);
+ EXPECT_NEAR(5, finger.touch_major, EPSILON);
+ EXPECT_NEAR(4, finger.touch_minor, EPSILON);
+ EXPECT_NEAR(42, finger.pressure, EPSILON);
+ EXPECT_NEAR(2, finger.orientation, EPSILON);
+ EXPECT_EQ(0u, finger.flags);
+
+ EXPECT_EQ(0, state.rel_x);
+ EXPECT_EQ(0, state.rel_y);
+ EXPECT_EQ(0, state.rel_wheel);
+ EXPECT_EQ(0, state.rel_wheel_hi_res);
+ EXPECT_EQ(0, state.rel_hwheel);
+ EXPECT_NEAR(0.0, state.msc_timestamp, EPSILON);
+}
+
+TEST_F(HardwareStateConverterTest, TwoFingers) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
+ processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 42);
+ processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 2);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 456);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, -20);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 40);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 8);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 7);
+ processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 21);
+ processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 1);
+
+ processAxis(conv, time, EV_ABS, ABS_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_PRESSURE, 42);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, time, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ ASSERT_EQ(2, schs->state.finger_cnt);
+ const FingerState& finger1 = schs->state.fingers[0];
+ EXPECT_EQ(123, finger1.tracking_id);
+ EXPECT_NEAR(50, finger1.position_x, EPSILON);
+ EXPECT_NEAR(100, finger1.position_y, EPSILON);
+ EXPECT_NEAR(5, finger1.touch_major, EPSILON);
+ EXPECT_NEAR(4, finger1.touch_minor, EPSILON);
+ EXPECT_NEAR(42, finger1.pressure, EPSILON);
+ EXPECT_NEAR(2, finger1.orientation, EPSILON);
+ EXPECT_EQ(0u, finger1.flags);
+
+ const FingerState& finger2 = schs->state.fingers[1];
+ EXPECT_EQ(456, finger2.tracking_id);
+ EXPECT_NEAR(-20, finger2.position_x, EPSILON);
+ EXPECT_NEAR(40, finger2.position_y, EPSILON);
+ EXPECT_NEAR(8, finger2.touch_major, EPSILON);
+ EXPECT_NEAR(7, finger2.touch_minor, EPSILON);
+ EXPECT_NEAR(21, finger2.pressure, EPSILON);
+ EXPECT_NEAR(1, finger2.orientation, EPSILON);
+ EXPECT_EQ(0u, finger2.flags);
+}
+
+TEST_F(HardwareStateConverterTest, ButtonPressed) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_KEY, BTN_LEFT, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(GESTURES_BUTTON_LEFT, schs->state.buttons_down);
+}
+
+TEST_F(HardwareStateConverterTest, MscTimestamp) {
+ const nsecs_t time = ARBITRARY_TIME;
+ mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_MSC, MSC_TIMESTAMP, 1200000);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_NEAR(1.2, schs->state.msc_timestamp, EPSILON);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index aaf50ce..d2ff097 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -22,12 +22,14 @@
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
#include <fcntl.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
#include <linux/input.h>
#include <sys/epoll.h>
#include <cinttypes>
+#include <compare>
#include <thread>
#include <unordered_set>
#include <vector>
@@ -43,6 +45,7 @@
namespace android::inputdispatcher {
using namespace ftl::flag_operators;
+using testing::AllOf;
// An arbitrary time value.
static constexpr nsecs_t ARBITRARY_TIME = 1234;
@@ -54,6 +57,7 @@
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;
+static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
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 =
@@ -82,6 +86,7 @@
struct PointF {
float x;
float y;
+ auto operator<=>(const PointF&) const = default;
};
/**
@@ -102,6 +107,57 @@
<< MotionEvent::actionToString(receivedAction);
}
+MATCHER_P(WithMotionAction, action, "MotionEvent with specified action") {
+ bool matches = action == arg.getAction();
+ if (!matches) {
+ *result_listener << "expected action " << MotionEvent::actionToString(action)
+ << ", but got " << MotionEvent::actionToString(arg.getAction());
+ }
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "downTime should match eventTime for ACTION_DOWN events";
+ matches &= arg.getDownTime() == arg.getEventTime();
+ }
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "expected FLAG_CANCELED to be set with ACTION_CANCEL, but was not set";
+ matches &= (arg.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+}
+
+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_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(0 /*pointerIndex*/) == x && arg.getY(0 /*pointerIndex*/) == y;
+}
+
+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 {
@@ -1186,6 +1242,7 @@
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
InputEvent* event = consume();
+ ASSERT_NE(nullptr, event);
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
@@ -1205,6 +1262,12 @@
mInputReceiver->consumeCaptureEvent(hasCapture);
}
+ void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ MotionEvent* motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
+ ASSERT_THAT(*motionEvent, matcher);
+ }
+
void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
@@ -1892,6 +1955,64 @@
wallpaperWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ window->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setIsWallpaper(true);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ wallpaperWindow->setPreventSplitting(true);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionPointerDown(1);
+ wallpaperWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ const MotionEvent secondFingerUpEvent =
+ MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionPointerUp(1);
+ wallpaperWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
/**
* On the display, have a single window, and also an area where there's no window.
* First pointer touches the "no window" area of the screen. Second pointer touches the window.
@@ -1968,6 +2089,7 @@
mDispatcher->waitForIdle();
InputEvent* inputEvent1 = window1->consume();
+ ASSERT_NE(inputEvent1, nullptr);
window2->assertNoEvents();
MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
@@ -1977,6 +2099,7 @@
mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
mDispatcher->waitForIdle();
InputEvent* inputEvent2 = window2->consume();
+ ASSERT_NE(inputEvent2, nullptr);
MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
@@ -1986,17 +2109,13 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent3 = window2->consume();
- MotionEvent& motionEvent3 = static_cast<MotionEvent&>(*inputEvent3);
- ASSERT_EQ(motionEvent3.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// Now add new touch down on the second window
mDispatcher->notifyMotion(
&(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent4 = window2->consume();
- MotionEvent& motionEvent4 = static_cast<MotionEvent&>(*inputEvent4);
- ASSERT_EQ(motionEvent4.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
window1->consumeMotionMove();
@@ -2006,16 +2125,12 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent5 = window1->consume();
- MotionEvent& motionEvent5 = static_cast<MotionEvent&>(*inputEvent5);
- ASSERT_EQ(motionEvent5.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
mDispatcher->notifyMotion(&(
args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent6 = window1->consume();
- MotionEvent& motionEvent6 = static_cast<MotionEvent&>(*inputEvent6);
- ASSERT_EQ(motionEvent6.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
}
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -2040,10 +2155,8 @@
.x(900)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2054,12 +2167,9 @@
.x(300)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2082,8 +2192,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2095,8 +2204,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2117,12 +2225,47 @@
.x(900)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+ // No more events
+ windowLeft->assertNoEvents();
+ windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
+ 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, 600, 800));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+ // Send mouse cursor to the window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(100)
+ .y(100))
+ .build()));
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
}
// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
@@ -2145,8 +2288,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2169,8 +2311,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2182,8 +2323,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2203,8 +2343,102 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+}
+
+/**
+ * Inject a mouse hover event followed by a tap from touchscreen.
+ * In the current implementation, the tap does not cause a HOVER_EXIT event.
+ */
+TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
+ 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, 100, 100));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // 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);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE))));
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE))));
+
+ // Tap on the window
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{10, 10}});
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{10, 10}});
+ mDispatcher->notifyMotion(&motionArgs);
+ ASSERT_NO_FATAL_FAILURE(
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
+}
+
+TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> windowDefaultDisplay =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay",
+ ADISPLAY_ID_DEFAULT);
+ windowDefaultDisplay->setFrame(Rect(0, 0, 600, 800));
+ sp<FakeWindowHandle> windowSecondDisplay =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "SecondDisplay",
+ SECOND_DISPLAY_ID);
+ windowSecondDisplay->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
+ {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+
+ // Set cursor position in window in default display and check that hover enter and move
+ // events are generated.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(300)
+ .y(600))
+ .build()));
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+ // Remove all windows in secondary display and check that no event happens on window in
+ // primary display.
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
+ windowDefaultDisplay->assertNoEvents();
+
+ // Move cursor position in window in default display and check that only hover move
+ // event is generated and not hover enter event.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}},
+ {SECOND_DISPLAY_ID, {windowSecondDisplay}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(400)
+ .y(700))
+ .build()));
+ windowDefaultDisplay->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+ windowDefaultDisplay->assertNoEvents();
}
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
@@ -2323,6 +2557,39 @@
}
/**
+ * Two windows. First is a regular window. Second does not overlap with the first, and has
+ * WATCH_OUTSIDE_TOUCH.
+ * Both windows are owned by the same UID.
+ * Tap first window. Make sure that the second window receives ACTION_OUTSIDE with correct, non-zero
+ * coordinates. The coordinates are not zeroed out because both windows are owned by the same UID.
+ */
+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);
+ window->setFrame(Rect{0, 0, 100, 100});
+
+ sp<FakeWindowHandle> outsideWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+ ADISPLAY_ID_DEFAULT);
+ outsideWindow->setFrame(Rect{100, 100, 200, 200});
+ outsideWindow->setWatchOutsideTouch(true);
+ // outsideWindow must be above 'window' to receive ACTION_OUTSIDE events when 'window' is tapped
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {outsideWindow, window}}});
+
+ // Tap on first window.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown();
+ // The coordinates of the tap in 'outsideWindow' are relative to its top left corner.
+ // Therefore, we should offset them by (100, 100) relative to the screen's top left corner.
+ outsideWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithCoords(-50, -50)));
+}
+
+/**
* This test documents the behavior of WATCH_OUTSIDE_TOUCH. The window will get ACTION_OUTSIDE when
* a another pointer causes ACTION_DOWN to be sent to another window for the first time. Only one
* ACTION_OUTSIDE event is sent per gesture.
@@ -2357,7 +2624,9 @@
motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{PointF{-10, -10}, PointF{105, 105}});
mDispatcher->notifyMotion(&motionArgs);
- window->consumeMotionOutside();
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{-10, -10}}, {1, PointF{105, 105}}};
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithPointers(expectedPointers)));
secondWindow->consumeMotionDown();
thirdWindow->assertNoEvents();
@@ -2400,6 +2669,43 @@
window->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+ // Ensure window is non-split and have some transform.
+ window->setPreventSplitting(true);
+ window->setWindowOffset(20, 40);
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(-30)
+ .y(-50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ const MotionEvent* event = window->consumeMotion();
+ EXPECT_EQ(POINTER_1_DOWN, event->getAction());
+ EXPECT_EQ(70, event->getX(0)); // 50 + 20
+ EXPECT_EQ(90, event->getY(0)); // 50 + 40
+ EXPECT_EQ(-10, event->getX(1)); // -30 + 20
+ EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+}
+
/**
* Ensure the correct coordinate spaces are used by InputDispatcher.
*
@@ -4449,6 +4755,7 @@
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
assertMotionAction(expectedAction, motionEvent.getAction());
+ ASSERT_EQ(points.size(), motionEvent.getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
new file mode 100644
index 0000000..a02ef05
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputMapperTest.h"
+
+#include <InputReaderBase.h>
+#include <gtest/gtest.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
+const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+ ftl::Flags<InputDeviceClass>(0); // not needed for current tests
+
+void InputMapperTest::SetUp(ftl::Flags<InputDeviceClass> classes, int bus) {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
+ // Consume the device reset notification generated when adding a new device.
+ mFakeListener->assertNotifyDeviceResetWasCalled();
+}
+
+void InputMapperTest::SetUp() {
+ SetUp(DEVICE_CLASSES);
+}
+
+void InputMapperTest::TearDown() {
+ mFakeListener.reset();
+ mFakePolicy.clear();
+}
+
+void InputMapperTest::addConfigurationProperty(const char* key, const char* value) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
+}
+
+std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
+ if (!changes ||
+ (changes &
+ (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ std::list<NotifyArgs> out =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& args : out) {
+ mFakeListener->notify(args);
+ }
+ mReader->loopOnce();
+ return out;
+}
+
+std::shared_ptr<InputDevice> InputMapperTest::newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location,
+ int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes,
+ int bus) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ identifier.bus = bus;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes, bus);
+ mReader->loopOnce();
+ return device;
+}
+
+void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
+ uniqueId, physicalPort, viewportType);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
+void InputMapperTest::clearViewports() {
+ mFakePolicy->clearViewports();
+}
+
+std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when, nsecs_t readTime,
+ int32_t type, int32_t code, int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = mapper.getDeviceContext().getEventHubId();
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::list<NotifyArgs> processArgList = mapper.process(&event);
+ for (const NotifyArgs& args : processArgList) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return processArgList;
+}
+
+void InputMapperTest::resetMapper(InputMapper& mapper, nsecs_t when) {
+ const auto resetArgs = mapper.reset(when);
+ for (const auto args : resetArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+}
+
+std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_t when) {
+ std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+}
+
+void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+ float min, float max, float flat, float fuzz) {
+ const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
+ ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
+ ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
+ ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
+}
+
+void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y,
+ float pressure, float size, float touchMajor,
+ float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance,
+ float scaledAxisEpsilon) {
+ ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
+ ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
+ ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
+ ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
+ ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
+ ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
new file mode 100644
index 0000000..63ca44c
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class InputMapperTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t DEVICE_GENERATION = 2;
+ static constexpr int32_t DEVICE_CONTROLLER_NUMBER = 0;
+ static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ void SetUp() override;
+ void TearDown() override;
+
+ void addConfigurationProperty(const char* key, const char* value);
+ std::list<NotifyArgs> configureDevice(uint32_t changes);
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ template <class T, typename... Args>
+ T& addMapperAndConfigure(Args... args) {
+ T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
+ configureDevice(0);
+ std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+ resetArgList += mapper.reset(ARBITRARY_TIME);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& loopArgs : resetArgList) {
+ mFakeListener->notify(loopArgs);
+ }
+ mReader->loopOnce();
+ return mapper;
+ }
+
+ void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType);
+ void clearViewports();
+ std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+ int32_t code, int32_t value);
+ void resetMapper(InputMapper& mapper, nsecs_t when);
+
+ std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when);
+
+ static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+ float min, float max, float flat, float fuzz);
+ static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
+ float size, float touchMajor, float touchMinor, float toolMajor,
+ float toolMinor, float orientation, float distance,
+ float scaledAxisEpsilon = 1.f);
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 5c5fc77..ef2080f 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -36,10 +36,18 @@
#include <UinputDevice.h>
#include <VibratorInputMapper.h>
#include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
+#include <ui/Rotation.h>
#include <thread>
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "FakePointerController.h"
+#include "InputMapperTest.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
#include "android/hardware/input/InputDeviceCountryCode.h"
#include "input/DisplayViewport.h"
#include "input/Input.h"
@@ -52,13 +60,6 @@
using testing::AllOf;
using std::chrono_literals::operator""ms;
-// Timeout for waiting for an expected event
-static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
-
-// An arbitrary time value.
-static constexpr nsecs_t ARBITRARY_TIME = 1234;
-static constexpr nsecs_t READ_TIME = 4321;
-
// Arbitrary display properties.
static constexpr int32_t DISPLAY_ID = 0;
static const std::string DISPLAY_UNIQUE_ID = "local:1";
@@ -79,10 +80,6 @@
static constexpr int32_t FIRST_TRACKING_ID = 0;
static constexpr int32_t SECOND_TRACKING_ID = 1;
static constexpr int32_t THIRD_TRACKING_ID = 2;
-static constexpr int32_t DEFAULT_BATTERY = 1;
-static constexpr int32_t BATTERY_STATUS = 4;
-static constexpr int32_t BATTERY_CAPACITY = 66;
-static const std::string BATTERY_DEVPATH = "/sys/devices/mydevice/power_supply/mybattery";
static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000;
static constexpr int32_t LIGHT_COLOR = 0x7F448866;
static constexpr int32_t LIGHT_PLAYER_ID = 2;
@@ -96,9 +93,6 @@
static constexpr int32_t ACTION_POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-// Error tolerance for floating point assertions.
-static const float EPSILON = 0.001f;
-
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
// Maximum smoothing time delta so that we don't generate events too far into the future.
@@ -118,12 +112,12 @@
{"green", LightColor::GREEN},
{"blue", LightColor::BLUE}};
-static int32_t getInverseRotation(int32_t orientation) {
+static ui::Rotation getInverseRotation(ui::Rotation orientation) {
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- return DISPLAY_ORIENTATION_270;
- case DISPLAY_ORIENTATION_270:
- return DISPLAY_ORIENTATION_90;
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
default:
return orientation;
}
@@ -157,960 +151,6 @@
}
}
-// --- FakePointerController ---
-
-class FakePointerController : public PointerControllerInterface {
- bool mHaveBounds;
- float mMinX, mMinY, mMaxX, mMaxY;
- float mX, mY;
- int32_t mButtonState;
- int32_t mDisplayId;
-
-public:
- FakePointerController() :
- mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
- mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
- }
-
- virtual ~FakePointerController() {}
-
- void setBounds(float minX, float minY, float maxX, float maxY) {
- mHaveBounds = true;
- mMinX = minX;
- mMinY = minY;
- mMaxX = maxX;
- mMaxY = maxY;
- }
-
- void setPosition(float x, float y) override {
- mX = x;
- mY = y;
- }
-
- void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
-
- int32_t getButtonState() const override { return mButtonState; }
-
- void getPosition(float* outX, float* outY) const override {
- *outX = mX;
- *outY = mY;
- }
-
- int32_t getDisplayId() const override { return mDisplayId; }
-
- void setDisplayViewport(const DisplayViewport& viewport) override {
- mDisplayId = viewport.displayId;
- }
-
- const std::map<int32_t, std::vector<int32_t>>& getSpots() {
- return mSpotsByDisplay;
- }
-
-private:
- bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
- *outMinX = mMinX;
- *outMinY = mMinY;
- *outMaxX = mMaxX;
- *outMaxY = mMaxY;
- return mHaveBounds;
- }
-
- void move(float deltaX, float deltaY) override {
- mX += deltaX;
- if (mX < mMinX) mX = mMinX;
- if (mX > mMaxX) mX = mMaxX;
- mY += deltaY;
- if (mY < mMinY) mY = mMinY;
- if (mY > mMaxY) mY = mMaxY;
- }
-
- void fade(Transition) override {}
-
- void unfade(Transition) override {}
-
- void setPresentation(Presentation) override {}
-
- void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
- int32_t displayId) override {
- std::vector<int32_t> newSpots;
- // Add spots for fingers that are down.
- for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
- uint32_t id = idBits.clearFirstMarkedBit();
- newSpots.push_back(id);
- }
-
- mSpotsByDisplay[displayId] = newSpots;
- }
-
- void clearSpots() override { mSpotsByDisplay.clear(); }
-
- std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
-};
-
-
-// --- FakeInputReaderPolicy ---
-
-class FakeInputReaderPolicy : public InputReaderPolicyInterface {
- std::mutex mLock;
- std::condition_variable mDevicesChangedCondition;
-
- InputReaderConfiguration mConfig;
- std::shared_ptr<FakePointerController> mPointerController;
- std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
- bool mInputDevicesChanged GUARDED_BY(mLock){false};
- std::vector<DisplayViewport> mViewports;
- TouchAffineTransformation transform;
- std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){};
-
-protected:
- virtual ~FakeInputReaderPolicy() {}
-
-public:
- FakeInputReaderPolicy() {
- }
-
- void assertInputDevicesChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (!devicesChanged) {
- FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
- }
- });
- }
-
- void assertInputDevicesNotChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (devicesChanged) {
- FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
- }
- });
- }
-
- void assertStylusGestureNotified(int32_t deviceId) {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mStylusGestureNotified);
- ASSERT_EQ(deviceId, *mStylusGestureNotified);
- mStylusGestureNotified.reset();
- }
-
- void assertStylusGestureNotNotified() {
- std::scoped_lock lock(mLock);
- ASSERT_FALSE(mStylusGestureNotified);
- }
-
- virtual void clearViewports() {
- mViewports.clear();
- mConfig.setDisplayViewports(mViewports);
- }
-
- std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
- return mConfig.getDisplayViewportByUniqueId(uniqueId);
- }
- std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
- return mConfig.getDisplayViewportByType(type);
- }
-
- std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
- return mConfig.getDisplayViewportByPort(displayPort);
- }
-
- void addDisplayViewport(DisplayViewport viewport) {
- mViewports.push_back(std::move(viewport));
- mConfig.setDisplayViewports(mViewports);
- }
-
- void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType type) {
- const bool isRotated =
- (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- v.isActive = isActive;
- v.uniqueId = uniqueId;
- v.physicalPort = physicalPort;
- v.type = type;
-
- addDisplayViewport(v);
- }
-
- bool updateViewport(const DisplayViewport& viewport) {
- size_t count = mViewports.size();
- for (size_t i = 0; i < count; i++) {
- const DisplayViewport& currentViewport = mViewports[i];
- if (currentViewport.displayId == viewport.displayId) {
- mViewports[i] = viewport;
- mConfig.setDisplayViewports(mViewports);
- return true;
- }
- }
- // no viewport found.
- return false;
- }
-
- void addExcludedDeviceName(const std::string& deviceName) {
- mConfig.excludedDeviceNames.push_back(deviceName);
- }
-
- void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
- mConfig.portAssociations.insert({inputPort, displayPort});
- }
-
- void addInputUniqueIdAssociation(const std::string& inputUniqueId,
- const std::string& displayUniqueId) {
- mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
- }
-
- void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
-
- void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
-
- void setPointerController(std::shared_ptr<FakePointerController> controller) {
- mPointerController = std::move(controller);
- }
-
- const InputReaderConfiguration* getReaderConfiguration() const {
- return &mConfig;
- }
-
- const std::vector<InputDeviceInfo>& getInputDevices() const {
- return mInputDevices;
- }
-
- TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) {
- return transform;
- }
-
- void setTouchAffineTransformation(const TouchAffineTransformation t) {
- transform = t;
- }
-
- PointerCaptureRequest setPointerCapture(bool enabled) {
- mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
- return mConfig.pointerCaptureRequest;
- }
-
- void setShowTouches(bool enabled) {
- mConfig.showTouches = enabled;
- }
-
- void setDefaultPointerDisplayId(int32_t pointerDisplayId) {
- mConfig.defaultPointerDisplayId = pointerDisplayId;
- }
-
- void setPointerGestureEnabled(bool enabled) { mConfig.pointerGesturesEnabled = enabled; }
-
- float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
-
- float getPointerGestureZoomSpeedRatio() { return mConfig.pointerGestureZoomSpeedRatio; }
-
- void setVelocityControlParams(const VelocityControlParameters& params) {
- mConfig.pointerVelocityControlParameters = params;
- mConfig.wheelVelocityControlParameters = params;
- }
-
-private:
- uint32_t mNextPointerCaptureSequenceNumber = 0;
-
- void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
- *outConfig = mConfig;
- }
-
- std::shared_ptr<PointerControllerInterface> obtainPointerController(
- int32_t /*deviceId*/) override {
- return mPointerController;
- }
-
- void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
- std::scoped_lock<std::mutex> lock(mLock);
- mInputDevices = inputDevices;
- mInputDevicesChanged = true;
- mDevicesChangedCondition.notify_all();
- }
-
- std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier&) override {
- return nullptr;
- }
-
- std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
-
- void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
- std::unique_lock<std::mutex> lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- const bool devicesChanged =
- mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
- return mInputDevicesChanged;
- });
- ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
- mInputDevicesChanged = false;
- }
-
- void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override {
- std::scoped_lock<std::mutex> lock(mLock);
- mStylusGestureNotified = deviceId;
- }
-};
-
-// --- FakeEventHub ---
-
-class FakeEventHub : public EventHubInterface {
- struct KeyInfo {
- int32_t keyCode;
- uint32_t flags;
- };
-
- struct SensorInfo {
- InputDeviceSensorType sensorType;
- int32_t sensorDataIndex;
- };
-
- struct Device {
- InputDeviceIdentifier identifier;
- ftl::Flags<InputDeviceClass> classes;
- PropertyMap configuration;
- KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes;
- KeyedVector<int, bool> relativeAxes;
- KeyedVector<int32_t, int32_t> keyCodeStates;
- KeyedVector<int32_t, int32_t> scanCodeStates;
- KeyedVector<int32_t, int32_t> switchStates;
- KeyedVector<int32_t, int32_t> absoluteAxisValue;
- KeyedVector<int32_t, KeyInfo> keysByScanCode;
- KeyedVector<int32_t, KeyInfo> keysByUsageCode;
- KeyedVector<int32_t, bool> leds;
- // fake mapping which would normally come from keyCharacterMap
- std::unordered_map<int32_t, int32_t> keyCodeMapping;
- std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
- BitArray<MSC_MAX> mscBitmask;
- std::vector<VirtualKeyDefinition> virtualKeys;
- bool enabled;
- InputDeviceCountryCode countryCode;
-
- status_t enable() {
- enabled = true;
- return OK;
- }
-
- status_t disable() {
- enabled = false;
- return OK;
- }
-
- explicit Device(ftl::Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {}
- };
-
- std::mutex mLock;
- std::condition_variable mEventsCondition;
-
- KeyedVector<int32_t, Device*> mDevices;
- std::vector<std::string> mExcludedDevices;
- std::vector<RawEvent> mEvents GUARDED_BY(mLock);
- std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
- std::vector<int32_t> mVibrators = {0, 1};
- std::unordered_map<int32_t, RawLightInfo> mRawLightInfos;
- // Simulates a device light brightness, from light id to light brightness.
- std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness;
- // Simulates a device light intensities, from light id to light intensities map.
- std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>>
- mLightIntensities;
-
-public:
- virtual ~FakeEventHub() {
- for (size_t i = 0; i < mDevices.size(); i++) {
- delete mDevices.valueAt(i);
- }
- }
-
- FakeEventHub() { }
-
- void addDevice(int32_t deviceId, const std::string& name, ftl::Flags<InputDeviceClass> classes,
- int bus = 0) {
- Device* device = new Device(classes);
- device->identifier.name = name;
- device->identifier.bus = bus;
- mDevices.add(deviceId, device);
-
- enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
- }
-
- void removeDevice(int32_t deviceId) {
- delete mDevices.valueFor(deviceId);
- mDevices.removeItem(deviceId);
-
- enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
- }
-
- bool isDeviceEnabled(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- if (device == nullptr) {
- ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
- return false;
- }
- return device->enabled;
- }
-
- status_t enableDevice(int32_t deviceId) override {
- status_t result;
- Device* device = getDevice(deviceId);
- if (device == nullptr) {
- ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
- return BAD_VALUE;
- }
- if (device->enabled) {
- ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId);
- return OK;
- }
- result = device->enable();
- return result;
- }
-
- status_t disableDevice(int32_t deviceId) override {
- Device* device = getDevice(deviceId);
- if (device == nullptr) {
- ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__);
- return BAD_VALUE;
- }
- if (!device->enabled) {
- ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId);
- return OK;
- }
- return device->disable();
- }
-
- void finishDeviceScan() {
- enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
- }
-
- void addConfigurationProperty(int32_t deviceId, const char* key, const char* value) {
- Device* device = getDevice(deviceId);
- device->configuration.addProperty(key, value);
- }
-
- void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
- Device* device = getDevice(deviceId);
- device->configuration.addAll(configuration);
- }
-
- void addAbsoluteAxis(int32_t deviceId, int axis,
- int32_t minValue, int32_t maxValue, int flat, int fuzz, int resolution = 0) {
- Device* device = getDevice(deviceId);
-
- RawAbsoluteAxisInfo info;
- info.valid = true;
- info.minValue = minValue;
- info.maxValue = maxValue;
- info.flat = flat;
- info.fuzz = fuzz;
- info.resolution = resolution;
- device->absoluteAxes.add(axis, info);
- }
-
- void addRelativeAxis(int32_t deviceId, int32_t axis) {
- Device* device = getDevice(deviceId);
- device->relativeAxes.add(axis, true);
- }
-
- void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->keyCodeStates.replaceValueFor(keyCode, state);
- }
-
- void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
- Device* device = getDevice(deviceId);
- device->countryCode = countryCode;
- }
-
- void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->scanCodeStates.replaceValueFor(scanCode, state);
- }
-
- void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
- Device* device = getDevice(deviceId);
- device->switchStates.replaceValueFor(switchCode, state);
- }
-
- void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) {
- Device* device = getDevice(deviceId);
- device->absoluteAxisValue.replaceValueFor(axis, value);
- }
-
- void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
- int32_t keyCode, uint32_t flags) {
- Device* device = getDevice(deviceId);
- KeyInfo info;
- info.keyCode = keyCode;
- info.flags = flags;
- if (scanCode) {
- device->keysByScanCode.add(scanCode, info);
- }
- if (usageCode) {
- device->keysByUsageCode.add(usageCode, info);
- }
- }
-
- void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) {
- Device* device = getDevice(deviceId);
- device->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
- }
-
- void addLed(int32_t deviceId, int32_t led, bool initialState) {
- Device* device = getDevice(deviceId);
- device->leds.add(led, initialState);
- }
-
- void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
- int32_t sensorDataIndex) {
- Device* device = getDevice(deviceId);
- SensorInfo info;
- info.sensorType = sensorType;
- info.sensorDataIndex = sensorDataIndex;
- device->sensorsByAbsCode.emplace(absCode, info);
- }
-
- void setMscEvent(int32_t deviceId, int32_t mscEvent) {
- Device* device = getDevice(deviceId);
- typename BitArray<MSC_MAX>::Buffer buffer;
- buffer[mscEvent / 32] = 1 << mscEvent % 32;
- device->mscBitmask.loadFromBuffer(buffer);
- }
-
- void addRawLightInfo(int32_t rawId, RawLightInfo&& info) {
- mRawLightInfos.emplace(rawId, std::move(info));
- }
-
- void fakeLightBrightness(int32_t rawId, int32_t brightness) {
- mLightBrightness.emplace(rawId, brightness);
- }
-
- void fakeLightIntensities(int32_t rawId,
- const std::unordered_map<LightColor, int32_t> intensities) {
- mLightIntensities.emplace(rawId, std::move(intensities));
- }
-
- bool getLedState(int32_t deviceId, int32_t led) {
- Device* device = getDevice(deviceId);
- return device->leds.valueFor(led);
- }
-
- std::vector<std::string>& getExcludedDevices() {
- return mExcludedDevices;
- }
-
- void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) {
- Device* device = getDevice(deviceId);
- device->virtualKeys.push_back(definition);
- }
-
- void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
- int32_t value) {
- std::scoped_lock<std::mutex> lock(mLock);
- RawEvent event;
- event.when = when;
- event.readTime = readTime;
- event.deviceId = deviceId;
- event.type = type;
- event.code = code;
- event.value = value;
- mEvents.push_back(event);
-
- if (type == EV_ABS) {
- setAbsoluteAxisValue(deviceId, code, value);
- }
- }
-
- void setVideoFrames(std::unordered_map<int32_t /*deviceId*/,
- std::vector<TouchVideoFrame>> videoFrames) {
- mVideoFrames = std::move(videoFrames);
- }
-
- void assertQueueIsEmpty() {
- std::unique_lock<std::mutex> lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
- const bool queueIsEmpty =
- mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
- [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
- if (!queueIsEmpty) {
- FAIL() << "Timed out waiting for EventHub queue to be emptied.";
- }
- }
-
-private:
- Device* getDevice(int32_t deviceId) const {
- ssize_t index = mDevices.indexOfKey(deviceId);
- return index >= 0 ? mDevices.valueAt(index) : nullptr;
- }
-
- ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- return device ? device->classes : ftl::Flags<InputDeviceClass>(0);
- }
-
- InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- return device ? device->identifier : InputDeviceIdentifier();
- }
-
- int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
-
- void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- *outConfiguration = device->configuration;
- }
- }
-
- status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
- RawAbsoluteAxisInfo* outAxisInfo) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->absoluteAxes.indexOfKey(axis);
- if (index >= 0) {
- *outAxisInfo = device->absoluteAxes.valueAt(index);
- return OK;
- }
- }
- outAxisInfo->clear();
- return -1;
- }
-
- bool hasRelativeAxis(int32_t deviceId, int axis) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- return device->relativeAxes.indexOfKey(axis) >= 0;
- }
- return false;
- }
-
- bool hasInputProperty(int32_t, int) const override { return false; }
-
- bool hasMscEvent(int32_t deviceId, int mscEvent) const override final {
- Device* device = getDevice(deviceId);
- if (device) {
- return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
- }
- return false;
- }
-
- status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
- int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- const KeyInfo* key = getKey(device, scanCode, usageCode);
- if (key) {
- if (outKeycode) {
- *outKeycode = key->keyCode;
- }
- if (outFlags) {
- *outFlags = key->flags;
- }
- if (outMetaState) {
- *outMetaState = metaState;
- }
- return OK;
- }
- }
- return NAME_NOT_FOUND;
- }
-
- const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const {
- if (usageCode) {
- ssize_t index = device->keysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- return &device->keysByUsageCode.valueAt(index);
- }
- }
- if (scanCode) {
- ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- return &device->keysByScanCode.valueAt(index);
- }
- }
- return nullptr;
- }
-
- status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
-
- base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
- int32_t deviceId, int32_t absCode) const override {
- Device* device = getDevice(deviceId);
- if (!device) {
- return Errorf("Sensor device not found.");
- }
- auto it = device->sensorsByAbsCode.find(absCode);
- if (it == device->sensorsByAbsCode.end()) {
- return Errorf("Sensor map not found.");
- }
- const SensorInfo& info = it->second;
- return std::make_pair(info.sensorType, info.sensorDataIndex);
- }
-
- void setExcludedDevices(const std::vector<std::string>& devices) override {
- mExcludedDevices = devices;
- }
-
- std::vector<RawEvent> getEvents(int) override {
- std::scoped_lock lock(mLock);
-
- std::vector<RawEvent> buffer;
- std::swap(buffer, mEvents);
-
- mEventsCondition.notify_all();
- return buffer;
- }
-
- std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
- auto it = mVideoFrames.find(deviceId);
- if (it != mVideoFrames.end()) {
- std::vector<TouchVideoFrame> frames = std::move(it->second);
- mVideoFrames.erase(deviceId);
- return frames;
- }
- return {};
- }
-
- int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
- if (index >= 0) {
- return device->scanCodeStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- return device->countryCode;
- }
- return InputDeviceCountryCode::INVALID;
- }
-
- int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
- if (index >= 0) {
- return device->keyCodeStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->switchStates.indexOfKey(sw);
- if (index >= 0) {
- return device->switchStates.valueAt(index);
- }
- }
- return AKEY_STATE_UNKNOWN;
- }
-
- status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
- int32_t* outValue) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
- if (index >= 0) {
- *outValue = device->absoluteAxisValue.valueAt(index);
- return OK;
- }
- }
- *outValue = 0;
- return -1;
- }
-
- int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
- Device* device = getDevice(deviceId);
- if (!device) {
- return AKEYCODE_UNKNOWN;
- }
- auto it = device->keyCodeMapping.find(locationKeyCode);
- return it != device->keyCodeMapping.end() ? it->second : locationKeyCode;
- }
-
- // Return true if the device has non-empty key layout.
- bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
- uint8_t* outFlags) const override {
- bool result = false;
- Device* device = getDevice(deviceId);
- if (device) {
- result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
- for (size_t i = 0; i < keyCodes.size(); i++) {
- for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
- if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
- outFlags[i] = 1;
- }
- }
- for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
- if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
- outFlags[i] = 1;
- }
- }
- }
- }
- return result;
- }
-
- bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
- return index >= 0;
- }
- return false;
- }
-
- bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
- Device* device = getDevice(deviceId);
- if (!device) {
- return false;
- }
- for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
- if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
- return true;
- }
- }
- for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
- if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
- return true;
- }
- }
- return false;
- }
-
- bool hasLed(int32_t deviceId, int32_t led) const override {
- Device* device = getDevice(deviceId);
- return device && device->leds.indexOfKey(led) >= 0;
- }
-
- void setLedState(int32_t deviceId, int32_t led, bool on) override {
- Device* device = getDevice(deviceId);
- if (device) {
- ssize_t index = device->leds.indexOfKey(led);
- if (index >= 0) {
- device->leds.replaceValueAt(led, on);
- } else {
- ADD_FAILURE()
- << "Attempted to set the state of an LED that the EventHub declared "
- "was not present. led=" << led;
- }
- }
- }
-
- void getVirtualKeyDefinitions(
- int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
- outVirtualKeys.clear();
-
- Device* device = getDevice(deviceId);
- if (device) {
- outVirtualKeys = device->virtualKeys;
- }
- }
-
- const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
- return nullptr;
- }
-
- bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
- return false;
- }
-
- void vibrate(int32_t, const VibrationElement&) override {}
-
- void cancelVibrate(int32_t) override {}
-
- std::vector<int32_t> getVibratorIds(int32_t deviceId) const override { return mVibrators; };
-
- std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override {
- return BATTERY_CAPACITY;
- }
-
- std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override {
- return BATTERY_STATUS;
- }
-
- std::vector<int32_t> getRawBatteryIds(int32_t deviceId) const override {
- return {DEFAULT_BATTERY};
- }
-
- std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId,
- int32_t batteryId) const override {
- if (batteryId != DEFAULT_BATTERY) return {};
- static const auto BATTERY_INFO = RawBatteryInfo{.id = DEFAULT_BATTERY,
- .name = "default battery",
- .flags = InputBatteryClass::CAPACITY,
- .path = BATTERY_DEVPATH};
- return BATTERY_INFO;
- }
-
- std::vector<int32_t> getRawLightIds(int32_t deviceId) const override {
- std::vector<int32_t> ids;
- for (const auto& [rawId, info] : mRawLightInfos) {
- ids.push_back(rawId);
- }
- return ids;
- }
-
- std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) const override {
- auto it = mRawLightInfos.find(lightId);
- if (it == mRawLightInfos.end()) {
- return std::nullopt;
- }
- return it->second;
- }
-
- void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override {
- mLightBrightness.emplace(lightId, brightness);
- }
-
- void setLightIntensities(int32_t deviceId, int32_t lightId,
- std::unordered_map<LightColor, int32_t> intensities) override {
- mLightIntensities.emplace(lightId, intensities);
- };
-
- std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override {
- auto lightIt = mLightBrightness.find(lightId);
- if (lightIt == mLightBrightness.end()) {
- return std::nullopt;
- }
- return lightIt->second;
- }
-
- std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
- int32_t deviceId, int32_t lightId) const override {
- auto lightIt = mLightIntensities.find(lightId);
- if (lightIt == mLightIntensities.end()) {
- return std::nullopt;
- }
- return lightIt->second;
- };
-
- void dump(std::string&) const override {}
-
- void monitor() const override {}
-
- void requestReopenDevices() override {}
-
- void wake() override {}
-};
-
// --- FakeInputMapper ---
class FakeInputMapper : public InputMapper {
@@ -1304,117 +344,6 @@
}
};
-
-// --- InstrumentedInputReader ---
-
-class InstrumentedInputReader : public InputReader {
- std::queue<std::shared_ptr<InputDevice>> mNextDevices;
-
-public:
- InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- InputListenerInterface& listener)
- : InputReader(eventHub, policy, listener), mFakeContext(this) {}
-
- virtual ~InstrumentedInputReader() {}
-
- void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location = "") {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- int32_t generation = deviceId + 1;
- return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
- }
-
- // Make the protected loopOnce method accessible to tests.
- using InputReader::loopOnce;
-
-protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId,
- const InputDeviceIdentifier& identifier)
- REQUIRES(mLock) {
- if (!mNextDevices.empty()) {
- std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
- mNextDevices.pop();
- return device;
- }
- return InputReader::createDeviceLocked(eventHubId, identifier);
- }
-
- // --- FakeInputReaderContext ---
- class FakeInputReaderContext : public ContextImpl {
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
- std::optional<nsecs_t> mRequestedTimeout;
- std::vector<InputDeviceInfo> mExternalStylusDevices;
-
- public:
- FakeInputReaderContext(InputReader* reader)
- : ContextImpl(reader),
- mGlobalMetaState(0),
- mUpdateGlobalMetaStateWasCalled(false),
- mGeneration(1) {}
-
- virtual ~FakeInputReaderContext() {}
-
- void assertUpdateGlobalMetaStateWasCalled() {
- ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
- << "Expected updateGlobalMetaState() to have been called.";
- mUpdateGlobalMetaStateWasCalled = false;
- }
-
- void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
-
- uint32_t getGeneration() { return mGeneration; }
-
- void updateGlobalMetaState() override {
- mUpdateGlobalMetaStateWasCalled = true;
- ContextImpl::updateGlobalMetaState();
- }
-
- int32_t getGlobalMetaState() override {
- return mGlobalMetaState | ContextImpl::getGlobalMetaState();
- }
-
- int32_t bumpGeneration() override {
- mGeneration = ContextImpl::bumpGeneration();
- return mGeneration;
- }
-
- void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
-
- void assertTimeoutWasRequested(nsecs_t when) {
- ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
- << " but there was no timeout requested.";
- ASSERT_EQ(when, *mRequestedTimeout);
- mRequestedTimeout.reset();
- }
-
- void assertTimeoutWasNotRequested() {
- ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
- " but one was requested at time "
- << *mRequestedTimeout;
- }
-
- void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
- outDevices = mExternalStylusDevices;
- }
-
- void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
- mExternalStylusDevices = devices;
- }
- } mFakeContext;
-
- friend class InputReaderTest;
-
-public:
- FakeInputReaderContext* getContext() { return &mFakeContext; }
-};
-
// --- InputReaderPolicyTest ---
class InputReaderPolicyTest : public testing::Test {
protected:
@@ -1440,9 +369,8 @@
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId, NO_PORT, ViewportType::INTERNAL);
// Check matching by uniqueId
internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
@@ -1471,21 +399,21 @@
constexpr int32_t virtualDisplayId2 = 3;
// Add an internal viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, internalUniqueId,
- NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, internalUniqueId, NO_PORT,
+ ViewportType::INTERNAL);
// Add an external viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, externalUniqueId,
- NO_PORT, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, externalUniqueId, NO_PORT,
+ ViewportType::EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId1,
- NO_PORT, ViewportType::VIRTUAL);
+ ui::ROTATION_0, true /*isActive*/, virtualUniqueId1, NO_PORT,
+ ViewportType::VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, virtualUniqueId2,
- NO_PORT, ViewportType::VIRTUAL);
+ ui::ROTATION_0, true /*isActive*/, virtualUniqueId2, NO_PORT,
+ ViewportType::VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
@@ -1533,13 +461,11 @@
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1,
- NO_PORT, type);
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId1, NO_PORT, type);
// Add another viewport
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2,
- NO_PORT, type);
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId2, NO_PORT, type);
// Check that correct display viewport was returned by comparing the display IDs.
std::optional<DisplayViewport> viewport1 =
@@ -1579,10 +505,10 @@
// Add the default display first and ensure it gets returned.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
std::optional<DisplayViewport> viewport =
@@ -1594,10 +520,10 @@
// Add the default display second to make sure order doesn't matter.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
+ ui::ROTATION_0, true /*isActive*/, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
@@ -1621,13 +547,11 @@
mFakePolicy->clearViewports();
// Add a viewport that's associated with some display port that's not of interest.
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId1, hdmi3,
- type);
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId1, hdmi3, type);
// Add another viewport, connected to HDMI1 port
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, uniqueId2, hdmi1,
- type);
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, uniqueId2, hdmi1, type);
// Check that correct display viewport was returned by comparing the display ports.
std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
@@ -2080,11 +1004,10 @@
// Add default and second display.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, "local:0", NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, "local:0", NO_PORT, ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, "local:1", hdmi1,
+ ui::ROTATION_0, true /*isActive*/, "local:1", hdmi1,
ViewportType::EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
@@ -2283,8 +1206,9 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY), BATTERY_CAPACITY);
- ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+ ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY),
+ FakeEventHub::BATTERY_CAPACITY);
+ ASSERT_EQ(mReader->getBatteryCapacity(deviceId), FakeEventHub::BATTERY_CAPACITY);
}
TEST_F(InputReaderTest, BatteryGetStatus) {
@@ -2300,8 +1224,9 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY), BATTERY_STATUS);
- ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+ ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY),
+ FakeEventHub::BATTERY_STATUS);
+ ASSERT_EQ(mReader->getBatteryStatus(deviceId), FakeEventHub::BATTERY_STATUS);
}
TEST_F(InputReaderTest, BatteryGetDevicePath) {
@@ -2316,7 +1241,7 @@
ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
- ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), BATTERY_DEVPATH);
+ ASSERT_EQ(mReader->getBatteryDevicePath(deviceId), FakeEventHub::BATTERY_DEVPATH);
}
TEST_F(InputReaderTest, LightGetColor) {
@@ -2541,9 +1466,8 @@
#endif
InputReaderIntegrationTest::SetUp();
// At least add an internal display.
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2554,7 +1478,7 @@
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId,
+ ui::Rotation orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
@@ -3492,7 +2416,7 @@
// Prepare displays.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
+ ui::ROTATION_0, true /*isActive*/, UNIQUE_ID, hdmi,
ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3527,7 +2451,7 @@
// Device should be enabled when a display is found.
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3553,7 +2477,7 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
+ ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -3581,194 +2505,6 @@
ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
}
-// --- InputMapperTest ---
-
-class InputMapperTest : public testing::Test {
-protected:
- static const char* DEVICE_NAME;
- static const char* DEVICE_LOCATION;
- static const int32_t DEVICE_ID;
- static const int32_t DEVICE_GENERATION;
- static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
- static const int32_t EVENTHUB_ID;
-
- std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<FakeInputReaderPolicy> mFakePolicy;
- std::unique_ptr<TestInputListener> mFakeListener;
- std::unique_ptr<InstrumentedInputReader> mReader;
- std::shared_ptr<InputDevice> mDevice;
-
- virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0) {
- mFakeEventHub = std::make_unique<FakeEventHub>();
- mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakeListener = std::make_unique<TestInputListener>();
- mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- *mFakeListener);
- mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
- // Consume the device reset notification generated when adding a new device.
- mFakeListener->assertNotifyDeviceResetWasCalled();
- }
-
- void SetUp() override {
- SetUp(DEVICE_CLASSES);
- }
-
- void TearDown() override {
- mFakeListener.reset();
- mFakePolicy.clear();
- }
-
- void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
- }
-
- std::list<NotifyArgs> configureDevice(uint32_t changes) {
- if (!changes ||
- (changes &
- (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
- mReader->requestRefreshConfiguration(changes);
- mReader->loopOnce();
- }
- std::list<NotifyArgs> out =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
- // Loop the reader to flush the input listener queue.
- for (const NotifyArgs& args : out) {
- mFakeListener->notify(args);
- }
- mReader->loopOnce();
- return out;
- }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location, int32_t eventHubId,
- ftl::Flags<InputDeviceClass> classes, int bus = 0) {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- identifier.bus = bus;
- std::shared_ptr<InputDevice> device =
- std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
- identifier);
- mReader->pushNextDevice(device);
- mFakeEventHub->addDevice(eventHubId, name, classes, bus);
- mReader->loopOnce();
- return device;
- }
-
- template <class T, typename... Args>
- T& addMapperAndConfigure(Args... args) {
- T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
- configureDevice(0);
- std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
- resetArgList += mapper.reset(ARBITRARY_TIME);
- // Loop the reader to flush the input listener queue.
- for (const NotifyArgs& loopArgs : resetArgList) {
- mFakeListener->notify(loopArgs);
- }
- mReader->loopOnce();
- return mapper;
- }
-
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
- uniqueId, physicalPort, viewportType);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void clearViewports() {
- mFakePolicy->clearViewports();
- }
-
- std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
- int32_t code, int32_t value) {
- RawEvent event;
- event.when = when;
- event.readTime = readTime;
- event.deviceId = mapper.getDeviceContext().getEventHubId();
- event.type = type;
- event.code = code;
- event.value = value;
- std::list<NotifyArgs> processArgList = mapper.process(&event);
- for (const NotifyArgs& args : processArgList) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return processArgList;
- }
-
- void resetMapper(InputMapper& mapper, nsecs_t when) {
- const auto resetArgs = mapper.reset(when);
- for (const auto args : resetArgs) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- }
-
- std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) {
- std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
- for (const NotifyArgs& args : generatedArgs) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return generatedArgs;
- }
-
- static void assertMotionRange(const InputDeviceInfo& info,
- int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
- const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
- ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
- }
-
- static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
- float size, float touchMajor, float touchMinor, float toolMajor,
- float toolMinor, float orientation, float distance,
- float scaledAxisEpsilon = 1.f) {
- ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
- ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
- ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
- ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
- ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
- ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
- }
-
- static void assertPosition(const FakePointerController& controller, float x, float y) {
- float actualX, actualY;
- controller.getPosition(&actualX, &actualY);
- ASSERT_NEAR(x, actualX, 1);
- ASSERT_NEAR(y, actualY, 1);
- }
-};
-
-const char* InputMapperTest::DEVICE_NAME = "device";
-const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
-const int32_t InputMapperTest::DEVICE_GENERATION = 2;
-const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
- ftl::Flags<InputDeviceClass>(0); // not needed for current tests
-const int32_t InputMapperTest::EVENTHUB_ID = 1;
-
// --- SwitchInputMapperTest ---
class SwitchInputMapperTest : public InputMapperTest {
@@ -4027,8 +2763,8 @@
class KeyboardInputMapperTest : public InputMapperTest {
protected:
const std::string UNIQUE_ID = "local:0";
-
- void prepareDisplay(int32_t orientation);
+ const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
+ void prepareDisplay(ui::Rotation orientation);
void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
int32_t originalKeyCode, int32_t rotatedKeyCode,
@@ -4038,7 +2774,7 @@
/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
* orientation.
*/
-void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
+void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
}
@@ -4176,6 +2912,27 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
}
+TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
+ mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+ // Key down by scan code.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEYCODE_B, args.keyCode);
+
+ // Key up by scan code.
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEYCODE_B, args.keyCode);
+}
+
/**
* Ensure that the readTime is set to the time when the EV_KEY is received.
*/
@@ -4250,7 +3007,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -4272,7 +3029,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4283,7 +3040,7 @@
AKEYCODE_DPAD_LEFT, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4294,7 +3051,7 @@
AKEYCODE_DPAD_DOWN, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4305,7 +3062,7 @@
AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
ASSERT_NO_FATAL_FAILURE(
testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
@@ -4319,7 +3076,7 @@
// in the key up as we did in the key down.
NotifyKeyArgs args;
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
@@ -4327,7 +3084,7 @@
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -4352,7 +3109,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
@@ -4374,7 +3131,7 @@
// Display id should be ADISPLAY_ID_NONE without any display configuration.
// ^--- already checked by the previous test
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -4384,7 +3141,7 @@
constexpr int32_t newDisplayId = 2;
clearViewports();
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
@@ -4594,9 +3351,9 @@
// Prepare second display.
constexpr int32_t newDisplayId = 2;
- setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
- setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+ setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
// Default device will reconfigure above, need additional reconfiguration for another device.
unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
@@ -4825,6 +3582,24 @@
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags);
}
+TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) {
+ mDevice->addMapper<KeyboardInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+
+ mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
+
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION);
+
+ InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
+ ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
+ deviceInfo.getKeyboardLayoutInfo()->languageTag);
+ ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
+ deviceInfo.getKeyboardLayoutInfo()->layoutType);
+}
+
// --- KeyboardInputMapperTest_ExternalDevice ---
class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
@@ -4927,14 +3702,14 @@
void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
int32_t rotatedX, int32_t rotatedY);
- void prepareDisplay(int32_t orientation) {
+ void prepareDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
}
void prepareSecondaryDisplay() {
setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
+ ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
ViewportType::EXTERNAL);
}
@@ -5219,7 +3994,7 @@
addConfigurationProperty("cursor.orientationAware", "1");
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -5238,7 +4013,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -5249,7 +4024,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
@@ -5260,7 +4035,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
@@ -5271,7 +4046,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
@@ -5587,7 +4362,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, Process_PointerCapture) {
@@ -5615,7 +4390,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(100.0f, 200.0f));
// Button press.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
@@ -5654,7 +4429,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(100.0f, 200.0f));
// Disable pointer capture and check that the device generation got bumped
// and events are generated the usual way.
@@ -5674,7 +4449,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
/**
@@ -5735,7 +4510,7 @@
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
// Ensure the display is rotated.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
NotifyMotionArgs args;
@@ -5771,7 +4546,7 @@
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
@@ -5791,14 +4566,14 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
WithCoords(110.0f, 220.0f))));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
// Set up the secondary display as the display on which the pointer should be shown,
// and associate the InputDevice with the secondary display.
@@ -5818,14 +4593,14 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
WithCoords(110.0f, 220.0f))));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
// Set up the default display as the display on which the pointer should be shown.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
// Associate the InputDevice with the secondary display.
@@ -5992,9 +4767,9 @@
TOOL_TYPE = 1 << 10,
};
- void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+ void prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port = NO_PORT);
void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
- void prepareVirtualDisplay(int32_t orientation);
+ void prepareVirtualDisplay(ui::Rotation orientation);
void prepareVirtualKeys();
void prepareLocationCalibration();
int32_t toRawX(float displayX);
@@ -6048,17 +4823,17 @@
{ KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
};
-void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
+void TouchInputMapperTest::prepareDisplay(ui::Rotation orientation, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID,
port, ViewportType::INTERNAL);
}
void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
+ ui::ROTATION_0, SECONDARY_UNIQUE_ID, port, type);
}
-void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareVirtualDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT,
orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT,
ViewportType::VIRTUAL);
@@ -6225,7 +5000,7 @@
TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6253,7 +5028,7 @@
TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6281,7 +5056,7 @@
TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6296,7 +5071,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6346,7 +5121,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6467,7 +5242,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6542,7 +5317,7 @@
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6638,7 +5413,7 @@
TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
prepareVirtualKeys();
@@ -6737,7 +5512,7 @@
NotifyMotionArgs args;
// Rotation 90.
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -6763,7 +5538,7 @@
// Rotation 0.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
processDown(mapper, toRawX(50), toRawY(75));
processSync(mapper);
@@ -6777,7 +5552,7 @@
// Rotation 90.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -6791,7 +5566,7 @@
// Rotation 180.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -6805,7 +5580,7 @@
// Rotation 270.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
processSync(mapper);
@@ -6825,7 +5600,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_0");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6849,7 +5624,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_90");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6873,7 +5648,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_180");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6897,7 +5672,7 @@
addConfigurationProperty("touch.orientationAware", "1");
addConfigurationProperty("touch.orientation", "ORIENTATION_270");
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
NotifyMotionArgs args;
@@ -6928,7 +5703,7 @@
// Orientation 90, Rotation 0.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
processSync(mapper);
@@ -6942,7 +5717,7 @@
// Orientation 90, Rotation 90.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
processSync(mapper);
@@ -6956,7 +5731,7 @@
// Orientation 90, Rotation 180.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_180);
+ prepareDisplay(ui::ROTATION_180);
processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
processSync(mapper);
@@ -6970,7 +5745,7 @@
// Orientation 90, Rotation 270.
clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
+ prepareDisplay(ui::ROTATION_270);
processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
processSync(mapper);
@@ -6986,7 +5761,7 @@
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7030,7 +5805,7 @@
TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareLocationCalibration();
prepareButtons();
prepareAxes(POSITION);
@@ -7053,7 +5828,7 @@
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7296,7 +6071,7 @@
TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7431,7 +6206,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
@@ -7503,7 +6278,7 @@
TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7574,7 +6349,7 @@
TEST_F(SingleTouchInputMapperTest, Reset_CancelsOngoingGesture) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7596,7 +6371,7 @@
TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION | PRESSURE);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7624,7 +6399,7 @@
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7652,7 +6427,7 @@
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7712,7 +6487,7 @@
TEST_F(SingleTouchInputMapperTest, ButtonIsReleasedOnTouchUp) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -7748,6 +6523,17 @@
WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
}
+TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorrectType) {
+ mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
@@ -7755,27 +6541,25 @@
// The values inside DisplayViewport are expected to be pre-rotated. This updates the current
// DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
// rotated equivalent of the given un-rotated physical display bounds.
- void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+ void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay) {
uint32_t inverseRotationFlags;
auto width = DISPLAY_WIDTH;
auto height = DISPLAY_HEIGHT;
switch (orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
inverseRotationFlags = ui::Transform::ROT_270;
std::swap(width, height);
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
inverseRotationFlags = ui::Transform::ROT_180;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
inverseRotationFlags = ui::Transform::ROT_90;
std::swap(width, height);
break;
- case DISPLAY_ORIENTATION_0:
+ case ui::ROTATION_0:
inverseRotationFlags = ui::Transform::ROT_0;
break;
- default:
- FAIL() << "Invalid orientation: " << orientation;
}
const ui::Transform rotation(inverseRotationFlags, width, height);
@@ -7819,7 +6603,7 @@
TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
@@ -7834,8 +6618,7 @@
static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
{{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
- for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
+ for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
configurePhysicalDisplay(orientation, kPhysicalDisplay);
// Touches outside the physical display should be ignored, and should not generate any
@@ -7855,7 +6638,7 @@
TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
@@ -7868,8 +6651,7 @@
// points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
static const Rect kPhysicalDisplay{10, 20, 70, 160};
- for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
+ for (auto orientation : {ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, ui::ROTATION_270}) {
configurePhysicalDisplay(orientation, kPhysicalDisplay);
// Touches that start outside the physical display should be ignored until it enters the
@@ -7920,7 +6702,7 @@
public:
SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -8409,7 +7191,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8681,7 +7463,7 @@
TEST_F(MultiTouchInputMapperTest, AxisResolution_IsPopulated) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
/*fuzz*/ 0, /*resolution*/ 10);
@@ -8711,7 +7493,7 @@
TEST_F(MultiTouchInputMapperTest, TouchMajorAndMinorAxes_DoNotAppearIfNotSupported) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, /*flat*/ 0,
/*fuzz*/ 0, /*resolution*/ 10);
@@ -8732,7 +7514,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8903,7 +7685,7 @@
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
prepareVirtualKeys();
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9069,7 +7851,7 @@
TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9118,7 +7900,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL | MINOR);
addConfigurationProperty("touch.size.calibration", "geometric");
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9155,7 +7937,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
addConfigurationProperty("touch.size.calibration", "diameter");
addConfigurationProperty("touch.size.scale", "10");
@@ -9206,7 +7988,7 @@
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | TOUCH | TOOL);
addConfigurationProperty("touch.size.calibration", "area");
addConfigurationProperty("touch.size.scale", "43");
@@ -9239,7 +8021,7 @@
TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | PRESSURE);
addConfigurationProperty("touch.pressure.calibration", "amplitude");
addConfigurationProperty("touch.pressure.scale", "0.01");
@@ -9273,7 +8055,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9516,7 +8298,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleMappedStylusButtons) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9573,7 +8355,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9723,7 +8505,7 @@
TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9794,7 +8576,7 @@
TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9894,7 +8676,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
// Add viewport for display 1 on hdmi1
- prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareDisplay(ui::ROTATION_0, hdmi1);
// Send a touch event again
processPosition(mapper, 100, 100);
processSync(mapper);
@@ -9911,8 +8693,8 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
// Send a touch event
processPosition(mapper, 100, 100);
@@ -9935,7 +8717,7 @@
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9959,7 +8741,7 @@
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1);
process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100);
process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100);
@@ -9984,9 +8766,8 @@
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) {
addConfigurationProperty("touch.deviceType", "touchScreen");
// Don't set touch.enableForInactiveViewport to verify the default behavior.
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10005,9 +8786,8 @@
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "1");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, false /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ false /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10023,9 +8803,8 @@
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "0");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ true /*isActive*/, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
std::optional<DisplayViewport> optionalDisplayViewport =
mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
ASSERT_TRUE(optionalDisplayViewport.has_value());
@@ -10119,7 +8898,7 @@
mFakePolicy->setShowTouches(true);
// Create displays.
- prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ prepareDisplay(ui::ROTATION_0, hdmi1);
prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
// Default device will reconfigure above, need additional reconfiguration for another device.
@@ -10164,7 +8943,7 @@
TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
NotifyMotionArgs motionArgs;
@@ -10195,8 +8974,7 @@
NotifyMotionArgs motionArgs;
// Test all 4 orientations
- for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
- DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
clearViewports();
prepareDisplay(orientation);
@@ -10221,8 +8999,7 @@
NotifyMotionArgs motionArgs;
// Test all 4 orientations
- for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
- DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
+ for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
clearViewports();
prepareDisplay(orientation);
@@ -10256,7 +9033,7 @@
std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
NotifyMotionArgs motionArgs;
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
processPosition(mapper, 100, 200);
processSync(mapper);
@@ -10279,7 +9056,7 @@
std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
NotifyMotionArgs motionArgs;
- prepareDisplay(DISPLAY_ORIENTATION_90);
+ prepareDisplay(ui::ROTATION_90);
mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
processPosition(mapper, 100, 200);
processSync(mapper);
@@ -10289,7 +9066,7 @@
// compared to the display. This is so that when the window transform (which contains the
// display rotation) is applied later by InputDispatcher, the coordinates end up in the
// window's coordinate space.
- frame.rotate(getInverseRotation(DISPLAY_ORIENTATION_90));
+ frame.rotate(getInverseRotation(ui::ROTATION_90));
});
ASSERT_EQ(frames, motionArgs.videoFrames);
}
@@ -10326,7 +9103,7 @@
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10371,7 +9148,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_SinglePointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10419,7 +9196,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_TwoPointers) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10494,7 +9271,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_ShouldCancelWhenAllTouchIsPalm) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10592,7 +9369,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPointer) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10664,7 +9441,7 @@
*/
TEST_F(MultiTouchInputMapperTest, Process_MultiTouch_WithInvalidTrackingId) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10721,7 +9498,7 @@
TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10762,7 +9539,7 @@
TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -10788,11 +9565,12 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-TEST_F(MultiTouchInputMapperTest, ToolTypeSource) {
+TEST_F(MultiTouchInputMapperTest, StylusSourceIsAddedDynamicallyFromToolType) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
// Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the
// ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source.
@@ -10801,7 +9579,7 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
// However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be
- // reported with the stylus source, even through the device doesn't support the stylus source.
+ // reported with the stylus source.
processId(mapper, FIRST_TRACKING_ID);
processToolType(mapper, MT_TOOL_PEN);
processPosition(mapper, 100, 200);
@@ -10812,15 +9590,27 @@
WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // Now that we know the device supports styluses, ensure that the device is re-configured with
+ // the stylus source.
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, mapper.getSources());
+ {
+ const auto& devices = mReader->getInputDevices();
+ auto deviceInfo =
+ std::find_if(devices.begin(), devices.end(),
+ [](const InputDeviceInfo& info) { return info.getId() == DEVICE_ID; });
+ LOG_ALWAYS_FATAL_IF(deviceInfo == devices.end(), "Cannot find InputDevice");
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, deviceInfo->getSources());
+ }
+
+ // Ensure the device was not reset to prevent interruptions of any ongoing gestures.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+
processId(mapper, INVALID_TRACKING_ID);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
-
- // The mapper should still report only a touchscreen source.
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
}
// --- MultiTouchInputMapperTest_ExternalDevice ---
@@ -10836,7 +9626,7 @@
TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
@@ -10867,7 +9657,7 @@
fakePointerController->setButtonState(0);
// prepare device and capture
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -11017,7 +9807,7 @@
fakePointerController->setButtonState(0);
// prepare device and capture
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
@@ -11058,7 +9848,7 @@
std::shared_ptr<FakePointerController> fakePointerController =
std::make_shared<FakePointerController>();
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakePolicy->setPointerController(fakePointerController);
@@ -11085,7 +9875,7 @@
TEST_F(BluetoothMultiTouchInputMapperTest, TimestampSmoothening) {
addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -11135,7 +9925,7 @@
fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(0, 0);
fakePointerController->setButtonState(0);
- prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
@@ -11495,7 +10285,7 @@
process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
- void prepareVirtualDisplay(int32_t orientation) {
+ void prepareVirtualDisplay(ui::Rotation orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::VIRTUAL);
@@ -11513,7 +10303,7 @@
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
- prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+ prepareVirtualDisplay(ui::ROTATION_0);
// Send an axis event
processAxis(mapper, ABS_X, 100);
@@ -11616,15 +10406,17 @@
TEST_F(BatteryControllerTest, GetBatteryCapacity) {
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
- ASSERT_TRUE(controller.getBatteryCapacity(DEFAULT_BATTERY));
- ASSERT_EQ(controller.getBatteryCapacity(DEFAULT_BATTERY).value_or(-1), BATTERY_CAPACITY);
+ ASSERT_TRUE(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryCapacity(FakeEventHub::DEFAULT_BATTERY).value_or(-1),
+ FakeEventHub::BATTERY_CAPACITY);
}
TEST_F(BatteryControllerTest, GetBatteryStatus) {
PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
- ASSERT_TRUE(controller.getBatteryStatus(DEFAULT_BATTERY));
- ASSERT_EQ(controller.getBatteryStatus(DEFAULT_BATTERY).value_or(-1), BATTERY_STATUS);
+ ASSERT_TRUE(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY));
+ ASSERT_EQ(controller.getBatteryStatus(FakeEventHub::DEFAULT_BATTERY).value_or(-1),
+ FakeEventHub::BATTERY_STATUS);
}
// --- LightControllerTest ---
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
new file mode 100644
index 0000000..1f8cd12
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InstrumentedInputReader.h"
+
+namespace android {
+
+InstrumentedInputReader::InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener)
+ : InputReader(eventHub, policy, listener), mFakeContext(this) {}
+
+void InstrumentedInputReader::pushNextDevice(std::shared_ptr<InputDevice> device) {
+ mNextDevices.push(device);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId,
+ const std::string& name,
+ const std::string& location) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ int32_t generation = deviceId + 1;
+ return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
+ return device;
+ }
+ return InputReader::createDeviceLocked(eventHubId, identifier);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
new file mode 100644
index 0000000..7f8d556
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include <InputDevice.h>
+#include <InputReader.h>
+#include <gtest/gtest.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class InstrumentedInputReader : public InputReader {
+public:
+ InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener);
+ virtual ~InstrumentedInputReader() {}
+
+ void pushNextDevice(std::shared_ptr<InputDevice> device);
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location = "");
+
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
+protected:
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier);
+
+ class FakeInputReaderContext : public ContextImpl {
+ public:
+ FakeInputReaderContext(InputReader* reader)
+ : ContextImpl(reader),
+ mGlobalMetaState(0),
+ mUpdateGlobalMetaStateWasCalled(false),
+ mGeneration(1) {}
+
+ virtual ~FakeInputReaderContext() {}
+
+ void assertUpdateGlobalMetaStateWasCalled() {
+ ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+ << "Expected updateGlobalMetaState() to have been called.";
+ mUpdateGlobalMetaStateWasCalled = false;
+ }
+
+ void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+ uint32_t getGeneration() { return mGeneration; }
+
+ void updateGlobalMetaState() override {
+ mUpdateGlobalMetaStateWasCalled = true;
+ ContextImpl::updateGlobalMetaState();
+ }
+
+ int32_t getGlobalMetaState() override {
+ return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+ }
+
+ int32_t bumpGeneration() override {
+ mGeneration = ContextImpl::bumpGeneration();
+ return mGeneration;
+ }
+
+ void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
+
+ void assertTimeoutWasRequested(nsecs_t when) {
+ ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
+ << " but there was no timeout requested.";
+ ASSERT_EQ(when, *mRequestedTimeout);
+ mRequestedTimeout.reset();
+ }
+
+ void assertTimeoutWasNotRequested() {
+ ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
+ " but one was requested at time "
+ << *mRequestedTimeout;
+ }
+
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
+ outDevices = mExternalStylusDevices;
+ }
+
+ void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
+ mExternalStylusDevices = devices;
+ }
+
+ private:
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
+ std::optional<nsecs_t> mRequestedTimeout;
+ std::vector<InputDeviceInfo> mExternalStylusDevices;
+ } mFakeContext;
+
+ friend class InputReaderTest;
+
+public:
+ FakeInputReaderContext* getContext() { return &mFakeContext; }
+
+private:
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/NotifyArgs_test.cpp b/services/inputflinger/tests/NotifyArgs_test.cpp
new file mode 100644
index 0000000..6715585
--- /dev/null
+++ b/services/inputflinger/tests/NotifyArgs_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include <NotifyArgs.h>
+#include <utils/Timers.h>
+
+#include <gtest/gtest.h>
+#include "android/input.h"
+#include "input/Input.h"
+#include "input/TouchVideoFrame.h"
+
+namespace android {
+
+// --- NotifyArgsTest ---
+
+/**
+ * Validate basic copy assignment.
+ */
+TEST(NotifyMotionArgsTest, TestCopyAssignmentOperator) {
+ int32_t id = 123;
+ nsecs_t downTime = systemTime();
+ nsecs_t eventTime = downTime++;
+ nsecs_t readTime = downTime++;
+ int32_t deviceId = 7;
+ uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ int32_t displayId = 42;
+ uint32_t policyFlags = POLICY_FLAG_GESTURE;
+ int32_t action = AMOTION_EVENT_ACTION_HOVER_MOVE;
+ int32_t actionButton = AMOTION_EVENT_BUTTON_PRIMARY;
+ int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ int32_t metaState = AMETA_SCROLL_LOCK_ON;
+ uint32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY;
+ MotionClassification classification = MotionClassification::DEEP_PRESS;
+ int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+ uint32_t pointerCount = 2;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ float x = 0;
+ float y = 10;
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[i].clear();
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x++);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y++);
+ }
+
+ float xPrecision = 1.2f;
+ float yPrecision = 3.4f;
+ float xCursorPosition = 5.6f;
+ float yCursorPosition = 7.8f;
+
+ std::vector<int16_t> videoData = {1, 2, 3, 4};
+ timeval timestamp = {5, 6};
+ TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
+ std::vector<TouchVideoFrame> videoFrames = {frame};
+ const NotifyMotionArgs args(id, eventTime, readTime, deviceId, source, displayId, policyFlags,
+ action, actionButton, flags, metaState, buttonState, classification,
+ edgeFlags, pointerCount, pointerProperties, pointerCoords,
+ xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
+ videoFrames);
+
+ NotifyMotionArgs otherArgs{};
+ otherArgs = args;
+
+ EXPECT_EQ(args, otherArgs);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
new file mode 100644
index 0000000..27881f6
--- /dev/null
+++ b/services/inputflinger/tests/TestConstants.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
+// An arbitrary time value.
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
+
+// Error tolerance for floating point assertions.
+static const float EPSILON = 0.001f;
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 8721bd8..e5a4b14 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -46,7 +46,8 @@
}
MATCHER_P(WithSource, source, "InputEvent with specified source") {
- *result_listener << "expected source " << source << ", but got " << arg.source;
+ *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
+ << inputEventSourceToString(arg.source);
return arg.source == source;
}
@@ -73,6 +74,14 @@
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_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;
@@ -96,6 +105,12 @@
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;
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index cc523e1..be85026 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -16,11 +16,10 @@
#include <CursorInputMapper.h>
#include <FuzzContainer.h>
-#include <fuzzer/FuzzedDataProvider.h>
namespace android {
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
{[&]() -> void { fuzzer.addProperty("cursor.mode", "pointer"); },
@@ -35,7 +34,8 @@
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
CursorInputMapper& mapper = fuzzer.getMapper<CursorInputMapper>();
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index 1e0764f..76d2bcd 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -20,7 +20,6 @@
#include <InputMapper.h>
#include <InputReader.h>
#include <MapperHelpers.h>
-#include <fuzzer/FuzzedDataProvider.h>
namespace android {
@@ -31,10 +30,10 @@
std::unique_ptr<FuzzInputReaderContext> mFuzzContext;
std::unique_ptr<InputDevice> mFuzzDevice;
InputReaderConfiguration mPolicyConfig;
- std::shared_ptr<FuzzedDataProvider> mFdp;
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
public:
- FuzzContainer(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(fdp) {
+ FuzzContainer(std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) : mFdp(fdp) {
// Setup parameters.
std::string deviceName = mFdp->ConsumeRandomLengthString(16);
std::string deviceLocation = mFdp->ConsumeRandomLengthString(12);
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 2eed997..a80839c 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -153,6 +153,10 @@
return reader->getLightPlayerId(deviceId, lightId);
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
+ }
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
@@ -166,7 +170,8 @@
};
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzInputListener fuzzListener;
sp<FuzzInputReaderPolicy> fuzzPolicy = sp<FuzzInputReaderPolicy>::make(fdp);
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index e880f55..8e2d677 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -16,13 +16,12 @@
#include <FuzzContainer.h>
#include <KeyboardInputMapper.h>
-#include <fuzzer/FuzzedDataProvider.h>
namespace android {
const int32_t kMaxKeycodes = 100;
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
{[&]() -> void { fuzzer.addProperty("keyboard.orientationAware", "1"); },
@@ -41,7 +40,8 @@
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
KeyboardInputMapper& mapper =
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 64316ba..a0910ea 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#pragma once
#include <InputDevice.h>
#include <InputMapper.h>
#include <InputReader.h>
-#include <fuzzer/FuzzedDataProvider.h>
+#include <ThreadSafeFuzzedDataProvider.h>
#include "android/hardware/input/InputDeviceCountryCode.h"
using android::hardware::input::InputDeviceCountryCode;
@@ -114,10 +113,10 @@
InputDeviceIdentifier mIdentifier;
std::vector<TouchVideoFrame> mVideoFrames;
PropertyMap mFuzzConfig;
- std::shared_ptr<FuzzedDataProvider> mFdp;
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
public:
- FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {}
+ FuzzEventHub(std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {}
~FuzzEventHub() {}
void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
@@ -211,6 +210,7 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
@@ -263,10 +263,10 @@
};
class FuzzPointerController : public PointerControllerInterface {
- std::shared_ptr<FuzzedDataProvider> mFdp;
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
public:
- FuzzPointerController(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {}
+ FuzzPointerController(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
~FuzzPointerController() {}
bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
return mFdp->ConsumeBool();
@@ -289,13 +289,13 @@
class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
TouchAffineTransformation mTransform;
std::shared_ptr<FuzzPointerController> mPointerController;
- std::shared_ptr<FuzzedDataProvider> mFdp;
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
protected:
~FuzzInputReaderPolicy() {}
public:
- FuzzInputReaderPolicy(std::shared_ptr<FuzzedDataProvider> mFdp) : mFdp(mFdp) {
+ FuzzInputReaderPolicy(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {
mPointerController = std::make_shared<FuzzPointerController>(mFdp);
}
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
@@ -311,7 +311,7 @@
return mFdp->ConsumeRandomLengthString(32);
}
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) override {
+ ui::Rotation surfaceRotation) override {
return mTransform;
}
void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; }
@@ -333,13 +333,13 @@
class FuzzInputReaderContext : public InputReaderContext {
std::shared_ptr<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
- std::shared_ptr<FuzzedDataProvider> mFdp;
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
public:
FuzzInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
InputListenerInterface& listener,
- std::shared_ptr<FuzzedDataProvider> mFdp)
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp)
: mEventHub(eventHub), mPolicy(policy), mFdp(mFdp) {}
~FuzzInputReaderContext() {}
void updateGlobalMetaState() override {}
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index 99fd083..011455b 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -16,13 +16,12 @@
#include <FuzzContainer.h>
#include <MultiTouchInputMapper.h>
-#include <fuzzer/FuzzedDataProvider.h>
namespace android {
const int32_t kMaxKeycodes = 100;
-static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<FuzzedDataProvider> fdp) {
+static void addProperty(FuzzContainer& fuzzer, std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp) {
// Pick a random property to set for the mapper to have set.
fdp->PickValueInArray<std::function<void()>>(
{[&]() -> void { fuzzer.addProperty("touch.deviceType", "touchScreen"); },
@@ -58,7 +57,8 @@
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
MultiTouchInputMapper& mapper = fuzzer.getMapper<MultiTouchInputMapper>();
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index 7416ce9..c4938f2 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -16,12 +16,12 @@
#include <FuzzContainer.h>
#include <SwitchInputMapper.h>
-#include <fuzzer/FuzzedDataProvider.h>
namespace android {
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
- std::shared_ptr<FuzzedDataProvider> fdp = std::make_shared<FuzzedDataProvider>(data, size);
+ std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
+ std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
FuzzContainer fuzzer(fdp);
SwitchInputMapper& mapper = fuzzer.getMapper<SwitchInputMapper>();
diff --git a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
new file mode 100644
index 0000000..2f76f18
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+/**
+ * A thread-safe interface to the FuzzedDataProvider
+ */
+class ThreadSafeFuzzedDataProvider : FuzzedDataProvider {
+private:
+ std::mutex mLock;
+
+public:
+ ThreadSafeFuzzedDataProvider(const uint8_t* data, size_t size)
+ : FuzzedDataProvider(data, size) {}
+
+ template <typename T>
+ std::vector<T> ConsumeBytes(size_t num_bytes) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeBytes<T>(num_bytes);
+ }
+
+ template <typename T>
+ std::vector<T> ConsumeBytesWithTerminator(size_t num_bytes, T terminator) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeBytesWithTerminator<T>(num_bytes, terminator);
+ }
+
+ template <typename T>
+ std::vector<T> ConsumeRemainingBytes() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeRemainingBytes<T>();
+ }
+
+ std::string ConsumeBytesAsString(size_t num_bytes) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeBytesAsString(num_bytes);
+ }
+
+ std::string ConsumeRandomLengthString(size_t max_length) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeRandomLengthString(max_length);
+ }
+
+ std::string ConsumeRandomLengthString() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeRandomLengthString();
+ }
+
+ std::string ConsumeRemainingBytesAsString() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeRemainingBytesAsString();
+ }
+
+ template <typename T>
+ T ConsumeIntegral() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeIntegral<T>();
+ }
+
+ template <typename T>
+ T ConsumeIntegralInRange(T min, T max) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeIntegralInRange<T>(min, max);
+ }
+
+ template <typename T>
+ T ConsumeFloatingPoint() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeFloatingPoint<T>();
+ }
+
+ template <typename T>
+ T ConsumeFloatingPointInRange(T min, T max) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeFloatingPointInRange<T>(min, max);
+ }
+
+ template <typename T>
+ T ConsumeProbability() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeProbability<T>();
+ }
+
+ bool ConsumeBool() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeBool();
+ }
+
+ template <typename T>
+ T ConsumeEnum() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeEnum<T>();
+ }
+
+ template <typename T, size_t size>
+ T PickValueInArray(const T (&array)[size]) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::PickValueInArray(array);
+ }
+
+ template <typename T, size_t size>
+ T PickValueInArray(const std::array<T, size>& array) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::PickValueInArray(array);
+ }
+
+ template <typename T>
+ T PickValueInArray(std::initializer_list<const T> list) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::PickValueInArray(list);
+ }
+
+ size_t ConsumeData(void* destination, size_t num_bytes) {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::ConsumeData(destination, num_bytes);
+ }
+
+ size_t remaining_bytes() {
+ std::scoped_lock _l(mLock);
+ return FuzzedDataProvider::remaining_bytes();
+ }
+};
diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp
index f67c610..f5b360f 100644
--- a/services/sensorservice/AidlSensorHalWrapper.cpp
+++ b/services/sensorservice/AidlSensorHalWrapper.cpp
@@ -23,6 +23,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
+#include <aidl/sensors/convert.h>
using ::aidl::android::hardware::sensors::AdditionalInfo;
using ::aidl::android::hardware::sensors::DynamicSensorInfo;
@@ -34,469 +35,16 @@
using ::android::AidlMessageQueue;
using ::android::hardware::EventFlag;
using ::android::hardware::sensors::V2_1::implementation::MAX_RECEIVE_BUFFER_EVENT_COUNT;
+using ::android::hardware::sensors::implementation::convertToStatus;
+using ::android::hardware::sensors::implementation::convertToSensor;
+using ::android::hardware::sensors::implementation::convertToSensorEvent;
+using ::android::hardware::sensors::implementation::convertFromSensorEvent;
+
namespace android {
namespace {
-status_t convertToStatus(ndk::ScopedAStatus status) {
- if (status.isOk()) {
- return OK;
- } else {
- switch (status.getExceptionCode()) {
- case EX_ILLEGAL_ARGUMENT: {
- return BAD_VALUE;
- }
- case EX_SECURITY: {
- return PERMISSION_DENIED;
- }
- case EX_UNSUPPORTED_OPERATION: {
- return INVALID_OPERATION;
- }
- case EX_SERVICE_SPECIFIC: {
- switch (status.getServiceSpecificError()) {
- case ISensors::ERROR_BAD_VALUE: {
- return BAD_VALUE;
- }
- case ISensors::ERROR_NO_MEMORY: {
- return NO_MEMORY;
- }
- default: {
- return UNKNOWN_ERROR;
- }
- }
- }
- default: {
- return UNKNOWN_ERROR;
- }
- }
- }
-}
-
-void convertToSensor(const SensorInfo &src, sensor_t *dst) {
- dst->name = strdup(src.name.c_str());
- dst->vendor = strdup(src.vendor.c_str());
- dst->version = src.version;
- dst->handle = src.sensorHandle;
- dst->type = (int)src.type;
- dst->maxRange = src.maxRange;
- dst->resolution = src.resolution;
- dst->power = src.power;
- dst->minDelay = src.minDelayUs;
- dst->fifoReservedEventCount = src.fifoReservedEventCount;
- dst->fifoMaxEventCount = src.fifoMaxEventCount;
- dst->stringType = strdup(src.typeAsString.c_str());
- dst->requiredPermission = strdup(src.requiredPermission.c_str());
- dst->maxDelay = src.maxDelayUs;
- dst->flags = src.flags;
- dst->reserved[0] = dst->reserved[1] = 0;
-}
-
-void convertToSensorEvent(const Event &src, sensors_event_t *dst) {
- *dst = {.version = sizeof(sensors_event_t),
- .sensor = src.sensorHandle,
- .type = (int32_t)src.sensorType,
- .reserved0 = 0,
- .timestamp = src.timestamp};
-
- switch (src.sensorType) {
- case SensorType::META_DATA: {
- // Legacy HALs expect the handle reference in the meta data field.
- // Copy it over from the handle of the event.
- dst->meta_data.what = (int32_t)src.payload.get<Event::EventPayload::meta>().what;
- dst->meta_data.sensor = src.sensorHandle;
- // Set the sensor handle to 0 to maintain compatibility.
- dst->sensor = 0;
- break;
- }
-
- case SensorType::ACCELEROMETER:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::GYROSCOPE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION: {
- dst->acceleration.x = src.payload.get<Event::EventPayload::vec3>().x;
- dst->acceleration.y = src.payload.get<Event::EventPayload::vec3>().y;
- dst->acceleration.z = src.payload.get<Event::EventPayload::vec3>().z;
- dst->acceleration.status = (int32_t)src.payload.get<Event::EventPayload::vec3>().status;
- break;
- }
-
- case SensorType::GAME_ROTATION_VECTOR: {
- dst->data[0] = src.payload.get<Event::EventPayload::vec4>().x;
- dst->data[1] = src.payload.get<Event::EventPayload::vec4>().y;
- dst->data[2] = src.payload.get<Event::EventPayload::vec4>().z;
- dst->data[3] = src.payload.get<Event::EventPayload::vec4>().w;
- break;
- }
-
- case SensorType::ROTATION_VECTOR:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
- dst->data[0] = src.payload.get<Event::EventPayload::data>().values[0];
- dst->data[1] = src.payload.get<Event::EventPayload::data>().values[1];
- dst->data[2] = src.payload.get<Event::EventPayload::data>().values[2];
- dst->data[3] = src.payload.get<Event::EventPayload::data>().values[3];
- dst->data[4] = src.payload.get<Event::EventPayload::data>().values[4];
- break;
- }
-
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::ACCELEROMETER_UNCALIBRATED: {
- dst->uncalibrated_gyro.x_uncalib = src.payload.get<Event::EventPayload::uncal>().x;
- dst->uncalibrated_gyro.y_uncalib = src.payload.get<Event::EventPayload::uncal>().y;
- dst->uncalibrated_gyro.z_uncalib = src.payload.get<Event::EventPayload::uncal>().z;
- dst->uncalibrated_gyro.x_bias = src.payload.get<Event::EventPayload::uncal>().xBias;
- dst->uncalibrated_gyro.y_bias = src.payload.get<Event::EventPayload::uncal>().yBias;
- dst->uncalibrated_gyro.z_bias = src.payload.get<Event::EventPayload::uncal>().zBias;
- break;
- }
-
- case SensorType::HINGE_ANGLE:
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::LIGHT:
- case SensorType::PRESSURE:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::STATIONARY_DETECT:
- case SensorType::MOTION_DETECT:
- case SensorType::HEART_BEAT:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT: {
- dst->data[0] = src.payload.get<Event::EventPayload::scalar>();
- break;
- }
-
- case SensorType::STEP_COUNTER: {
- dst->u64.step_counter = src.payload.get<Event::EventPayload::stepCount>();
- break;
- }
-
- case SensorType::HEART_RATE: {
- dst->heart_rate.bpm = src.payload.get<Event::EventPayload::heartRate>().bpm;
- dst->heart_rate.status =
- (int8_t)src.payload.get<Event::EventPayload::heartRate>().status;
- break;
- }
-
- case SensorType::POSE_6DOF: { // 15 floats
- for (size_t i = 0; i < 15; ++i) {
- dst->data[i] = src.payload.get<Event::EventPayload::pose6DOF>().values[i];
- }
- break;
- }
-
- case SensorType::DYNAMIC_SENSOR_META: {
- dst->dynamic_sensor_meta.connected =
- src.payload.get<Event::EventPayload::dynamic>().connected;
- dst->dynamic_sensor_meta.handle =
- src.payload.get<Event::EventPayload::dynamic>().sensorHandle;
- dst->dynamic_sensor_meta.sensor = NULL; // to be filled in later
-
- memcpy(dst->dynamic_sensor_meta.uuid,
- src.payload.get<Event::EventPayload::dynamic>().uuid.values.data(), 16);
-
- break;
- }
-
- case SensorType::ADDITIONAL_INFO: {
- const AdditionalInfo &srcInfo = src.payload.get<Event::EventPayload::additional>();
-
- additional_info_event_t *dstInfo = &dst->additional_info;
- dstInfo->type = (int32_t)srcInfo.type;
- dstInfo->serial = srcInfo.serial;
-
- switch (srcInfo.payload.getTag()) {
- case AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32: {
- const auto &values =
- srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataInt32>()
- .values;
- CHECK_EQ(values.size() * sizeof(int32_t), sizeof(dstInfo->data_int32));
- memcpy(dstInfo->data_int32, values.data(), sizeof(dstInfo->data_int32));
- break;
- }
- case AdditionalInfo::AdditionalInfoPayload::Tag::dataFloat: {
- const auto &values =
- srcInfo.payload.get<AdditionalInfo::AdditionalInfoPayload::dataFloat>()
- .values;
- CHECK_EQ(values.size() * sizeof(float), sizeof(dstInfo->data_float));
- memcpy(dstInfo->data_float, values.data(), sizeof(dstInfo->data_float));
- break;
- }
- default: {
- ALOGE("Invalid sensor additional info tag: %d", (int)srcInfo.payload.getTag());
- }
- }
- break;
- }
-
- case SensorType::HEAD_TRACKER: {
- const auto &ht = src.payload.get<Event::EventPayload::headTracker>();
- dst->head_tracker.rx = ht.rx;
- dst->head_tracker.ry = ht.ry;
- dst->head_tracker.rz = ht.rz;
- dst->head_tracker.vx = ht.vx;
- dst->head_tracker.vy = ht.vy;
- dst->head_tracker.vz = ht.vz;
- dst->head_tracker.discontinuity_count = ht.discontinuityCount;
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES:
- case SensorType::GYROSCOPE_LIMITED_AXES:
- dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x;
- dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y;
- dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z;
- dst->limited_axes_imu.x_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported;
- dst->limited_axes_imu.y_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported;
- dst->limited_axes_imu.z_supported =
- src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported;
- break;
-
- case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
- case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED:
- dst->limited_axes_imu_uncalibrated.x_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x;
- dst->limited_axes_imu_uncalibrated.y_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y;
- dst->limited_axes_imu_uncalibrated.z_uncalib =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z;
- dst->limited_axes_imu_uncalibrated.x_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias;
- dst->limited_axes_imu_uncalibrated.y_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias;
- dst->limited_axes_imu_uncalibrated.z_bias =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias;
- dst->limited_axes_imu_uncalibrated.x_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported;
- dst->limited_axes_imu_uncalibrated.y_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported;
- dst->limited_axes_imu_uncalibrated.z_supported =
- src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported;
- break;
-
- case SensorType::HEADING:
- dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading;
- dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy;
- break;
-
- default: {
- CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
- memcpy(dst->data, src.payload.get<Event::EventPayload::data>().values.data(),
- 16 * sizeof(float));
- break;
- }
- }
-}
-
-void convertFromSensorEvent(const sensors_event_t &src, Event *dst) {
- *dst = {
- .timestamp = src.timestamp,
- .sensorHandle = src.sensor,
- .sensorType = (SensorType)src.type,
- };
-
- switch (dst->sensorType) {
- case SensorType::META_DATA: {
- Event::EventPayload::MetaData meta;
- meta.what = (Event::EventPayload::MetaData::MetaDataEventType)src.meta_data.what;
- // Legacy HALs contain the handle reference in the meta data field.
- // Copy that over to the handle of the event. In legacy HALs this
- // field was expected to be 0.
- dst->sensorHandle = src.meta_data.sensor;
- dst->payload.set<Event::EventPayload::Tag::meta>(meta);
- break;
- }
-
- case SensorType::ACCELEROMETER:
- case SensorType::MAGNETIC_FIELD:
- case SensorType::ORIENTATION:
- case SensorType::GYROSCOPE:
- case SensorType::GRAVITY:
- case SensorType::LINEAR_ACCELERATION: {
- Event::EventPayload::Vec3 vec3;
- vec3.x = src.acceleration.x;
- vec3.y = src.acceleration.y;
- vec3.z = src.acceleration.z;
- vec3.status = (SensorStatus)src.acceleration.status;
- dst->payload.set<Event::EventPayload::Tag::vec3>(vec3);
- break;
- }
-
- case SensorType::GAME_ROTATION_VECTOR: {
- Event::EventPayload::Vec4 vec4;
- vec4.x = src.data[0];
- vec4.y = src.data[1];
- vec4.z = src.data[2];
- vec4.w = src.data[3];
- dst->payload.set<Event::EventPayload::Tag::vec4>(vec4);
- break;
- }
-
- case SensorType::ROTATION_VECTOR:
- case SensorType::GEOMAGNETIC_ROTATION_VECTOR: {
- Event::EventPayload::Data data;
- memcpy(data.values.data(), src.data, 5 * sizeof(float));
- dst->payload.set<Event::EventPayload::Tag::data>(data);
- break;
- }
-
- case SensorType::MAGNETIC_FIELD_UNCALIBRATED:
- case SensorType::GYROSCOPE_UNCALIBRATED:
- case SensorType::ACCELEROMETER_UNCALIBRATED: {
- Event::EventPayload::Uncal uncal;
- uncal.x = src.uncalibrated_gyro.x_uncalib;
- uncal.y = src.uncalibrated_gyro.y_uncalib;
- uncal.z = src.uncalibrated_gyro.z_uncalib;
- uncal.xBias = src.uncalibrated_gyro.x_bias;
- uncal.yBias = src.uncalibrated_gyro.y_bias;
- uncal.zBias = src.uncalibrated_gyro.z_bias;
- dst->payload.set<Event::EventPayload::Tag::uncal>(uncal);
- break;
- }
-
- case SensorType::DEVICE_ORIENTATION:
- case SensorType::LIGHT:
- case SensorType::PRESSURE:
- case SensorType::PROXIMITY:
- case SensorType::RELATIVE_HUMIDITY:
- case SensorType::AMBIENT_TEMPERATURE:
- case SensorType::SIGNIFICANT_MOTION:
- case SensorType::STEP_DETECTOR:
- case SensorType::TILT_DETECTOR:
- case SensorType::WAKE_GESTURE:
- case SensorType::GLANCE_GESTURE:
- case SensorType::PICK_UP_GESTURE:
- case SensorType::WRIST_TILT_GESTURE:
- case SensorType::STATIONARY_DETECT:
- case SensorType::MOTION_DETECT:
- case SensorType::HEART_BEAT:
- case SensorType::LOW_LATENCY_OFFBODY_DETECT:
- case SensorType::HINGE_ANGLE: {
- dst->payload.set<Event::EventPayload::Tag::scalar>((float)src.data[0]);
- break;
- }
-
- case SensorType::STEP_COUNTER: {
- dst->payload.set<Event::EventPayload::Tag::stepCount>(src.u64.step_counter);
- break;
- }
-
- case SensorType::HEART_RATE: {
- Event::EventPayload::HeartRate heartRate;
- heartRate.bpm = src.heart_rate.bpm;
- heartRate.status = (SensorStatus)src.heart_rate.status;
- dst->payload.set<Event::EventPayload::Tag::heartRate>(heartRate);
- break;
- }
-
- case SensorType::POSE_6DOF: { // 15 floats
- Event::EventPayload::Pose6Dof pose6DOF;
- for (size_t i = 0; i < 15; ++i) {
- pose6DOF.values[i] = src.data[i];
- }
- dst->payload.set<Event::EventPayload::Tag::pose6DOF>(pose6DOF);
- break;
- }
-
- case SensorType::DYNAMIC_SENSOR_META: {
- DynamicSensorInfo dynamic;
- dynamic.connected = src.dynamic_sensor_meta.connected;
- dynamic.sensorHandle = src.dynamic_sensor_meta.handle;
-
- memcpy(dynamic.uuid.values.data(), src.dynamic_sensor_meta.uuid, 16);
- dst->payload.set<Event::EventPayload::Tag::dynamic>(dynamic);
- break;
- }
-
- case SensorType::ADDITIONAL_INFO: {
- AdditionalInfo info;
- const additional_info_event_t &srcInfo = src.additional_info;
- info.type = (AdditionalInfo::AdditionalInfoType)srcInfo.type;
- info.serial = srcInfo.serial;
-
- AdditionalInfo::AdditionalInfoPayload::Int32Values data;
- CHECK_EQ(data.values.size() * sizeof(int32_t), sizeof(srcInfo.data_int32));
- memcpy(data.values.data(), srcInfo.data_int32, sizeof(srcInfo.data_int32));
- info.payload.set<AdditionalInfo::AdditionalInfoPayload::Tag::dataInt32>(data);
-
- dst->payload.set<Event::EventPayload::Tag::additional>(info);
- break;
- }
-
- case SensorType::HEAD_TRACKER: {
- Event::EventPayload::HeadTracker headTracker;
- headTracker.rx = src.head_tracker.rx;
- headTracker.ry = src.head_tracker.ry;
- headTracker.rz = src.head_tracker.rz;
- headTracker.vx = src.head_tracker.vx;
- headTracker.vy = src.head_tracker.vy;
- headTracker.vz = src.head_tracker.vz;
- headTracker.discontinuityCount = src.head_tracker.discontinuity_count;
-
- dst->payload.set<Event::EventPayload::Tag::headTracker>(headTracker);
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES:
- case SensorType::GYROSCOPE_LIMITED_AXES: {
- Event::EventPayload::LimitedAxesImu limitedAxesImu;
- limitedAxesImu.x = src.limited_axes_imu.x;
- limitedAxesImu.y = src.limited_axes_imu.y;
- limitedAxesImu.z = src.limited_axes_imu.z;
- limitedAxesImu.xSupported = src.limited_axes_imu.x_supported;
- limitedAxesImu.ySupported = src.limited_axes_imu.y_supported;
- limitedAxesImu.zSupported = src.limited_axes_imu.z_supported;
- dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu);
- break;
- }
-
- case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
- case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: {
- Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal;
- limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib;
- limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib;
- limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib;
- limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias;
- limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias;
- limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias;
- limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported;
- limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported;
- limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported;
- dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal);
- break;
- }
-
- case SensorType::HEADING: {
- Event::EventPayload::Heading heading;
- heading.heading = src.heading.heading;
- heading.accuracy = src.heading.accuracy;
- dst->payload.set<Event::EventPayload::heading>(heading);
- break;
- }
-
- default: {
- CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE);
-
- Event::EventPayload::Data data;
- memcpy(data.values.data(), src.data, 16 * sizeof(float));
- dst->payload.set<Event::EventPayload::Tag::data>(data);
- break;
- }
- }
-}
-
void serviceDied(void *cookie) {
ALOGW("Sensors HAL died, attempting to reconnect.");
((AidlSensorHalWrapper *)cookie)->prepareForReconnect();
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 5ad4815..11c56a8 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -75,7 +75,8 @@
static_libs: [
"libaidlcommonsupport",
"android.hardware.sensors@1.0-convert",
- "android.hardware.sensors-V1-ndk",
+ "android.hardware.sensors-V1-convert",
+ "android.hardware.sensors-V2-ndk",
],
generated_headers: ["framework-cppstream-protos"],
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 291c770..4ac9651 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -151,7 +151,7 @@
return PERMISSION_DENIED;
}
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
return NAME_NOT_FOUND;
}
@@ -228,7 +228,7 @@
for (auto &i : existingConnections) {
int handle = i.first;
int rateLevel = i.second;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType()) &&
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f06f947..82d0295 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -160,7 +160,7 @@
bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
Mutex::Autolock _l(mConnectionLock);
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
!mService->canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ",
mOpPackageName) ||
@@ -202,7 +202,7 @@
Mutex::Autolock _l(mConnectionLock);
for (auto &it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
return true;
}
@@ -245,7 +245,7 @@
if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
for (auto& it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().isWakeUpSensor()) {
looper_flags |= ALOOPER_EVENT_INPUT;
}
@@ -555,7 +555,7 @@
// flush complete events to be sent.
for (auto& it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
continue;
}
@@ -689,7 +689,7 @@
if (enabled) {
nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
bool isSensorCapped = false;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType())) {
@@ -729,7 +729,7 @@
nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
bool isSensorCapped = false;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType())) {
@@ -852,7 +852,7 @@
}
sensors_event_t sensor_event;
memcpy(&sensor_event, buf, sizeof(sensors_event_t));
- sp<SensorInterface> si =
+ std::shared_ptr<SensorInterface> si =
mService->getSensorInterfaceFromHandle(sensor_event.sensor);
if (si == nullptr) {
return 1;
@@ -903,7 +903,7 @@
size_t fifoWakeUpSensors = 0;
size_t fifoNonWakeUpSensors = 0;
for (auto& it : mSensorInfo) {
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
if (si == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 46f00e8..398cdf9 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -87,6 +87,42 @@
// ---------------------------------------------------------------------------
+RuntimeSensor::RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback)
+ : BaseSensor(sensor), mCallback(std::move(callback)) {
+}
+
+status_t RuntimeSensor::activate(void*, bool enabled) {
+ if (enabled != mEnabled) {
+ mEnabled = enabled;
+ mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+ }
+ return OK;
+}
+
+status_t RuntimeSensor::batch(void*, int, int, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ if (mSamplingPeriodNs != samplingPeriodNs || mBatchReportLatencyNs != maxBatchReportLatencyNs) {
+ mSamplingPeriodNs = samplingPeriodNs;
+ mBatchReportLatencyNs = maxBatchReportLatencyNs;
+ if (mEnabled) {
+ mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+ }
+ }
+ return OK;
+}
+
+status_t RuntimeSensor::setDelay(void*, int, int64_t ns) {
+ if (mSamplingPeriodNs != ns) {
+ mSamplingPeriodNs = ns;
+ if (mEnabled) {
+ mCallback->onStateChanged(mEnabled, mSamplingPeriodNs, mBatchReportLatencyNs);
+ }
+ }
+ return OK;
+}
+
+// ---------------------------------------------------------------------------
+
ProximitySensor::ProximitySensor(const sensor_t& sensor, SensorService& service)
: HardwareSensor(sensor), mSensorService(service) {
}
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index 5704359..5ee5e12 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -104,6 +104,32 @@
// ---------------------------------------------------------------------------
+class RuntimeSensor : public BaseSensor {
+public:
+ static constexpr int DEFAULT_DEVICE_ID = 0;
+
+ class StateChangeCallback : public virtual RefBase {
+ public:
+ virtual void onStateChanged(bool enabled, int64_t samplingPeriodNs,
+ int64_t batchReportLatencyNs) = 0;
+ };
+ RuntimeSensor(const sensor_t& sensor, sp<StateChangeCallback> callback);
+ virtual status_t activate(void* ident, bool enabled) override;
+ virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) override;
+ virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
+ virtual bool process(sensors_event_t*, const sensors_event_t&) { return false; }
+ virtual bool isVirtual() const override { return false; }
+
+private:
+ bool mEnabled = false;
+ int64_t mSamplingPeriodNs = 0;
+ int64_t mBatchReportLatencyNs = 0;
+ sp<StateChangeCallback> mCallback;
+};
+
+// ---------------------------------------------------------------------------
+
class ProximitySensor : public HardwareSensor {
public:
explicit ProximitySensor(const sensor_t& sensor, SensorService& service);
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 85ce0f0..daff4d0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -28,13 +28,13 @@
const Sensor SensorList::mNonSensor = Sensor("unknown");
-bool SensorList::add(
- int handle, SensorInterface* si, bool isForDebug, bool isVirtual) {
+bool SensorList::add(int handle, std::shared_ptr<SensorInterface> si, bool isForDebug,
+ bool isVirtual, int deviceId) {
std::lock_guard<std::mutex> lk(mLock);
if (handle == si->getSensor().getHandle() &&
mUsedHandle.insert(handle).second) {
// will succeed as the mUsedHandle does not have this handle
- mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual));
+ mHandleMap.emplace(handle, Entry(std::move(si), isForDebug, isVirtual, deviceId));
return true;
}
// handle exist already or handle mismatch
@@ -63,12 +63,12 @@
mNonSensor.getStringType());
}
-sp<SensorInterface> SensorList::getInterface(int handle) const {
- return getOne<sp<SensorInterface>>(
- handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
+std::shared_ptr<SensorInterface> SensorList::getInterface(int handle) const {
+ return getOne<std::shared_ptr<SensorInterface>>(
+ handle, [] (const Entry& e) -> std::shared_ptr<SensorInterface> {return e.si;},
+ nullptr);
}
-
bool SensorList::isNewHandle(int handle) const {
std::lock_guard<std::mutex> lk(mLock);
return mUsedHandle.find(handle) == mUsedHandle.end();
@@ -79,7 +79,8 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) {
+ if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()
+ && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
sensors.add(e.si->getSensor());
}
return true;
@@ -92,7 +93,8 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (!e.si->getSensor().isDynamicSensor()) {
+ if (!e.si->getSensor().isDynamicSensor()
+ && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
sensors.add(e.si->getSensor());
}
return true;
@@ -105,7 +107,8 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) {
+ if (!e.isForDebug && e.si->getSensor().isDynamicSensor()
+ && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
sensors.add(e.si->getSensor());
}
return true;
@@ -118,7 +121,20 @@
Vector<Sensor> sensors;
forEachEntry(
[&sensors] (const Entry& e) -> bool {
- if (e.isVirtual) {
+ if (e.isVirtual && e.deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) {
+ sensors.add(e.si->getSensor());
+ }
+ return true;
+ });
+ return sensors;
+}
+
+const Vector<Sensor> SensorList::getRuntimeSensors(int deviceId) const {
+ // lock in forEachEntry
+ Vector<Sensor> sensors;
+ forEachEntry(
+ [&sensors, deviceId] (const Entry& e) -> bool {
+ if (!e.isForDebug && e.deviceId == deviceId) {
sensors.add(e.si->getSensor());
}
return true;
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 049ae7c..ad5b21f 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -37,20 +37,18 @@
class SensorList : public Dumpable {
public:
struct Entry {
- sp<SensorInterface> si;
+ std::shared_ptr<SensorInterface> si;
const bool isForDebug;
const bool isVirtual;
- Entry(SensorInterface* si_, bool debug_, bool virtual_) :
- si(si_), isForDebug(debug_), isVirtual(virtual_) {
+ const int deviceId;
+ Entry(std::shared_ptr<SensorInterface> si_, bool debug_, bool virtual_, int deviceId_) :
+ si(std::move(si_)), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
}
};
- // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
- // object it pointed to and the object should not be released elsewhere.
- bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false);
-
- // After a handle is removed, the object that SensorInterface * pointing to may get deleted if
- // no more sp<> of the same object exist.
+ // SensorList owns the SensorInterface pointer.
+ bool add(int handle, std::shared_ptr<SensorInterface> si, bool isForDebug = false,
+ bool isVirtual = false, int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
bool remove(int handle);
inline bool hasAnySensor() const { return mHandleMap.size() > 0;}
@@ -60,11 +58,12 @@
const Vector<Sensor> getUserDebugSensors() const;
const Vector<Sensor> getDynamicSensors() const;
const Vector<Sensor> getVirtualSensors() const;
+ const Vector<Sensor> getRuntimeSensors(int deviceId) const;
String8 getName(int handle) const;
String8 getStringType(int handle) const;
- sp<SensorInterface> getInterface(int handle) const;
+ std::shared_ptr<SensorInterface> getInterface(int handle) const;
bool isNewHandle(int handle) const;
// Iterate through Sensor in sensor list and perform operation f on each Sensor object.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 21d6b6b..6504b79 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <aidl/android/hardware/sensors/ISensors.h>
#include <android-base/strings.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/util/ProtoOutputStream.h>
@@ -102,6 +103,31 @@
static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE");
static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS");
+namespace {
+
+int32_t nextRuntimeSensorHandle() {
+ using ::aidl::android::hardware::sensors::ISensors;
+ static int32_t nextHandle = ISensors::RUNTIME_SENSORS_HANDLE_BASE;
+ if (nextHandle == ISensors::RUNTIME_SENSORS_HANDLE_END) {
+ return -1;
+ }
+ return nextHandle++;
+}
+
+class RuntimeSensorCallbackProxy : public RuntimeSensor::StateChangeCallback {
+ public:
+ RuntimeSensorCallbackProxy(sp<SensorService::RuntimeSensorStateChangeCallback> callback)
+ : mCallback(std::move(callback)) {}
+ void onStateChanged(bool enabled, int64_t samplingPeriodNs,
+ int64_t batchReportLatencyNs) override {
+ mCallback->onStateChanged(enabled, samplingPeriodNs, batchReportLatencyNs);
+ }
+ private:
+ sp<SensorService::RuntimeSensorStateChangeCallback> mCallback;
+};
+
+} // namespace
+
static bool isAutomotive() {
sp<IServiceManager> serviceManager = defaultServiceManager();
if (serviceManager.get() == nullptr) {
@@ -137,6 +163,59 @@
mMicSensorPrivacyPolicy = new MicrophonePrivacyPolicy(this);
}
+int SensorService::registerRuntimeSensor(
+ const sensor_t& sensor, int deviceId, sp<RuntimeSensorStateChangeCallback> callback) {
+ int handle = 0;
+ while (handle == 0 || !mSensors.isNewHandle(handle)) {
+ handle = nextRuntimeSensorHandle();
+ if (handle < 0) {
+ // Ran out of the dedicated range for runtime sensors.
+ return handle;
+ }
+ }
+
+ ALOGI("Registering runtime sensor handle 0x%x, type %d, name %s",
+ handle, sensor.type, sensor.name);
+
+ sp<RuntimeSensor::StateChangeCallback> runtimeSensorCallback(
+ new RuntimeSensorCallbackProxy(std::move(callback)));
+ sensor_t runtimeSensor = sensor;
+ // force the handle to be consistent
+ runtimeSensor.handle = handle;
+ auto si = std::make_shared<RuntimeSensor>(runtimeSensor, std::move(runtimeSensorCallback));
+
+ Mutex::Autolock _l(mLock);
+ if (!registerSensor(std::move(si), /* isDebug= */ false, /* isVirtual= */ false, deviceId)) {
+ // The registration was unsuccessful.
+ return mSensors.getNonSensor().getHandle();
+ }
+
+ return handle;
+}
+
+status_t SensorService::unregisterRuntimeSensor(int handle) {
+ ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle);
+ {
+ Mutex::Autolock _l(mLock);
+ if (!unregisterDynamicSensorLocked(handle)) {
+ ALOGE("Runtime sensor release error.");
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
+ for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) {
+ connection->removeSensor(handle);
+ }
+ return OK;
+}
+
+status_t SensorService::sendRuntimeSensorEvent(const sensors_event_t& event) {
+ Mutex::Autolock _l(mLock);
+ mRuntimeSensorEventQueue.push(event);
+ return OK;
+}
+
bool SensorService::initializeHmacKey() {
int fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_RDONLY|O_CLOEXEC);
if (fd != -1) {
@@ -238,11 +317,13 @@
}
if (useThisSensor) {
if (list[i].type == SENSOR_TYPE_PROXIMITY) {
- SensorInterface* s = new ProximitySensor(list[i], *this);
- registerSensor(s);
- mProxSensorHandles.push_back(s->getSensor().getHandle());
+ auto s = std::make_shared<ProximitySensor>(list[i], *this);
+ const int handle = s->getSensor().getHandle();
+ if (registerSensor(std::move(s))) {
+ mProxSensorHandles.push_back(handle);
+ }
} else {
- registerSensor(new HardwareSensor(list[i]));
+ registerSensor(std::make_shared<HardwareSensor>(list[i]));
}
}
}
@@ -257,56 +338,63 @@
// available in the HAL
bool needRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) != 0;
-
- registerSensor(new RotationVectorSensor(), !needRotationVector, true);
- registerSensor(new OrientationSensor(), !needRotationVector, true);
+ registerVirtualSensor(std::make_shared<RotationVectorSensor>(),
+ /* isDebug= */ !needRotationVector);
+ registerVirtualSensor(std::make_shared<OrientationSensor>(),
+ /* isDebug= */ !needRotationVector);
// virtual debugging sensors are not for user
- registerSensor( new CorrectedGyroSensor(list, count), true, true);
- registerSensor( new GyroDriftSensor(), true, true);
+ registerVirtualSensor(std::make_shared<CorrectedGyroSensor>(list, count),
+ /* isDebug= */ true);
+ registerVirtualSensor(std::make_shared<GyroDriftSensor>(), /* isDebug= */ true);
}
if (hasAccel && (hasGyro || hasGyroUncalibrated)) {
bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
- registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
+ registerVirtualSensor(std::make_shared<GravitySensor>(list, count),
+ /* isDebug= */ !needGravitySensor);
bool needLinearAcceleration =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
- registerSensor(new LinearAccelerationSensor(list, count),
- !needLinearAcceleration, true);
+ registerVirtualSensor(std::make_shared<LinearAccelerationSensor>(list, count),
+ /* isDebug= */ !needLinearAcceleration);
bool needGameRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
- registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
+ registerVirtualSensor(std::make_shared<GameRotationVectorSensor>(),
+ /* isDebug= */ !needGameRotationVector);
}
if (hasAccel && hasMag) {
bool needGeoMagRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR)) != 0;
- registerSensor(new GeoMagRotationVectorSensor(), !needGeoMagRotationVector, true);
+ registerVirtualSensor(std::make_shared<GeoMagRotationVectorSensor>(),
+ /* isDebug= */ !needGeoMagRotationVector);
}
if (isAutomotive()) {
if (hasAccel) {
- registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_ACCELEROMETER),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_ACCELEROMETER));
}
if (hasGyro) {
- registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_GYROSCOPE),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_GYROSCOPE));
}
if (hasAccelUncalibrated) {
- registerSensor(new LimitedAxesImuSensor(list, count,
- SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED));
}
if (hasGyroUncalibrated) {
- registerSensor(new LimitedAxesImuSensor(list, count,
- SENSOR_TYPE_GYROSCOPE_UNCALIBRATED),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_GYROSCOPE_UNCALIBRATED));
}
}
@@ -407,19 +495,21 @@
&& isUidActive(uid) && !isOperationRestrictedLocked(opPackageName);
}
-const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) {
- int handle = s->getSensor().getHandle();
- int type = s->getSensor().getType();
- if (mSensors.add(handle, s, isDebug, isVirtual)){
+bool SensorService::registerSensor(std::shared_ptr<SensorInterface> s, bool isDebug, bool isVirtual,
+ int deviceId) {
+ const int handle = s->getSensor().getHandle();
+ const int type = s->getSensor().getType();
+ if (mSensors.add(handle, std::move(s), isDebug, isVirtual, deviceId)) {
mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
- return s->getSensor();
+ return true;
} else {
- return mSensors.getNonSensor();
+ LOG_FATAL("Failed to register sensor with handle %d", handle);
+ return false;
}
}
-const Sensor& SensorService::registerDynamicSensorLocked(SensorInterface* s, bool isDebug) {
- return registerSensor(s, isDebug);
+bool SensorService::registerDynamicSensorLocked(std::shared_ptr<SensorInterface> s, bool isDebug) {
+ return registerSensor(std::move(s), isDebug);
}
bool SensorService::unregisterDynamicSensorLocked(int handle) {
@@ -433,8 +523,8 @@
return ret;
}
-const Sensor& SensorService::registerVirtualSensor(SensorInterface* s, bool isDebug) {
- return registerSensor(s, isDebug, true);
+bool SensorService::registerVirtualSensor(std::shared_ptr<SensorInterface> s, bool isDebug) {
+ return registerSensor(std::move(s), isDebug, true);
}
SensorService::~SensorService() {
@@ -529,8 +619,8 @@
result.append("Recent Sensor events:\n");
for (auto&& i : mRecentEvent) {
- sp<SensorInterface> s = mSensors.getInterface(i.first);
- if (!i.second->isEmpty()) {
+ std::shared_ptr<SensorInterface> s = getSensorInterfaceFromHandle(i.first);
+ if (!i.second->isEmpty() && s != nullptr) {
if (privileged || s->getSensor().getRequiredPermission().isEmpty()) {
i.second->setFormat("normal");
} else {
@@ -647,8 +737,8 @@
// Write SensorEventsProto
token = proto.start(SENSOR_EVENTS);
for (auto&& i : mRecentEvent) {
- sp<SensorInterface> s = mSensors.getInterface(i.first);
- if (!i.second->isEmpty()) {
+ std::shared_ptr<SensorInterface> s = getSensorInterfaceFromHandle(i.first);
+ if (!i.second->isEmpty() && s != nullptr) {
i.second->setFormat(privileged || s->getSensor().getRequiredPermission().isEmpty() ?
"normal" : "mask_data");
const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
@@ -938,7 +1028,7 @@
handle = buffer[i].meta_data.sensor;
}
if (connection->hasSensor(handle)) {
- sp<SensorInterface> si = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = getSensorInterfaceFromHandle(handle);
// If this buffer has an event from a one_shot sensor and this connection is registered
// for this particular one_shot sensor, try cleaning up the connection.
if (si != nullptr &&
@@ -1003,6 +1093,7 @@
recordLastValueLocked(mSensorEventBuffer, count);
// handle virtual sensors
+ bool bufferNeedsSorting = false;
if (count && vcount) {
sensors_event_t const * const event = mSensorEventBuffer;
if (!mActiveVirtualSensors.empty()) {
@@ -1022,7 +1113,7 @@
break;
}
sensors_event_t out;
- sp<SensorInterface> si = mSensors.getInterface(handle);
+ std::shared_ptr<SensorInterface> si = getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
ALOGE("handle %d is not an valid virtual sensor", handle);
continue;
@@ -1038,12 +1129,37 @@
// record the last synthesized values
recordLastValueLocked(&mSensorEventBuffer[count], k);
count += k;
- // sort the buffer by time-stamps
- sortEventBuffer(mSensorEventBuffer, count);
+ bufferNeedsSorting = true;
}
}
}
+ // handle runtime sensors
+ {
+ size_t k = 0;
+ while (!mRuntimeSensorEventQueue.empty()) {
+ if (count + k >= minBufferSize) {
+ ALOGE("buffer too small to hold all events: count=%zd, k=%zu, size=%zu",
+ count, k, minBufferSize);
+ break;
+ }
+ mSensorEventBuffer[count + k] = mRuntimeSensorEventQueue.front();
+ mRuntimeSensorEventQueue.pop();
+ k++;
+ }
+ if (k) {
+ // record the last synthesized values
+ recordLastValueLocked(&mSensorEventBuffer[count], k);
+ count += k;
+ bufferNeedsSorting = true;
+ }
+ }
+
+ if (bufferNeedsSorting) {
+ // sort the buffer by time-stamps
+ sortEventBuffer(mSensorEventBuffer, count);
+ }
+
// handle backward compatibility for RotationVector sensor
if (halVersion < SENSORS_DEVICE_API_VERSION_1_0) {
for (int i = 0; i < count; i++) {
@@ -1092,12 +1208,12 @@
// force the handle to be consistent
s.handle = handle;
- SensorInterface *si = new HardwareSensor(s, uuid);
+ auto si = std::make_shared<HardwareSensor>(s, uuid);
// This will release hold on dynamic sensor meta, so it should be called
// after Sensor object is created.
device.handleDynamicSensorConnection(handle, true /*connected*/);
- registerDynamicSensorLocked(si);
+ registerDynamicSensorLocked(std::move(si));
} else {
ALOGE("Handle %d has been used, cannot use again before reboot.", handle);
}
@@ -1224,7 +1340,7 @@
}
bool SensorService::isVirtualSensor(int handle) const {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->isVirtual();
}
@@ -1233,7 +1349,7 @@
if (event.type == SENSOR_TYPE_META_DATA) {
handle = event.meta_data.sensor;
}
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->getSensor().isWakeUpSensor();
}
@@ -1342,19 +1458,37 @@
return accessibleSensorList;
}
+void SensorService::addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor,
+ Vector<Sensor>& accessibleSensorList) {
+ if (canAccessSensor(sensor, "can't see", opPackageName)) {
+ accessibleSensorList.add(sensor);
+ } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
+ ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
+ sensor.getName().string(), sensor.getRequiredPermission().string(),
+ sensor.getRequiredAppOp());
+ }
+}
+
Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) {
Vector<Sensor> accessibleSensorList;
mSensors.forEachSensor(
[this, &opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool {
if (sensor.isDynamicSensor()) {
- if (canAccessSensor(sensor, "can't see", opPackageName)) {
- accessibleSensorList.add(sensor);
- } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
- ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
- sensor.getName().string(),
- sensor.getRequiredPermission().string(),
- sensor.getRequiredAppOp());
- }
+ addSensorIfAccessible(opPackageName, sensor, accessibleSensorList);
+ }
+ return true;
+ });
+ makeUuidsIntoIdsForSensorList(accessibleSensorList);
+ return accessibleSensorList;
+}
+
+Vector<Sensor> SensorService::getRuntimeSensorList(const String16& opPackageName, int deviceId) {
+ Vector<Sensor> accessibleSensorList;
+ mSensors.forEachEntry(
+ [this, &opPackageName, deviceId, &accessibleSensorList] (
+ const SensorServiceUtil::SensorList::Entry& e) -> bool {
+ if (e.deviceId == deviceId) {
+ addSensorIfAccessible(opPackageName, e.si->getSensor(), accessibleSensorList);
}
return true;
});
@@ -1615,7 +1749,7 @@
int handle = mActiveSensors.keyAt(i);
if (c->hasSensor(handle)) {
ALOGD_IF(DEBUG_CONNECTIONS, "%zu: disabling handle=0x%08x", i, handle);
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor != nullptr) {
sensor->activate(c, false);
} else {
@@ -1729,7 +1863,7 @@
return NAME_NOT_FOUND;
}
-sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
+std::shared_ptr<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
return mSensors.getInterface(handle);
}
@@ -1739,7 +1873,7 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr ||
!canAccessSensor(sensor->getSensor(), "Tried enabling", opPackageName)) {
return BAD_VALUE;
@@ -1885,7 +2019,7 @@
Mutex::Autolock _l(mLock);
status_t err = cleanupWithoutDisableLocked(connection, handle);
if (err == NO_ERROR) {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
}
@@ -1931,7 +2065,7 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr ||
!canAccessSensor(sensor->getSensor(), "Tried configuring", opPackageName)) {
return BAD_VALUE;
@@ -1957,7 +2091,7 @@
Mutex::Autolock _l(mLock);
// Loop through all sensors for this connection and call flush on each of them.
for (int handle : connection->getActiveSensorHandles()) {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 4ba3c51..78df501 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -42,6 +42,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -143,6 +144,14 @@
virtual void onProximityActive(bool isActive) = 0;
};
+ class RuntimeSensorStateChangeCallback : public virtual RefBase {
+ public:
+ // Note that the callback is invoked from an async thread and can interact with the
+ // SensorService directly.
+ virtual void onStateChanged(bool enabled, int64_t samplingPeriodNanos,
+ int64_t batchReportLatencyNanos) = 0;
+ };
+
static char const* getServiceName() ANDROID_API { return "sensorservice"; }
SensorService() ANDROID_API;
@@ -169,6 +178,11 @@
status_t addProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
status_t removeProximityActiveListener(const sp<ProximityActiveListener>& callback) ANDROID_API;
+ int registerRuntimeSensor(const sensor_t& sensor, int deviceId,
+ sp<RuntimeSensorStateChangeCallback> callback) ANDROID_API;
+ status_t unregisterRuntimeSensor(int handle) ANDROID_API;
+ status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API;
+
// Returns true if a sensor should be throttled according to our rate-throttling rules.
static bool isSensorInCappedSet(int sensorType);
@@ -346,6 +360,7 @@
// ISensorServer interface
virtual Vector<Sensor> getSensorList(const String16& opPackageName);
virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName);
+ virtual Vector<Sensor> getRuntimeSensorList(const String16& opPackageName, int deviceId);
virtual sp<ISensorEventConnection> createSensorEventConnection(
const String8& packageName,
int requestedMode, const String16& opPackageName, const String16& attributionTag);
@@ -360,14 +375,14 @@
String8 getSensorName(int handle) const;
String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
- sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
+ std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
- const Sensor& registerSensor(SensorInterface* sensor,
- bool isDebug = false, bool isVirtual = false);
- const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false);
- const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false);
+ bool registerSensor(std::shared_ptr<SensorInterface> sensor, bool isDebug = false,
+ bool isVirtual = false, int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
+ bool registerVirtualSensor(std::shared_ptr<SensorInterface> sensor, bool isDebug = false);
+ bool registerDynamicSensorLocked(std::shared_ptr<SensorInterface> sensor, bool isDebug = false);
bool unregisterDynamicSensorLocked(int handle);
status_t cleanupWithoutDisable(const sp<SensorEventConnection>& connection, int handle);
status_t cleanupWithoutDisableLocked(const sp<SensorEventConnection>& connection, int handle);
@@ -375,6 +390,8 @@
sensors_event_t const* buffer, const int count);
bool canAccessSensor(const Sensor& sensor, const char* operation,
const String16& opPackageName);
+ void addSensorIfAccessible(const String16& opPackageName, const Sensor& sensor,
+ Vector<Sensor>& accessibleSensorList);
static bool hasPermissionForSensor(const Sensor& sensor);
static int getTargetSdkVersion(const String16& opPackageName);
static void resetTargetSdkVersionCache(const String16& opPackageName);
@@ -492,6 +509,7 @@
wp<const SensorEventConnection> * mMapFlushEventsToConnections;
std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
Mode mCurrentOperatingMode;
+ std::queue<sensors_event_t> mRuntimeSensorEventQueue;
// true if the head tracker sensor type is currently restricted to system usage only
// (can only be unrestricted for testing, via shell cmd)
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
new file mode 100644
index 0000000..542fcae
--- /dev/null
+++ b/services/sensorservice/aidl/Android.bp
@@ -0,0 +1,44 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library {
+ name: "libsensorserviceaidl",
+ srcs: [
+ "EventQueue.cpp",
+ "DirectReportChannel.cpp",
+ "SensorManager.cpp",
+ "utils.cpp",
+ ],
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ header_libs: ["jni_headers"],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ "libcutils",
+ "libbinder_ndk",
+ "libsensor",
+ "android.frameworks.sensorservice-V1-ndk",
+ "android.hardware.sensors-V2-ndk",
+ ],
+ export_include_dirs: [
+ "include/",
+ ],
+ static_libs: [
+ "android.hardware.sensors-V1-convert",
+ ],
+
+ export_header_lib_headers: ["jni_headers"],
+ local_include_dirs: [
+ "include/sensorserviceaidl/",
+ ],
+}
diff --git a/services/sensorservice/aidl/DirectReportChannel.cpp b/services/sensorservice/aidl/DirectReportChannel.cpp
new file mode 100644
index 0000000..cab53c1
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#include "DirectReportChannel.h"
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+DirectReportChannel::DirectReportChannel(::android::SensorManager& manager, int channelId)
+ : mManager(manager), mId(channelId) {}
+
+DirectReportChannel::~DirectReportChannel() {
+ mManager.destroyDirectChannel(mId);
+}
+
+ndk::ScopedAStatus DirectReportChannel::configure(
+ int32_t sensorHandle, ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+ int32_t* _aidl_return) {
+ int token = mManager.configureDirectChannel(mId, sensorHandle, static_cast<int>(rate));
+ if (token <= 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(token);
+ }
+ *_aidl_return = token;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/DirectReportChannel.h b/services/sensorservice/aidl/DirectReportChannel.h
new file mode 100644
index 0000000..d9ea73d
--- /dev/null
+++ b/services/sensorservice/aidl/DirectReportChannel.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <aidl/android/frameworks/sensorservice/BnDirectReportChannel.h>
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class DirectReportChannel final
+ : public ::aidl::android::frameworks::sensorservice::BnDirectReportChannel {
+public:
+ DirectReportChannel(::android::SensorManager& manager, int channelId);
+ ~DirectReportChannel();
+
+ ndk::ScopedAStatus configure(int32_t sensorHandle,
+ ::aidl::android::hardware::sensors::ISensors::RateLevel rate,
+ int32_t* _aidl_return) override;
+
+private:
+ ::android::SensorManager& mManager;
+ const int mId;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.cpp b/services/sensorservice/aidl/EventQueue.cpp
new file mode 100644
index 0000000..c394709
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#include "EventQueue.h"
+#include "utils.h"
+
+#include <android-base/logging.h>
+#include <utils/Looper.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::hardware::sensors::Event;
+
+class EventQueueLooperCallback : public ::android::LooperCallback {
+public:
+ EventQueueLooperCallback(sp<::android::SensorEventQueue> queue,
+ std::shared_ptr<IEventQueueCallback> callback)
+ : mQueue(queue), mCallback(callback) {}
+
+ int handleEvent(int /* fd */, int /* events */, void* /* data */) {
+ ASensorEvent event;
+ ssize_t actual;
+
+ auto internalQueue = mQueue.promote();
+ if (internalQueue == nullptr) {
+ return 1;
+ }
+
+ while ((actual = internalQueue->read(&event, 1)) > 0) {
+ internalQueue->sendAck(&event, actual);
+ ndk::ScopedAStatus ret = mCallback->onEvent(convertEvent(event));
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Failed to envoke EventQueueCallback: " << ret;
+ }
+ }
+
+ return 1; // continue to receive callbacks
+ }
+
+private:
+ wp<::android::SensorEventQueue> mQueue;
+ std::shared_ptr<IEventQueueCallback> mCallback;
+};
+
+EventQueue::EventQueue(std::shared_ptr<IEventQueueCallback> callback, sp<::android::Looper> looper,
+ sp<::android::SensorEventQueue> internalQueue)
+ : mLooper(looper), mInternalQueue(internalQueue) {
+ mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT,
+ new EventQueueLooperCallback(internalQueue, callback), nullptr);
+}
+
+EventQueue::~EventQueue() {
+ mLooper->removeFd(mInternalQueue->getFd());
+}
+
+ndk::ScopedAStatus EventQueue::enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+ int64_t in_maxBatchReportLatencyUs) {
+ return convertResult(mInternalQueue->enableSensor(in_sensorHandle, in_samplingPeriodUs,
+ in_maxBatchReportLatencyUs, 0));
+}
+
+ndk::ScopedAStatus EventQueue::disableSensor(int32_t in_sensorHandle) {
+ return convertResult(mInternalQueue->disableSensor(in_sensorHandle));
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/EventQueue.h b/services/sensorservice/aidl/EventQueue.h
new file mode 100644
index 0000000..0ae1eba
--- /dev/null
+++ b/services/sensorservice/aidl/EventQueue.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "SensorManagerAidl.h"
+
+#include <aidl/android/frameworks/sensorservice/BnEventQueue.h>
+#include <sensor/SensorManager.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+struct EventQueue final : public aidl::android::frameworks::sensorservice::BnEventQueue {
+ EventQueue(
+ std::shared_ptr<aidl::android::frameworks::sensorservice::IEventQueueCallback> callback,
+ sp<::android::Looper> looper, sp<::android::SensorEventQueue> internalQueue);
+ ~EventQueue();
+
+ ndk::ScopedAStatus enableSensor(int32_t in_sensorHandle, int32_t in_samplingPeriodUs,
+ int64_t in_maxBatchReportLatencyUs) override;
+ ndk::ScopedAStatus disableSensor(int32_t sensorHandle) override;
+
+private:
+ friend class EventQueueLooperCallback;
+ sp<::android::Looper> mLooper;
+ sp<::android::SensorEventQueue> mInternalQueue;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
new file mode 100644
index 0000000..9b03344
--- /dev/null
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+// LOG_TAG defined via build flag.
+#ifndef LOG_TAG
+#define LOG_TAG "AidlSensorManager"
+#endif
+
+#include "DirectReportChannel.h"
+#include "EventQueue.h"
+#include "SensorManagerAidl.h"
+#include "utils.h"
+
+#include <aidl/android/hardware/sensors/ISensors.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <sched.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+using ::aidl::android::frameworks::sensorservice::IDirectReportChannel;
+using ::aidl::android::frameworks::sensorservice::IEventQueue;
+using ::aidl::android::frameworks::sensorservice::IEventQueueCallback;
+using ::aidl::android::frameworks::sensorservice::ISensorManager;
+using ::aidl::android::hardware::common::Ashmem;
+using ::aidl::android::hardware::sensors::ISensors;
+using ::aidl::android::hardware::sensors::SensorInfo;
+using ::aidl::android::hardware::sensors::SensorType;
+using ::android::frameworks::sensorservice::implementation::SensorManagerAidl;
+
+static const char* POLL_THREAD_NAME = "aidl_ssvc_poll";
+
+SensorManagerAidl::SensorManagerAidl(JavaVM* vm)
+ : mLooper(new Looper(false)), mStopThread(true), mJavaVm(vm) {}
+SensorManagerAidl::~SensorManagerAidl() {
+ // Stops pollAll inside the thread.
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+
+ mStopThread = true;
+ if (mLooper != nullptr) {
+ mLooper->wake();
+ }
+ if (mPollThread.joinable()) {
+ mPollThread.join();
+ }
+}
+
+ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type,
+ const native_handle_t* handle,
+ std::shared_ptr<IDirectReportChannel>* chan) {
+ int channelId = manager.createDirectChannel(size, type, handle);
+ if (channelId < 0) {
+ return convertResult(channelId);
+ }
+ if (channelId == 0) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+ *chan = ndk::SharedRefBase::make<DirectReportChannel>(manager, channelId);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createAshmemDirectChannel(
+ const Ashmem& in_mem, int64_t in_size,
+ std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+ if (in_size > in_mem.size || in_size < ISensors::DIRECT_REPORT_SENSOR_EVENT_TOTAL_LENGTH) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+ }
+ native_handle_t* handle = native_handle_create(1, 0);
+ handle->data[0] = dup(in_mem.fd.get());
+
+ auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_ASHMEM,
+ handle, _aidl_return);
+ int result = native_handle_close(handle);
+ CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+ result = native_handle_delete(handle);
+ CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+ return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createGrallocDirectChannel(
+ const ndk::ScopedFileDescriptor& in_mem, int64_t in_size,
+ std::shared_ptr<IDirectReportChannel>* _aidl_return) {
+ native_handle_t* handle = native_handle_create(1, 0);
+ handle->data[0] = dup(in_mem.get());
+
+ auto status = createDirectChannel(getInternalManager(), in_size, SENSOR_DIRECT_MEM_TYPE_GRALLOC,
+ handle, _aidl_return);
+ int result = native_handle_close(handle);
+ CHECK(result == 0) << "Failed to close the native_handle_t: " << result;
+ result = native_handle_delete(handle);
+ CHECK(result == 0) << "Failed to delete the native_handle_t: " << result;
+
+ return status;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::createEventQueue(
+ const std::shared_ptr<IEventQueueCallback>& in_callback,
+ std::shared_ptr<IEventQueue>* _aidl_return) {
+ if (in_callback == nullptr) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_BAD_VALUE);
+ }
+
+ sp<::android::Looper> looper = getLooper();
+ if (looper == nullptr) {
+ LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue cannot initialize looper";
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+
+ String8 package(String8::format("aidl_client_pid_%d", AIBinder_getCallingPid()));
+ sp<::android::SensorEventQueue> internalQueue = getInternalManager().createEventQueue(package);
+ if (internalQueue == nullptr) {
+ LOG(ERROR) << "::android::SensorManagerAidl::createEventQueue returns nullptr.";
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+
+ *_aidl_return = ndk::SharedRefBase::make<EventQueue>(in_callback, looper, internalQueue);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+SensorInfo convertSensor(Sensor src) {
+ SensorInfo dst;
+ dst.sensorHandle = src.getHandle();
+ dst.name = src.getName();
+ dst.vendor = src.getVendor();
+ dst.version = src.getVersion();
+ dst.type = static_cast<SensorType>(src.getType());
+ dst.typeAsString = src.getStringType();
+ // maxRange uses maxValue because ::android::Sensor wraps the
+ // internal sensor_t in this way.
+ dst.maxRange = src.getMaxValue();
+ dst.resolution = src.getResolution();
+ dst.power = src.getPowerUsage();
+ dst.minDelayUs = src.getMinDelay();
+ dst.fifoReservedEventCount = src.getFifoReservedEventCount();
+ dst.fifoMaxEventCount = src.getFifoMaxEventCount();
+ dst.requiredPermission = src.getRequiredPermission();
+ dst.maxDelayUs = src.getMaxDelay();
+ dst.flags = src.getFlags();
+ return dst;
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getDefaultSensor(SensorType in_type,
+ SensorInfo* _aidl_return) {
+ ::android::Sensor const* sensor =
+ getInternalManager().getDefaultSensor(static_cast<int>(in_type));
+ if (!sensor) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_NOT_EXIST);
+ }
+ *_aidl_return = convertSensor(*sensor);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SensorManagerAidl::getSensorList(std::vector<SensorInfo>* _aidl_return) {
+ Sensor const* const* list;
+ _aidl_return->clear();
+ ssize_t count = getInternalManager().getSensorList(&list);
+ if (count < 0 || list == nullptr) {
+ LOG(ERROR) << "SensorMAanger::getSensorList failed with count: " << count;
+ return ndk::ScopedAStatus::fromServiceSpecificError(ISensorManager::RESULT_UNKNOWN_ERROR);
+ }
+ _aidl_return->reserve(static_cast<size_t>(count));
+ for (ssize_t i = 0; i < count; ++i) {
+ _aidl_return->push_back(convertSensor(*list[i]));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+::android::SensorManager& SensorManagerAidl::getInternalManager() {
+ std::lock_guard<std::mutex> lock(mInternalManagerMutex);
+ if (mInternalManager == nullptr) {
+ mInternalManager = &::android::SensorManager::getInstanceForPackage(
+ String16(ISensorManager::descriptor));
+ }
+ return *mInternalManager;
+}
+
+/* One global looper for all event queues created from this SensorManager. */
+sp<Looper> SensorManagerAidl::getLooper() {
+ std::lock_guard<std::mutex> lock(mThreadMutex);
+
+ if (!mPollThread.joinable()) {
+ // if thread not initialized, start thread
+ mStopThread = false;
+ std::thread pollThread{[&stopThread = mStopThread, looper = mLooper, javaVm = mJavaVm] {
+ struct sched_param p = {};
+ p.sched_priority = 10;
+ if (sched_setscheduler(0 /* current thread*/, SCHED_FIFO, &p) != 0) {
+ LOG(ERROR) << "Could not use SCHED_FIFO for looper thread: " << strerror(errno);
+ }
+
+ // set looper
+ Looper::setForThread(looper);
+
+ // Attach the thread to JavaVM so that pollAll do not crash if the thread
+ // eventually calls into Java.
+ JavaVMAttachArgs args{.version = JNI_VERSION_1_2,
+ .name = POLL_THREAD_NAME,
+ .group = nullptr};
+ JNIEnv* env;
+ if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) {
+ LOG(FATAL) << "Cannot attach SensorManager looper thread to Java VM.";
+ }
+
+ LOG(INFO) << POLL_THREAD_NAME << " started.";
+ for (;;) {
+ int pollResult = looper->pollAll(-1 /* timeout */);
+ if (pollResult == Looper::POLL_WAKE) {
+ if (stopThread == true) {
+ LOG(INFO) << POLL_THREAD_NAME << ": requested to stop";
+ break;
+ } else {
+ LOG(INFO) << POLL_THREAD_NAME << ": spurious wake up, back to work";
+ }
+ } else {
+ LOG(ERROR) << POLL_THREAD_NAME << ": Looper::pollAll returns unexpected "
+ << pollResult;
+ break;
+ }
+ }
+
+ if (javaVm->DetachCurrentThread() != JNI_OK) {
+ LOG(ERROR) << "Cannot detach SensorManager looper thread from Java VM.";
+ }
+
+ LOG(INFO) << POLL_THREAD_NAME << " is terminated.";
+ }};
+ mPollThread = std::move(pollThread);
+ }
+ return mLooper;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
new file mode 100644
index 0000000..5301fe9
--- /dev/null
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -0,0 +1,52 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+ name: "libsensorserviceaidl_fuzzer",
+ defaults: [
+ "service_fuzzer_defaults",
+ ],
+ host_supported: true,
+ static_libs: [
+ "libsensorserviceaidl",
+ "libpermission",
+ "android.frameworks.sensorservice-V1-ndk",
+ "android.hardware.sensors-V1-convert",
+ "android.hardware.sensors-V2-ndk",
+ "android.hardware.common-V2-ndk",
+ "libsensor",
+ "libfakeservicemanager",
+ "libcutils",
+ "liblog",
+ ],
+ srcs: [
+ "fuzzer.cpp",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-sensors@google.com",
+ "devinmoore@google.com",
+ ],
+ },
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ diag: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ },
+ address: true,
+ integer_overflow: true,
+ },
+
+}
diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
new file mode 100644
index 0000000..1b63d76
--- /dev/null
+++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <ServiceManager.h>
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <fuzzbinder/random_binder.h>
+#include <sensorserviceaidl/SensorManagerAidl.h>
+
+using android::fuzzService;
+using android::frameworks::sensorservice::implementation::SensorManagerAidl;
+using ndk::SharedRefBase;
+
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager();
+ std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); });
+ fakeServiceManager->clear();
+
+ FuzzedDataProvider fdp(data, size);
+ android::sp<android::IBinder> binder = android::getRandomBinder(&fdp);
+ if (binder == nullptr) {
+ // Nothing to do if we get a null binder. It will cause SensorManager to
+ // hang while trying to get sensorservice.
+ return 0;
+ }
+
+ CHECK(android::NO_ERROR == fakeServiceManager->addService(android::String16("sensorservice"),
+ binder));
+
+ std::shared_ptr<SensorManagerAidl> sensorService =
+ ndk::SharedRefBase::make<SensorManagerAidl>(nullptr);
+
+ fuzzService(sensorService->asBinder().get(), std::move(fdp));
+
+ return 0;
+}
diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
new file mode 100644
index 0000000..c77ee88
--- /dev/null
+++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
@@ -0,0 +1,74 @@
+/*
+ * 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 <aidl/android/frameworks/sensorservice/BnSensorManager.h>
+#include <jni.h>
+#include <sensor/SensorManager.h>
+#include <utils/Looper.h>
+#include <mutex>
+#include <thread>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+class SensorManagerAidl : public ::aidl::android::frameworks::sensorservice::BnSensorManager {
+public:
+ explicit SensorManagerAidl(JavaVM* vm);
+ ~SensorManagerAidl();
+
+ ::ndk::ScopedAStatus createAshmemDirectChannel(
+ const ::aidl::android::hardware::common::Ashmem& in_mem, int64_t in_size,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+ _aidl_return) override;
+ ::ndk::ScopedAStatus createEventQueue(
+ const std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueueCallback>&
+ in_callback,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IEventQueue>* _aidl_return)
+ override;
+ ::ndk::ScopedAStatus createGrallocDirectChannel(
+ const ::ndk::ScopedFileDescriptor& in_buffer, int64_t in_size,
+ std::shared_ptr<::aidl::android::frameworks::sensorservice::IDirectReportChannel>*
+ _aidl_return) override;
+ ::ndk::ScopedAStatus getDefaultSensor(
+ ::aidl::android::hardware::sensors::SensorType in_type,
+ ::aidl::android::hardware::sensors::SensorInfo* _aidl_return) override;
+ ::ndk::ScopedAStatus getSensorList(
+ std::vector<::aidl::android::hardware::sensors::SensorInfo>* _aidl_return) override;
+
+private:
+ // Block until ::android::SensorManager is initialized.
+ ::android::SensorManager& getInternalManager();
+ sp<Looper> getLooper();
+
+ std::mutex mInternalManagerMutex;
+ ::android::SensorManager* mInternalManager = nullptr; // does not own
+ sp<Looper> mLooper;
+
+ volatile bool mStopThread;
+ std::mutex mThreadMutex; // protects mPollThread
+ std::thread mPollThread;
+
+ JavaVM* mJavaVm;
+};
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
new file mode 100644
index 0000000..beb38b9
--- /dev/null
+++ b/services/sensorservice/aidl/utils.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include "utils.h"
+
+#include <aidl/android/frameworks/sensorservice/ISensorManager.h>
+#include <aidl/sensors/convert.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+ndk::ScopedAStatus convertResult(status_t src) {
+ using ::aidl::android::frameworks::sensorservice::ISensorManager;
+
+ int err = 0;
+ switch (src) {
+ case OK:
+ return ndk::ScopedAStatus::ok();
+ case NAME_NOT_FOUND:
+ err = ISensorManager::RESULT_NOT_EXIST;
+ break;
+ case NO_MEMORY:
+ err = ISensorManager::RESULT_NO_MEMORY;
+ break;
+ case NO_INIT:
+ err = ISensorManager::RESULT_NO_INIT;
+ break;
+ case PERMISSION_DENIED:
+ err = ISensorManager::RESULT_PERMISSION_DENIED;
+ break;
+ case BAD_VALUE:
+ err = ISensorManager::RESULT_BAD_VALUE;
+ break;
+ case INVALID_OPERATION:
+ err = ISensorManager::RESULT_INVALID_OPERATION;
+ break;
+ default:
+ err = ISensorManager::RESULT_UNKNOWN_ERROR;
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificError(err);
+}
+
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
+ ::aidl::android::hardware::sensors::Event dst;
+ ::android::hardware::sensors::implementation::
+ convertFromASensorEvent(src, &dst);
+ return dst;
+}
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/aidl/utils.h b/services/sensorservice/aidl/utils.h
new file mode 100644
index 0000000..06ba59e
--- /dev/null
+++ b/services/sensorservice/aidl/utils.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <aidl/android/hardware/sensors/Event.h>
+#include <sensor/Sensor.h>
+
+namespace android {
+namespace frameworks {
+namespace sensorservice {
+namespace implementation {
+
+::ndk::ScopedAStatus convertResult(status_t status);
+::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& event);
+
+} // namespace implementation
+} // namespace sensorservice
+} // namespace frameworks
+} // namespace android
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
::android::hardware::sensors::V1_0::Event dst;
- ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
- reinterpret_cast<const sensors_event_t&>(src), &dst);
+ ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent(
+ src, &dst);
return dst;
}
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 9e13849..410a5af 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -30,14 +30,14 @@
StatsHal::StatsHal() {}
ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
- std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Not a valid vendor atom ID");
}
- if (reverseDomainName.length() > 50) {
- ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ if (vendorAtom.reverseDomainName.length() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.",
+ vendorAtom.reverseDomainName.c_str());
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Vendor atom reverse domain name is too long");
}
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index ae0a984..d27d989 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -112,13 +112,13 @@
}
hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
- std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
return hardware::Void();
}
- if (reverseDomainName.length() > 50) {
- ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ if (vendorAtom.reverseDomainName.size() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.",
+ vendorAtom.reverseDomainName.c_str());
return hardware::Void();
}
AStatsEvent* event = AStatsEvent_obtain();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 14fdd12..e0dcab5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,8 +26,8 @@
name: "libsurfaceflinger_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "librenderengine_deps",
"surfaceflinger_defaults",
- "skia_renderengine_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
@@ -157,13 +157,15 @@
"EventLog/EventLog.cpp",
"FrontEnd/LayerCreationArgs.cpp",
"FrontEnd/LayerHandle.cpp",
+ "FrontEnd/LayerHierarchy.cpp",
+ "FrontEnd/LayerLifecycleManager.cpp",
+ "FrontEnd/RequestedLayerState.cpp",
"FrontEnd/TransactionHandler.cpp",
"FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
- "HwcSlotGenerator.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2bd8f32..09e41ff 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -118,7 +118,8 @@
Usage::READABLE));
}
-void ClientCache::erase(const client_cache_t& cacheId) {
+sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) {
+ sp<GraphicBuffer> buffer;
auto& [processToken, id] = cacheId;
std::vector<sp<ErasedRecipient>> pendingErase;
{
@@ -126,9 +127,11 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
ALOGE("failed to erase buffer, could not retrieve buffer");
- return;
+ return nullptr;
}
+ buffer = buf->buffer->getBuffer();
+
for (auto& recipient : buf->recipients) {
sp<ErasedRecipient> erasedRecipient = recipient.promote();
if (erasedRecipient) {
@@ -142,6 +145,7 @@
for (auto& recipient : pendingErase) {
recipient->bufferErased(cacheId);
}
+ return buffer;
}
std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index cdeac2b..b56b252 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -33,6 +33,17 @@
namespace android {
+// This class manages a cache of buffer handles between SurfaceFlinger clients
+// and the SurfaceFlinger process which optimizes away some of the cost of
+// sending buffer handles across processes.
+//
+// Buffers are explicitly cached and uncached by the SurfaceFlinger client. When
+// a buffer is uncached, it is not only purged from this cache, but the buffer
+// ID is also passed down to CompositionEngine to purge it from a similar cache
+// used between SurfaceFlinger and Composer HAL. The buffer ID used to purge
+// both the SurfaceFlinger side of this other cache, as well as Composer HAL's
+// side of the cache.
+//
class ClientCache : public Singleton<ClientCache> {
public:
ClientCache();
@@ -41,7 +52,8 @@
base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add(
const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
- void erase(const client_cache_t& cacheId);
+
+ sp<GraphicBuffer> erase(const client_cache_t& cacheId);
std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 30d34a5..f3a0186 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@
name: "libcompositionengine_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
cflags: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index f861fc9..4777f13 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -52,6 +52,9 @@
// All the layers that have queued updates.
Layers layersWithQueuedFrames;
+ // All graphic buffers that will no longer be used and should be removed from caches.
+ std::vector<uint64_t> bufferIdsToUncache;
+
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
@@ -64,9 +67,6 @@
// Used to correctly apply an inverse-display buffer transform if applicable
ui::Transform::RotationFlags internalDisplayRotationFlags{ui::Transform::ROT_0};
- // If true, GPU clocks will be increased when rendering blurs
- bool blursAreExpensive{false};
-
// If true, the complete output geometry needs to be recomputed this frame
bool updatingOutputGeometryThisFrame{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 974f7c6..ad98e93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -163,7 +163,6 @@
// The buffer and related state
sp<GraphicBuffer> buffer;
- int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
uint64_t frameNumber = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 874b330..52ebd9e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -23,6 +23,7 @@
#include <type_traits>
#include <unordered_map>
#include <utility>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
@@ -272,6 +273,7 @@
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+ virtual void uncacheBuffers(const std::vector<uint64_t>&) = 0;
virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
@@ -288,12 +290,11 @@
using GpuCompositionResult = compositionengine::impl::GpuCompositionResult;
// Runs prepare frame in another thread while running client composition using
// the previous frame's composition strategy.
- virtual GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) = 0;
+ virtual GpuCompositionResult prepareFrameAsync() = 0;
virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
- virtual void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) = 0;
+ virtual void finishFrame(GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
+ const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
virtual void postFramebuffer() = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual bool chooseCompositionStrategy(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 6d0c395..4dbf8d2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <optional>
#include <string>
+#include <vector>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
@@ -81,6 +82,10 @@
// TODO(lpique): Make this protected once it is only internally called.
virtual CompositionState& editState() = 0;
+ // Clear the cache entries for a set of buffers that SurfaceFlinger no
+ // longer cares about.
+ virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0;
+
// Recalculates the state of the output layer from the output-independent
// layer. If includeGeometry is false, the geometry state can be skipped.
// internalDisplayRotationFlags must be set to the rotation flags for the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 33a10a3..6cf1d68 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -61,7 +61,7 @@
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
void setExpensiveRenderingExpected(bool) override;
- void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+ void finishFrame(GpuCompositionResult&&) override;
// compositionengine::Display overrides
DisplayId getId() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index fd22aa3..b6a4240 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -17,7 +17,8 @@
#pragma once
#include <cstdint>
-#include <vector>
+#include <stack>
+#include <unordered_map>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -37,35 +38,76 @@
namespace compositionengine::impl {
-// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
-// HWC display and layer. When updating a display target or a layer buffer,
-// we have the option to send the buffer handle over or to request the HAL to
-// retrieve it from its cache. The latter is cheaper since it eliminates the
-// overhead to transfer the handle over the trasport layer, and the overhead
-// for the HAL to clone and retain the handle.
-//
-// To be able to find out whether a buffer is already in the HAL's cache, we
-// use HWComposerBufferCache to mirror the cache in SF.
-class HwcBufferCache {
-public:
- HwcBufferCache();
- // Given a buffer, return the HWC cache slot and
- // buffer to be sent to HWC.
- //
- // outBuffer is set to buffer when buffer is not in the HWC cache;
- // otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer);
+// The buffer cache returns both a slot and the buffer that should be sent to HWC. In cases
+// where the buffer is already cached, the buffer is a nullptr and will not be sent to HWC as
+// an optimization.
+struct HwcSlotAndBuffer {
+ uint32_t slot;
+ sp<GraphicBuffer> buffer;
+};
- // Special caching slot for the layer caching feature.
- static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+//
+// Manages the slot assignments for a buffers stored in Composer HAL's cache.
+//
+// Cache slots are an optimization when communicating buffer handles to Composer
+// HAL. When updating a layer's buffer, we can either send a new buffer handle
+// along with it's slot assignment or request the HAL to reuse a buffer handle
+// that we've already sent by using the slot assignment. The latter is cheaper
+// since it eliminates the overhead to transfer the buffer handle over IPC and
+// the overhead for the HAL to clone the handle.
+//
+class HwcBufferCache {
+private:
+ static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
+
+public:
+ // public for testing
+ // Override buffers don't use the normal cache slots because we don't want them to evict client
+ // buffers from the cache. We add an extra slot at the end for the override buffers.
+ static const constexpr size_t kOverrideBufferSlot = kMaxLayerBufferCount;
+
+ HwcBufferCache();
+
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // A special slot number is used for override buffers.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getOverrideHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+
+ //
+ // When a client process discards a buffer, it needs to be purged from the HWC cache.
+ //
+ // Returns the slot number of the buffer, or UINT32_MAX if it wasn't found in the cache.
+ //
+ uint32_t uncache(uint64_t graphicBufferId);
private:
- // an array where the index corresponds to a slot and the value corresponds to a (counter,
- // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
- // or used and allows us to keep track of the least-recently used buffer.
- static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
+ uint32_t cache(const sp<GraphicBuffer>& buffer);
+ uint32_t getLeastRecentlyUsedSlot();
+
+ struct Cache {
+ sp<GraphicBuffer> buffer;
+ uint32_t slot;
+ // Cache entries are evicted according to least-recently-used when more than
+ // kMaxLayerBufferCount unique buffers have been sent to a layer.
+ uint64_t lruCounter;
+ };
+
+ std::unordered_map<uint64_t, Cache> mCacheByBufferId;
+ sp<GraphicBuffer> mLastOverrideBuffer;
+ std::stack<uint32_t> mFreeSlots;
+ uint64_t mLeastRecentlyUsedCounter;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 9ca5da9..8ec77c0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -82,6 +82,7 @@
void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
void present(const CompositionRefreshArgs&) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
@@ -95,11 +96,10 @@
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
- GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) override;
+ GpuCompositionResult prepareFrameAsync() override;
void devOptRepaintFlash(const CompositionRefreshArgs&) override;
- void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+ void finishFrame(GpuCompositionResult&&) override;
std::optional<base::unique_fd> composeSurfaces(const Region&,
- const compositionengine::CompositionRefreshArgs&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&) override;
void postFramebuffer() override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6d4abf9..f383392 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,6 +20,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
@@ -44,6 +45,8 @@
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 2b383c1..2a5bfae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -136,6 +136,9 @@
// cost of sending reused buffers to the HWC.
HwcBufferCache hwcBufferCache;
+ // The previously-active buffer for this layer.
+ uint32_t activeBufferSlot;
+
// Set to true when overridden info has been sent to HW composer
bool stateOverridden = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 14922a4..12e063b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -36,6 +36,7 @@
// sp<StrictMock<LayerFE>>::make()
friend class sp<LayerFE>;
friend class testing::StrictMock<LayerFE>;
+ friend class testing::NiceMock<LayerFE>;
public:
virtual ~LayerFE();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 7592cac..a56fc79 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -82,6 +82,7 @@
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
MOCK_METHOD2(rebuildLayerStacks,
void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD2(collectVisibleLayers,
@@ -99,7 +100,7 @@
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(chooseCompositionStrategy,
bool(std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD1(chooseCompositionStrategyAsync,
@@ -109,14 +110,12 @@
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD2(finishFrame,
- void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&));
+ MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&,
- const compositionengine::CompositionRefreshArgs& refreshArgs,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index c22f1bf..5fef63a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -35,6 +35,8 @@
MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
+
MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 24669c2..d50a768 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -428,8 +428,7 @@
mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
}
-void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
- GpuCompositionResult&& result) {
+void Display::finishFrame(GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
@@ -439,7 +438,7 @@
return;
}
- impl::Output::finishFrame(refreshArgs, std::move(result));
+ impl::Output::finishFrame(std::move(result));
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f95382d..34ed214 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -16,43 +16,80 @@
#include <compositionengine/impl/HwcBufferCache.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <gui/BufferQueue.h>
#include <ui/GraphicBuffer.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-
namespace android::compositionengine::impl {
HwcBufferCache::HwcBufferCache() {
- std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr));
+ for (uint32_t i = kMaxLayerBufferCount; i-- > 0;) {
+ mFreeSlots.push(i);
+ }
}
-void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- // default is 0
- if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
- slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
- *outSlot = 0;
- } else {
- *outSlot = static_cast<uint32_t>(slot);
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+ // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send
+ // in nullptrs.
+ if (buffer == nullptr) {
+ return {0, nullptr};
}
-
- auto& currentBuffer = mBuffers[*outSlot];
- wp<GraphicBuffer> weakCopy(buffer);
- if (currentBuffer == weakCopy) {
- // already cached in HWC, skip sending the buffer
- *outBuffer = nullptr;
- } else {
- *outBuffer = buffer;
-
- // update cache
- currentBuffer = buffer;
+ if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
+ Cache& cache = i->second;
+ // mark this cache slot as more recently used so it won't get evicted anytime soon
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ return {cache.slot, nullptr};
}
+ return {cache(buffer), buffer};
+}
+
+HwcSlotAndBuffer HwcBufferCache::getOverrideHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+ if (buffer == mLastOverrideBuffer) {
+ return {kOverrideBufferSlot, nullptr};
+ }
+ mLastOverrideBuffer = buffer;
+ return {kOverrideBufferSlot, buffer};
+}
+
+uint32_t HwcBufferCache::uncache(uint64_t bufferId) {
+ if (auto i = mCacheByBufferId.find(bufferId); i != mCacheByBufferId.end()) {
+ uint32_t slot = i->second.slot;
+ mCacheByBufferId.erase(i);
+ mFreeSlots.push(slot);
+ return slot;
+ }
+ if (mLastOverrideBuffer && bufferId == mLastOverrideBuffer->getId()) {
+ mLastOverrideBuffer = nullptr;
+ return kOverrideBufferSlot;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t HwcBufferCache::cache(const sp<GraphicBuffer>& buffer) {
+ Cache cache;
+ cache.slot = getLeastRecentlyUsedSlot();
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ cache.buffer = buffer;
+ mCacheByBufferId.emplace(buffer->getId(), cache);
+ return cache.slot;
+}
+
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+ if (mFreeSlots.empty()) {
+ assert(!mCacheByBufferId.empty());
+ // evict the least recently used cache entry
+ auto cacheToErase = mCacheByBufferId.begin();
+ for (auto i = cacheToErase; i != mCacheByBufferId.end(); ++i) {
+ if (i->second.lruCounter < cacheToErase->second.lruCounter) {
+ cacheToErase = i;
+ }
+ }
+ uint32_t slot = cacheToErase->second.slot;
+ mCacheByBufferId.erase(cacheToErase);
+ mFreeSlots.push(slot);
+ }
+ uint32_t slot = mFreeSlots.top();
+ mFreeSlots.pop();
+ return slot;
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 6631a27..a405c4d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -106,7 +106,6 @@
dumpVal(out, "composition type", toString(compositionType), compositionType);
out.append("\n buffer: ");
- dumpVal(out, "slot", bufferSlot);
dumpVal(out, "buffer", buffer.get());
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3ee8017..d513731 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -428,6 +428,7 @@
ALOGV(__FUNCTION__);
rebuildLayerStacks(refreshArgs, geomSnapshots);
+ uncacheBuffers(refreshArgs.bufferIdsToUncache);
}
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -444,17 +445,26 @@
GpuCompositionResult result;
const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
if (predictCompositionStrategy) {
- result = prepareFrameAsync(refreshArgs);
+ result = prepareFrameAsync();
} else {
prepareFrame();
}
devOptRepaintFlash(refreshArgs);
- finishFrame(refreshArgs, std::move(result));
+ finishFrame(std::move(result));
postFramebuffer();
renderCachedSets(refreshArgs);
}
+void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ if (bufferIdsToUncache.empty()) {
+ return;
+ }
+ for (auto outputLayer : getOutputLayersOrderedByZ()) {
+ outputLayer->uncacheBuffers(bufferIdsToUncache);
+ }
+}
+
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
ATRACE_CALL();
@@ -1059,7 +1069,7 @@
[&, changes]() { return chooseCompositionStrategy(changes); });
}
-GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+GpuCompositionResult Output::prepareFrameAsync() {
ATRACE_CALL();
ALOGV(__FUNCTION__);
auto& state = editState();
@@ -1079,7 +1089,7 @@
GpuCompositionResult compositionResult;
if (dequeueSucceeded) {
std::optional<base::unique_fd> optFd =
- composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ composeSurfaces(Region::INVALID_REGION, buffer, bufferFence);
if (optFd) {
compositionResult.fence = std::move(*optFd);
}
@@ -1117,7 +1127,7 @@
std::shared_ptr<renderengine::ExternalTexture> buffer;
updateProtectedContentState();
dequeueRenderBuffer(&bufferFence, &buffer);
- static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence));
+ static_cast<void>(composeSurfaces(dirtyRegion, buffer, bufferFence));
mRenderSurface->queueBuffer(base::unique_fd());
}
}
@@ -1129,7 +1139,7 @@
prepareFrame();
}
-void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
+void Output::finishFrame(GpuCompositionResult&& result) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
const auto& outputState = getState();
@@ -1154,7 +1164,7 @@
}
// Repaint the framebuffer (if needed), getting the optional fence for when
// the composition completes.
- optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ optReadyFence = composeSurfaces(Region::INVALID_REGION, buffer, bufferFence);
}
if (!optReadyFence) {
return;
@@ -1208,8 +1218,8 @@
}
std::optional<base::unique_fd> Output::composeSurfaces(
- const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs,
- std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) {
+ const Region& debugRegion, std::shared_ptr<renderengine::ExternalTexture> tex,
+ base::unique_fd& fd) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -1265,9 +1275,7 @@
// or complex GPU shaders and it's expensive. We boost the GPU frequency so that
// GPU composition can finish in time. We must reset GPU frequency afterwards,
// because high frequency consumes extra battery.
- const bool expensiveBlurs =
- refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
- const bool expensiveRenderingExpected = expensiveBlurs ||
+ const bool expensiveRenderingExpected =
std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
[outputDataspace =
clientCompositionDisplay.outputDataspace](const auto& layer) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index a39c527..766d7ea 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -610,6 +610,29 @@
}
}
+void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) {
+ auto& state = editState();
+ // Skip doing this if there is no HWC interface
+ if (!state.hwc) {
+ return;
+ }
+
+ std::vector<uint32_t> slotsToClear;
+ for (uint64_t bufferId : bufferIdsToUncache) {
+ uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId);
+ if (slot != UINT32_MAX) {
+ slotsToClear.push_back(slot);
+ }
+ }
+
+ hal::Error error =
+ state.hwc->hwcLayer->setBufferSlotsToClear(slotsToClear, state.hwc->activeBufferSlot);
+ if (error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to clear buffer slots: %s (%d)", getLayerFE().getDebugName(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+}
+
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
@@ -622,27 +645,34 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- sp<GraphicBuffer> buffer = outputIndependentState.buffer;
- sp<Fence> acquireFence = outputIndependentState.acquireFence;
- int slot = outputIndependentState.bufferSlot;
- if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
- buffer = getState().overrideInfo.buffer->getBuffer();
- acquireFence = getState().overrideInfo.acquireFence;
- slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
+ HwcSlotAndBuffer hwcSlotAndBuffer;
+ sp<Fence> hwcFence;
+ {
+ // Editing the state only because we update the HWC buffer cache and active buffer.
+ auto& state = editState();
+ // Override buffers use a special cache slot so that they don't evict client buffers.
+ if (state.overrideInfo.buffer != nullptr && !skipLayer) {
+ hwcSlotAndBuffer = state.hwc->hwcBufferCache.getOverrideHwcSlotAndBuffer(
+ state.overrideInfo.buffer->getBuffer());
+ hwcFence = state.overrideInfo.acquireFence;
+ } else {
+ hwcSlotAndBuffer =
+ state.hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
+ hwcFence = outputIndependentState.acquireFence;
+ }
+
+ // Keep track of the active buffer slot, so we can restore it after clearing other buffer
+ // slots.
+ if (hwcSlotAndBuffer.buffer) {
+ state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot;
+ }
}
- ALOGV("Writing buffer %p", buffer.get());
-
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- // We need access to the output-dependent state for the buffer cache there,
- // though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
-
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+ hwcSlotAndBuffer.buffer->handle, to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 95459c0..0756c1b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -959,7 +959,7 @@
mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- mDisplay->finishFrame({}, std::move(mResultWithBuffer));
+ mDisplay->finishFrame(std::move(mResultWithBuffer));
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -980,7 +980,7 @@
gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
gpuDisplay->beginFrame();
- gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+ gpuDisplay->finishFrame(std::move(mResultWithoutBuffer));
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
@@ -1001,7 +1001,7 @@
gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
gpuDisplay->beginFrame();
- gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+ gpuDisplay->finishFrame(std::move(mResultWithoutBuffer));
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
@@ -1022,7 +1022,7 @@
gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
gpuDisplay->beginFrame();
- gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
+ gpuDisplay->finishFrame(std::move(mResultWithBuffer));
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index 7197780..c5fb594 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -22,66 +22,172 @@
namespace android::compositionengine {
namespace {
-class TestableHwcBufferCache : public impl::HwcBufferCache {
-public:
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
- }
-};
+using impl::HwcBufferCache;
+using impl::HwcSlotAndBuffer;
class HwcBufferCacheTest : public testing::Test {
public:
~HwcBufferCacheTest() override = default;
- void testSlot(const int inSlot, const uint32_t expectedSlot) {
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer1, outBuffer);
-
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer2, outBuffer);
-
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
- }
-
- impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
sp<GraphicBuffer> mBuffer2 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
};
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
- testSlot(0, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot);
+ EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2);
}
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
- testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
}
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
- testSlot(-123, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = 0;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ ASSERT_GT(finalCachedBufferIndex, 1);
+ // the final cached buffer has the same slot value as the oldest buffer
+ EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot);
+ // the oldest buffer is no longer in the cache because it was evicted
+ EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+
+ // the 1st buffer should be found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot);
+ // since the 1st buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+ // the 2nd buffer should be still found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot);
+ // since the 2nd buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenCached_returnsSameSlotAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
+}
+
+TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenSlotsFull_returnsIndependentSlot) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = -1;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ // expect to have cached at least a few buffers before evicting
+ ASSERT_GT(finalCachedBufferIndex, 1);
+
+ sp<GraphicBuffer> overrideBuffer =
+ sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ HwcSlotAndBuffer overrideSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(overrideBuffer);
+ // expect us to have a slot number
+ EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX);
+ // expect this to be the first time we cached the buffer
+ EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr);
+
+ // expect the slot number to not equal any other slot number, even after the slots have been
+ // exhausted, indicating that the override buffer slot is independent from the slots for
+ // non-override buffers
+ for (int i = 0; i < finalCachedBufferIndex; ++i) {
+ EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot);
+ }
+ // the override buffer is independently uncached from the oldest cached buffer
+ // expect to find the override buffer still in the override buffer slot
+ EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot);
+ // expect that the first buffer was not evicted from the cache when the override buffer was
+ // cached
+ EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot);
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index d933b94..b0b1a02 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -56,6 +56,7 @@
MOCK_METHOD3(setBuffer,
Error(uint32_t, const android::sp<android::GraphicBuffer>&,
const android::sp<android::Fence>&));
+ MOCK_METHOD2(setBufferSlotsToClear, Error(const std::vector<uint32_t>&, uint32_t));
MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 758b346..33caa7a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -139,8 +139,8 @@
MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
(const, override));
MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
- MOCK_METHOD(status_t, getOverlaySupport,
- (aidl::android::hardware::graphics::composer3::OverlayProperties*));
+ MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
+ getOverlaySupport, (), (const, override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e220541..c555b39 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -34,7 +34,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
- MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 0f7dce8..0edc226 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -42,6 +42,8 @@
using testing::_;
using testing::InSequence;
+using testing::Mock;
+using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::StrictMock;
@@ -82,13 +84,13 @@
struct OutputLayerTest : public testing::Test {
struct OutputLayer final : public impl::OutputLayer {
- OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
+ OutputLayer(const compositionengine::Output& output, compositionengine::LayerFE& layerFE)
: mOutput(output), mLayerFE(layerFE) {}
~OutputLayer() override = default;
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
- compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+ compositionengine::LayerFE& getLayerFE() const override { return mLayerFE; }
const impl::OutputLayerCompositionState& getState() const override { return mState; }
impl::OutputLayerCompositionState& editState() override { return mState; }
@@ -96,21 +98,22 @@
void dumpState(std::string& out) const override { mState.dump(out); }
const compositionengine::Output& mOutput;
- sp<compositionengine::LayerFE> mLayerFE;
+ compositionengine::LayerFE& mLayerFE;
impl::OutputLayerCompositionState mState;
};
OutputLayerTest() {
- EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
- EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
+ ON_CALL(mLayerFE, getDebugName()).WillByDefault(Return("Test LayerFE"));
+ ON_CALL(mOutput, getName()).WillByDefault(ReturnRef(kOutputName));
- EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
- EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+ ON_CALL(mLayerFE, getCompositionState()).WillByDefault(Return(&mLayerFEState));
+ ON_CALL(mOutput, getState()).WillByDefault(ReturnRef(mOutputState));
}
- compositionengine::mock::Output mOutput;
- sp<StrictMock<compositionengine::mock::LayerFE>> mLayerFE =
- sp<StrictMock<compositionengine::mock::LayerFE>>::make();
+ NiceMock<compositionengine::mock::Output> mOutput;
+ sp<NiceMock<compositionengine::mock::LayerFE>> mLayerFE_ =
+ sp<NiceMock<compositionengine::mock::LayerFE>>::make();
+ NiceMock<compositionengine::mock::LayerFE>& mLayerFE = *mLayerFE_;
OutputLayer mOutputLayer{mOutput, mLayerFE};
LayerFECompositionState mLayerFEState;
@@ -530,7 +533,7 @@
struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer {
OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
- sp<compositionengine::LayerFE> layerFE)
+ compositionengine::LayerFE& layerFE)
: mOutput(output), mLayerFE(layerFE) {}
// Mock everything called by updateCompositionState to simplify testing it.
MOCK_CONST_METHOD1(calculateOutputSourceCrop, FloatRect(uint32_t));
@@ -539,7 +542,7 @@
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
- compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+ compositionengine::LayerFE& getLayerFE() const override { return mLayerFE; }
const impl::OutputLayerCompositionState& getState() const override { return mState; }
impl::OutputLayerCompositionState& editState() override { return mState; }
@@ -547,7 +550,7 @@
MOCK_CONST_METHOD1(dumpState, void(std::string&));
const compositionengine::Output& mOutput;
- sp<compositionengine::LayerFE> mLayerFE;
+ compositionengine::LayerFE& mLayerFE;
impl::OutputLayerCompositionState mState;
};
@@ -588,7 +591,7 @@
};
TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
- EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+ EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
}
@@ -774,7 +777,7 @@
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
- static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
+ static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::kOverrideBufferSlot;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
static constexpr float kWhitePointNits = 200.f;
@@ -823,7 +826,6 @@
mLayerFEState.hdrMetadata = kHdrMetadata;
mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
mLayerFEState.buffer = kBuffer;
- mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
@@ -834,7 +836,6 @@
EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
.WillRepeatedly(Return(kSupportedPerFrameMetadata));
}
-
// Some tests may need to simulate unsupported HWC calls
enum class SimulateUnsupported { None, ColorTransform };
@@ -953,7 +954,10 @@
const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
reinterpret_cast<native_handle_t*>(1031);
-const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer =
+ sp<GraphicBuffer>::make(1, 2, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer =
sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888,
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
@@ -969,7 +973,7 @@
{4, 5, 6, 7}};
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
- EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+ EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -994,7 +998,7 @@
expectPerFrameCommonCalls();
expectNoSetCompositionTypeCall();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+ EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1019,7 +1023,6 @@
mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
// Setting the composition type should happen before setting the color. We
// check this in this test only by setting up an testing::InSeqeuence
@@ -1039,8 +1042,6 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Composition::SIDEBAND);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
@@ -1052,8 +1053,6 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::CURSOR);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
@@ -1065,8 +1064,6 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
@@ -1080,8 +1077,6 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
@@ -1120,8 +1115,6 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
@@ -1134,8 +1127,6 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
@@ -1150,7 +1141,6 @@
kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1166,7 +1156,6 @@
kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1182,7 +1171,6 @@
kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1198,7 +1186,6 @@
kOverrideSurfaceDamage, kOverrideLayerBrightness);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1213,7 +1200,6 @@
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1230,7 +1216,6 @@
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::DEVICE);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1248,22 +1233,20 @@
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Composition::CLIENT);
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
- auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
- OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
+ auto peekThroughLayerFE = sp<NiceMock<compositionengine::mock::LayerFE>>::make();
+ OutputLayer peekThroughLayer{mOutput, *peekThroughLayerFE};
mOutputLayer.mState.overrideInfo.peekThroughLayer = &peekThroughLayer;
expectGeometryCommonCalls(kDisplayFrame, kSourceCrop, kBufferTransform,
Hwc2::IComposerClient::BlendMode::PREMULTIPLIED);
expectPerFrameCommonCalls();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1281,7 +1264,6 @@
TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) {
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ true, /*isPeekingThrough*/
@@ -1292,7 +1274,7 @@
TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) {
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
+ EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1304,7 +1286,7 @@
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
expectSetCompositionTypeCall(Composition::DEVICE);
mLayerFEState.compositionType = Composition::DEVICE;
@@ -1323,7 +1305,6 @@
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
kSurfaceDamage, kLayerBrightness, blockingRegion);
expectSetHdrMetadataAndBufferCalls();
- EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
expectSetCompositionTypeCall(Composition::DISPLAY_DECORATION);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1332,6 +1313,76 @@
}
/*
+ * OutputLayer::uncacheBuffers
+ */
+struct OutputLayerUncacheBufferTest : public OutputLayerTest {
+ static const sp<GraphicBuffer> kBuffer1;
+ static const sp<GraphicBuffer> kBuffer2;
+ static const sp<Fence> kFence;
+
+ OutputLayerUncacheBufferTest() {
+ auto& outputLayerState = mOutputLayer.editState();
+ outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer_);
+
+ mLayerFEState.compositionType = Composition::DEVICE;
+ mLayerFEState.acquireFence = kFence;
+
+ ON_CALL(mOutput, getDisplayColorProfile()).WillByDefault(Return(&mDisplayColorProfile));
+ }
+
+ std::shared_ptr<HWC2::mock::Layer> mHwcLayer_{std::make_shared<NiceMock<HWC2::mock::Layer>>()};
+ HWC2::mock::Layer& mHwcLayer = *mHwcLayer_;
+ NiceMock<mock::DisplayColorProfile> mDisplayColorProfile;
+};
+
+const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer1 =
+ sp<GraphicBuffer>::make(1, 2, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer2 =
+ sp<GraphicBuffer>::make(2, 3, PIXEL_FORMAT_RGBA_8888,
+ AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+const sp<Fence> OutputLayerUncacheBufferTest::kFence = sp<Fence>::make();
+
+TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) {
+ // Buffer1 is stored in slot 0
+ mLayerFEState.buffer = kBuffer1;
+ EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer1, kFence));
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+ // Buffer2 is stored in slot 1
+ mLayerFEState.buffer = kBuffer2;
+ EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer2, kFence));
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+ // buffer slots are cleared in HWC
+ std::vector<uint32_t> slotsToClear = {0, 1};
+ EXPECT_CALL(mHwcLayer,
+ setBufferSlotsToClear(/*slotsToClear*/ slotsToClear, /*activeBufferSlot*/ 1));
+ mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId()});
+ Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+ // rather than allocating a new slot, the active buffer slot (slot 1) is reused first to free
+ // the memory as soon as possible
+ mLayerFEState.buffer = kBuffer1;
+ EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+ // rather than allocating a new slot, slot 0 is reused
+ mLayerFEState.buffer = kBuffer2;
+ EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer2, kFence));
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+/*
* OutputLayer::writeCursorPositionToHWC()
*/
@@ -1359,7 +1410,7 @@
const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4};
TEST_F(OutputLayerWriteCursorPositionToHWCTest, doesNothingIfNoFECompositionState) {
- EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+ EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
mOutputLayer.writeCursorPositionToHWC();
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 2109987..aaf0f06 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -799,20 +799,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -839,20 +836,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -878,20 +872,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -917,8 +908,7 @@
InSequence seq;
EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
@@ -926,9 +916,7 @@
EXPECT_CALL(*layer0.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer0.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
-
+ EXPECT_CALL(*layer0.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
// After calling planComposition (which clears overrideInfo), this test sets
// layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
@@ -938,18 +926,15 @@
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/
true));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer0);
injectOutputLayer(layer1);
@@ -1050,10 +1035,10 @@
MOCK_METHOD1(
chooseCompositionStrategyAsync,
std::future<bool>(std::optional<android::HWComposer::DeviceRequestedChanges>*));
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_METHOD0(resetCompositionStrategy, void());
};
@@ -1087,9 +1072,9 @@
EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_))
.WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
Return(ByMove(p.get_future()))));
- EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::SUCCESS);
EXPECT_FALSE(result.bufferAvailable());
}
@@ -1112,7 +1097,7 @@
.WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
Return(ByMove(p.get_future()))));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
EXPECT_FALSE(result.bufferAvailable());
}
@@ -1140,9 +1125,9 @@
EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_)).WillOnce([&] {
return p.get_future();
});
- EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
EXPECT_TRUE(result.bufferAvailable());
}
@@ -1172,9 +1157,9 @@
EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_)).WillOnce([&] {
return p.get_future();
});
- EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
EXPECT_TRUE(result.bufferAvailable());
}
@@ -1192,14 +1177,49 @@
compositionengine::LayerFESet&));
};
+ OutputPrepareTest() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+
+ mRefreshArgs.layers.push_back(mLayer1.layerFE);
+ mRefreshArgs.layers.push_back(mLayer2.layerFE);
+ }
+
+ struct Layer {
+ StrictMock<mock::OutputLayer> outputLayer;
+ sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
+ };
+
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
LayerFESet mGeomSnapshots;
+ Layer mLayer1;
+ Layer mLayer2;
};
-TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+TEST_F(OutputPrepareTest, callsUncacheBuffersOnEachOutputLayerAndThenRebuildsLayerStacks) {
InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {1, 3, 5};
+
EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+
+ mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputPrepareTest, skipsUncacheBuffersIfEmptyAndThenRebuildsLayerStacks) {
+ InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {};
+
+ EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(_)).Times(0);
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(_)).Times(0);
mOutput.prepare(mRefreshArgs, mGeomSnapshots);
}
@@ -2000,11 +2020,9 @@
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD2(finishFrame,
- void(const compositionengine::CompositionRefreshArgs&,
- GpuCompositionResult&&));
+ MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
@@ -2026,7 +2044,7 @@
EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
- EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+ EXPECT_CALL(mOutput, finishFrame(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
@@ -2044,9 +2062,9 @@
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
- EXPECT_CALL(mOutput, prepareFrameAsync(Ref(args)));
+ EXPECT_CALL(mOutput, prepareFrameAsync());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
- EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+ EXPECT_CALL(mOutput, finishFrame(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
@@ -2945,10 +2963,10 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD(Region, getDirtyRegion, (), (const));
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD0(updateProtectedContentState, void());
@@ -3012,7 +3030,7 @@
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, prepareFrame());
@@ -3028,10 +3046,10 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
@@ -3047,24 +3065,23 @@
StrictMock<OutputPartialMock> mOutput;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- CompositionRefreshArgs mRefreshArgs;
};
TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
mOutput.mState.isEnabled = true;
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _));
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
@@ -3073,12 +3090,12 @@
InSequence seq;
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _))
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, predictionSucceeded) {
@@ -3088,7 +3105,7 @@
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
@@ -3104,11 +3121,11 @@
2);
EXPECT_CALL(mOutput,
- composeSurfaces(RegionEq(Region::INVALID_REGION), _, result.buffer,
+ composeSurfaces(RegionEq(Region::INVALID_REGION), result.buffer,
Eq(ByRef(result.fence))))
.WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
/*
@@ -3299,7 +3316,8 @@
// mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD3(generateClientCompositionRequests,
- std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<LayerFE*>&));
+ std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace,
+ std::vector<LayerFE*>&));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -3345,8 +3363,8 @@
getInstance()->mOutput.dequeueRenderBuffer(&fence, &externalTexture);
if (success) {
getInstance()->mReadyFence =
- getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs,
- externalTexture, fence);
+ getInstance()->mOutput.composeSurfaces(kDebugRegion, externalTexture,
+ fence);
}
return nextState<FenceCheckState>();
}
@@ -4020,7 +4038,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -4042,7 +4060,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
@@ -4055,7 +4073,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
@@ -4068,7 +4086,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -4101,61 +4119,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
- : public OutputComposeSurfacesTest_SetsExpensiveRendering {
- OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
- mLayer.layerFEState.backgroundBlurRadius = 10;
- mLayer.layerFEState.isOpaque = false;
- mOutput.editState().isEnabled = true;
-
- EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(mLayer.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
- .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
- .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&mLayer.outputLayer));
- }
-
- NonInjectedLayer mLayer;
- compositionengine::CompositionRefreshArgs mRefreshArgs;
-};
-
-TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
- mRefreshArgs.blursAreExpensive = true;
- mOutput.updateCompositionState(mRefreshArgs);
- mOutput.planComposition();
- mOutput.writeCompositionState(mRefreshArgs);
-
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
- mRefreshArgs.blursAreExpensive = false;
- mOutput.updateCompositionState(mRefreshArgs);
- mOutput.planComposition();
- mOutput.writeCompositionState(mRefreshArgs);
-
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
/*
@@ -4166,7 +4130,7 @@
struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
std::vector<LayerFE::LayerSettings> generateClientCompositionRequestsHelper(
- bool supportsProtectedContent, ui::Dataspace dataspace) {
+ bool supportsProtectedContent, ui::Dataspace dataspace) {
std::vector<LayerFE*> ignore;
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
dataspace, ignore);
@@ -4258,8 +4222,9 @@
EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -4268,8 +4233,9 @@
mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -4281,8 +4247,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
@@ -4312,8 +4279,9 @@
prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly)))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
@@ -4341,8 +4309,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
}
@@ -4364,8 +4333,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
}
@@ -4427,8 +4397,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
// The second layer is expected to be rendered as alpha=0 black with no blending
@@ -4492,7 +4463,7 @@
static_cast<void>(
mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace));
+ kDisplayDataspace));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4714,8 +4685,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
- static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
- kDisplayDataspace));
+ static_cast<void>(
+ mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
+ kDisplayDataspace));
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) {
@@ -4728,14 +4700,12 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
layer2.layerFEState.isOpaque = true;
@@ -4764,20 +4734,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
layer2.layerFEState.isOpaque = false;
@@ -4807,20 +4774,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);
@@ -4917,8 +4881,8 @@
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(rightLayer.mLayerSettings)));
constexpr bool supportsProtectedContent = true;
- auto requests =
- mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent, kOutputDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent,
+ kOutputDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
@@ -4956,8 +4920,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mShadowSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mShadowSettings, requests[0]);
@@ -4993,8 +4958,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
index ac25fe0..d07cdf5 100644
--- a/services/surfaceflinger/Display/DisplayModeRequest.h
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -18,19 +18,19 @@
#include <ftl/non_null.h>
-#include "DisplayHardware/DisplayMode.h"
+#include <scheduler/FrameRateMode.h>
namespace android::display {
struct DisplayModeRequest {
- ftl::NonNull<DisplayModePtr> modePtr;
+ scheduler::FrameRateMode mode;
// Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
bool emitEvent = false;
};
inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) {
- return lhs.modePtr == rhs.modePtr && lhs.emitEvent == rhs.emitEvent;
+ return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent;
}
} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9868c8e..96ae77f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -41,6 +41,7 @@
#include "Display/DisplaySnapshot.h"
#include "DisplayDevice.h"
+#include "FrontEnd/DisplayInfo.h"
#include "Layer.h"
#include "RefreshRateOverlay.h"
#include "SurfaceFlinger.h"
@@ -67,6 +68,7 @@
mCompositionDisplay{args.compositionDisplay},
mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
+ mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary),
mRefreshRateSelector(std::move(args.refreshRateSelector)) {
@@ -103,7 +105,9 @@
mCompositionDisplay->getRenderSurface()->initialize();
- if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
+ if (const auto powerModeOpt = args.initialPowerMode) {
+ setPowerMode(*powerModeOpt);
+ }
// initialize the display orientation transform.
setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -131,7 +135,7 @@
}
}
-auto DisplayDevice::getInputInfo() const -> InputInfo {
+auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo {
gui::DisplayInfo info;
info.displayId = getLayerStack().id;
@@ -160,7 +164,9 @@
return {.info = info,
.transform = displayTransform,
.receivesInput = receivesInput(),
- .isSecure = isSecure()};
+ .isSecure = isSecure(),
+ .isPrimary = isPrimary(),
+ .rotationFlags = ui::Transform::toRotationFlags(mOrientation)};
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
@@ -175,8 +181,7 @@
mPowerMode = mode;
- getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
- *mPowerMode != hal::PowerMode::OFF);
+ getCompositionDisplay()->setCompositionEnabled(isPoweredOn());
}
void DisplayDevice::enableLayerCaching(bool enable) {
@@ -191,35 +196,32 @@
return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
}
-void DisplayDevice::setActiveMode(DisplayModeId modeId, const display::DisplaySnapshot& snapshot) {
- const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
- [](const DisplayModePtr& mode) { return mode->getFps(); });
+void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
+ ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue());
+ ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
- LOG_ALWAYS_FATAL_IF(!fpsOpt, "Unknown mode");
- const Fps fps = *fpsOpt;
-
- ATRACE_INT(mActiveModeFPSTrace.c_str(), fps.getIntValue());
-
- mRefreshRateSelector->setActiveModeId(modeId);
+ mRefreshRateSelector->setActiveMode(modeId, renderFps);
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(fps);
+ mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
}
}
status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
const hal::VsyncPeriodChangeConstraints& constraints,
hal::VsyncPeriodChangeTimeline* outTimeline) {
- if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) {
+ if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) {
ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
- info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null",
+ info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str()
+ : "null",
to_string(getId()).c_str());
return BAD_VALUE;
}
mUpcomingActiveMode = info;
- ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
- return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
- constraints, outTimeline);
+ ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue());
+ return mHwComposer.setActiveModeWithConstraints(getPhysicalId(),
+ info.modeOpt->modePtr->getHwcId(), constraints,
+ outTimeline);
}
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -234,7 +236,7 @@
return vsyncPeriod;
}
- return refreshRateSelector().getActiveModePtr()->getVsyncPeriod();
+ return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod();
}
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
@@ -408,26 +410,35 @@
capabilities.getDesiredMinLuminance());
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
}
+ ftl::Flags<RefreshRateOverlay::Features> features;
+ if (showSpinner) {
+ features |= RefreshRateOverlay::Features::Spinner;
+ }
+
+ if (showRenderRate) {
+ features |= RefreshRateOverlay::Features::RenderRate;
+ }
+
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- mRefreshRateOverlay->changeRefreshRate(getActiveMode().getFps());
+ mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
}
bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
bool timerExpired) {
if (mRefreshRateSelector && mRefreshRateOverlay) {
- const auto newRefreshRate =
+ const auto newMode =
mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
- if (newRefreshRate) {
- mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+ if (newMode) {
+ mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
return true;
}
}
@@ -441,13 +452,15 @@
}
}
-bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) {
+auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
+ -> DesiredActiveModeAction {
ATRACE_CALL();
- LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
- LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch");
+ LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
+ LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(),
+ "DisplayId mismatch");
- ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str());
+ ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str());
std::scoped_lock lock(mActiveModeLock);
if (mDesiredActiveModeChanged) {
@@ -455,18 +468,25 @@
const auto prevConfig = mDesiredActiveMode.event;
mDesiredActiveMode = info;
mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
- return false;
+ return DesiredActiveModeAction::None;
}
+ const auto& desiredMode = *info.modeOpt->modePtr;
+
// Check if we are already at the desired mode
- if (refreshRateSelector().getActiveModePtr()->getId() == info.mode->getId()) {
- return false;
+ if (!force && refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
+ if (refreshRateSelector().getActiveMode() == info.modeOpt) {
+ return DesiredActiveModeAction::None;
+ }
+
+ setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps);
+ return DesiredActiveModeAction::InitiateRenderRateSwitch;
}
// Initiate a mode change.
mDesiredActiveModeChanged = true;
mDesiredActiveMode = info;
- return true;
+ return DesiredActiveModeAction::InitiateDisplayModeSwitch;
}
std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1602a71..d757673 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -45,11 +45,11 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
+#include "FrontEnd/DisplayInfo.h"
#include "Scheduler/RefreshRateSelector.h"
#include "ThreadContext.h"
#include "TracedOrdinal.h"
#include "Utils/Dumper.h"
-
namespace android {
class Fence;
@@ -167,14 +167,7 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
- struct InputInfo {
- gui::DisplayInfo info;
- ui::Transform transform;
- bool receivesInput;
- bool isSecure;
- };
-
- InputInfo getInputInfo() const;
+ surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const;
/* ------------------------------------------------------------------------
* Display power mode management.
@@ -197,33 +190,39 @@
using Event = scheduler::DisplayModeEvent;
ActiveModeInfo() = default;
- ActiveModeInfo(DisplayModePtr mode, Event event) : mode(std::move(mode)), event(event) {}
+ ActiveModeInfo(scheduler::FrameRateMode mode, Event event)
+ : modeOpt(std::move(mode)), event(event) {}
explicit ActiveModeInfo(display::DisplayModeRequest&& request)
- : ActiveModeInfo(std::move(request.modePtr).take(),
+ : ActiveModeInfo(std::move(request.mode),
request.emitEvent ? Event::Changed : Event::None) {}
- DisplayModePtr mode;
+ ftl::Optional<scheduler::FrameRateMode> modeOpt;
Event event = Event::None;
bool operator!=(const ActiveModeInfo& other) const {
- return mode != other.mode || event != other.event;
+ return modeOpt != other.modeOpt || event != other.event;
}
};
- bool setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+ enum class DesiredActiveModeAction {
+ None,
+ InitiateDisplayModeSwitch,
+ InitiateRenderRateSwitch
+ };
+ DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&, bool force = false)
+ EXCLUDES(mActiveModeLock);
std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
return mUpcomingActiveMode;
}
- const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext) {
+ scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
return mRefreshRateSelector->getActiveMode();
}
- // Precondition: DisplaySnapshot must contain a mode with DisplayModeId.
- void setActiveMode(DisplayModeId, const display::DisplaySnapshot&) REQUIRES(kMainThreadContext);
+ void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps);
status_t initiateModeChange(const ActiveModeInfo&,
const hal::VsyncPeriodChangeConstraints& constraints,
@@ -238,7 +237,8 @@
}
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
+ void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate)
+ REQUIRES(kMainThreadContext);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
void animateRefreshRateOverlay();
@@ -261,6 +261,7 @@
std::string mDisplayName;
std::string mActiveModeFPSTrace;
std::string mActiveModeFPSHwcTrace;
+ std::string mRenderFrameRateFPSTrace;
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index eff5130..e372b72 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
+#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -232,6 +233,25 @@
addReader(translate<Display>(kSingleReaderKey));
+ // If unable to read interface version, then become backwards compatible.
+ int32_t version = 1;
+ const auto status = mAidlComposerClient->getInterfaceVersion(&version);
+ if (!status.isOk()) {
+ ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
+ status.getDescription().c_str());
+ }
+ if (version == 1) {
+ mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_SW_READ_OFTEN |
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ "AidlComposer");
+ if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
+ LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
+ return;
+ }
+ }
+
ALOGI("Loaded AIDL composer3 HAL service");
}
@@ -535,15 +555,19 @@
return static_cast<Error>(status.getServiceSpecificError());
}
- *outTypes = translate<Hdr>(capabilities.types);
+ *outTypes = capabilities.types;
*outMaxLuminance = capabilities.maxLuminance;
*outMaxAverageLuminance = capabilities.maxAverageLuminance;
*outMinLuminance = capabilities.minLuminance;
return Error::NONE;
}
-Error AidlComposer::getOverlaySupport(AidlOverlayProperties* /*outProperties*/) {
- // TODO(b/242588489): implement details
+Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) {
+ const auto status = mAidlComposerClient->getOverlaySupport(outProperties);
+ if (!status.isOk()) {
+ ALOGE("getOverlaySupport failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
return Error::NONE;
}
@@ -574,13 +598,15 @@
}
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
- ATRACE_NAME("HwcPresentDisplay");
+ const auto displayId = translate<int64_t>(display);
+ ATRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
+
Error error = Error::NONE;
mMutex.lock_shared();
auto writer = getWriter(display);
auto reader = getReader(display);
if (writer && reader) {
- writer->get().presentDisplay(translate<int64_t>(display));
+ writer->get().presentDisplay(displayId);
error = execute(display);
} else {
error = Error::BAD_DISPLAY;
@@ -591,7 +617,7 @@
return error;
}
- auto fence = reader->get().takePresentFence(translate<int64_t>(display));
+ auto fence = reader->get().takePresentFence(displayId);
mMutex.unlock_shared();
// take ownership
*outPresentFence = fence.get();
@@ -703,8 +729,9 @@
Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
uint32_t* outNumTypes, uint32_t* outNumRequests) {
- ATRACE_NAME("HwcValidateDisplay");
const auto displayId = translate<int64_t>(display);
+ ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);
+
Error error = Error::NONE;
mMutex.lock_shared();
auto writer = getWriter(display);
@@ -730,8 +757,9 @@
Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
uint32_t* outNumTypes, uint32_t* outNumRequests,
int* outPresentFence, uint32_t* state) {
- ATRACE_NAME("HwcPresentOrValidateDisplay");
const auto displayId = translate<int64_t>(display);
+ ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);
+
Error error = Error::NONE;
mMutex.lock_shared();
auto writer = getWriter(display);
@@ -805,6 +833,49 @@
return error;
}
+Error AidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer,
+ const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) {
+ if (slotsToClear.empty()) {
+ return Error::NONE;
+ }
+
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto writer = getWriter(display)) {
+ // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
+ // buffer, using the slot that needs to cleared... tricky.
+ if (mClearSlotBuffer == nullptr) {
+ writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
+ translate<int64_t>(layer), slotsToClear);
+ } else {
+ for (uint32_t slot : slotsToClear) {
+ // Don't clear the active buffer slot because we need to restore the active buffer
+ // after clearing the requested buffer slots with a placeholder buffer.
+ if (slot != activeBufferSlot) {
+ writer->get().setLayerBufferWithNewCommand(translate<int64_t>(display),
+ translate<int64_t>(layer), slot,
+ mClearSlotBuffer->handle,
+ /*fence*/ -1);
+ }
+ }
+ // Since we clear buffers by setting them to a placeholder buffer, we want to make
+ // sure that the last setLayerBuffer command is sent with the currently active
+ // buffer, not the placeholder buffer, so that there is no perceptual change when
+ // buffers are discarded.
+ writer->get().setLayerBufferWithNewCommand(translate<int64_t>(display),
+ translate<int64_t>(layer), activeBufferSlot,
+ // The active buffer is still cached in
+ // its slot and doesn't need a fence.
+ /*buffer*/ nullptr, /*fence*/ -1);
+ }
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
+}
+
Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) {
Error error = Error::NONE;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d84efe7..9a7ade7 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -104,7 +104,7 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
Error getOverlaySupport(OverlayProperties* outProperties) override;
@@ -143,6 +143,9 @@
/* see setClientTarget for the purpose of slot */
Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
const sp<GraphicBuffer>& buffer, int acquireFence) override;
+ Error setLayerBufferSlotsToClear(Display display, Layer layer,
+ const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) override;
Error setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) override;
Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
@@ -280,6 +283,9 @@
// threading annotations.
ftl::SharedMutex mMutex;
+ // Buffer slots for layers are cleared by setting the slot buffer to this buffer.
+ sp<GraphicBuffer> mClearSlotBuffer;
+
// Aidl interface
using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer;
using AidlIComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index a9bf282..1c2b8b5 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -66,7 +66,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -84,6 +83,7 @@
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using aidl::android::hardware::graphics::common::Hdr;
class Composer {
public:
@@ -140,7 +140,7 @@
virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0;
- virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) = 0;
@@ -179,6 +179,9 @@
/* see setClientTarget for the purpose of slot */
virtual Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
const sp<GraphicBuffer>& buffer, int acquireFence) = 0;
+ virtual Error setLayerBufferSlotsToClear(Display display, Layer layer,
+ const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) = 0;
virtual Error setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) = 0;
virtual Error setLayerBlendMode(Display display, Layer layer,
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index eb14933..ce602a8 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -72,6 +72,10 @@
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
@@ -88,31 +92,16 @@
}
status_t FramebufferSurface::advanceFrame() {
- uint32_t slot = 0;
- sp<GraphicBuffer> buf;
- sp<Fence> acquireFence(Fence::NO_FENCE);
- Dataspace dataspace = Dataspace::UNKNOWN;
- status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
- mDataSpace = dataspace;
- if (result != NO_ERROR) {
- ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
- strerror(-result), result);
- }
- return result;
-}
-
-status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
- sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
- Dataspace& outDataspace) {
Mutex::Autolock lock(mMutex);
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
+ mDataspace = Dataspace::UNKNOWN;
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
+ mDataspace = Dataspace::UNKNOWN;
return err;
}
@@ -133,13 +122,18 @@
mCurrentBufferSlot = item.mSlot;
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
mCurrentFence = item.mFence;
+ mDataspace = static_cast<Dataspace>(item.mDataSpace);
- outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
- outDataspace = static_cast<Dataspace>(item.mDataSpace);
- status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
+ // assume HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (mCurrentBuffer->getId() != mHwcBufferIds[mCurrentBufferSlot]) {
+ mHwcBufferIds[mCurrentBufferSlot] = mCurrentBuffer->getId();
+ hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
+ status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer,
+ mDataspace);
if (result != NO_ERROR) {
- ALOGE("error posting framebuffer: %d", result);
+ ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result);
return result;
}
@@ -190,7 +184,7 @@
limitedSize.width = maxSize.height * aspectRatio;
wasLimited = true;
}
- ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+ ALOGI_IF(wasLimited, "Framebuffer size has been limited to [%dx%d] from [%dx%d]",
limitedSize.width, limitedSize.height, size.width, size.height);
return limitedSize;
}
@@ -198,9 +192,9 @@
void FramebufferSurface::dumpAsString(String8& result) const {
Mutex::Autolock lock(mMutex);
result.append(" FramebufferSurface\n");
- result.appendFormat(" mDataSpace=%s (%d)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- mDataSpace);
+ result.appendFormat(" mDataspace=%s (%d)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataspace)).c_str(),
+ mDataspace);
ConsumerBase::dumpLocked(result, " ");
}
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index d41a856..0b863da 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <ui/DisplayId.h>
#include <ui/Size.h>
@@ -69,12 +69,6 @@
virtual void dumpLocked(String8& result, const char* prefix) const;
- // nextBuffer waits for and then latches the next buffer from the
- // BufferQueue and releases the previously latched buffer to the
- // BufferQueue. The new buffer is returned in the 'buffer' argument.
- status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
- sp<Fence>& outFence, ui::Dataspace& outDataspace);
-
const PhysicalDisplayId mDisplayId;
// Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
@@ -91,7 +85,7 @@
// compositing. Otherwise it will display the dataspace of the buffer
// use for compositing which can change as wide-color content is
// on/off.
- ui::Dataspace mDataSpace;
+ ui::Dataspace mDataspace;
// mCurrentBuffer is the current buffer or nullptr to indicate that there is
// no current buffer.
@@ -103,7 +97,9 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
+ // Buffers that HWC has seen before, indexed by slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index a9337d8..6738f00 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -320,23 +320,23 @@
float maxLuminance = -1.0f;
float maxAverageLuminance = -1.0f;
float minLuminance = -1.0f;
- std::vector<Hwc2::Hdr> types;
- auto intError = mComposer.getHdrCapabilities(mId, &types,
- &maxLuminance, &maxAverageLuminance, &minLuminance);
+ std::vector<Hwc2::Hdr> hdrTypes;
+ auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance,
+ &maxAverageLuminance, &minLuminance);
auto error = static_cast<HWC2::Error>(intError);
if (error != Error::NONE) {
return error;
}
- *outCapabilities = HdrCapabilities(std::move(types),
- maxLuminance, maxAverageLuminance, minLuminance);
+ *outCapabilities =
+ HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance);
return Error::NONE;
}
-Error Display::getOverlaySupport(OverlayProperties* /*outProperties*/) const {
- // TODO(b/242588489): implement details
- return Error::NONE;
+Error Display::getOverlaySupport(OverlayProperties* outProperties) const {
+ auto intError = mComposer.getOverlaySupport(outProperties);
+ return static_cast<Error>(intError);
}
Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
@@ -717,6 +717,16 @@
return static_cast<Error>(intError);
}
+Error Layer::setBufferSlotsToClear(const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+ auto intError = mComposer.setLayerBufferSlotsToClear(mDisplay->getId(), mId, slotsToClear,
+ activeBufferSlot);
+ return static_cast<Error>(intError);
+}
+
Error Layer::setSurfaceDamage(const Region& damage)
{
if (CC_UNLIKELY(!mDisplay)) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 4971d19..c1c7070 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -310,6 +310,8 @@
[[nodiscard]] virtual hal::Error setBuffer(uint32_t slot,
const android::sp<android::GraphicBuffer>& buffer,
const android::sp<android::Fence>& acquireFence) = 0;
+ [[nodiscard]] virtual hal::Error setBufferSlotsToClear(
+ const std::vector<uint32_t>& slotsToClear, uint32_t activeBufferSlot) = 0;
[[nodiscard]] virtual hal::Error setSurfaceDamage(const android::Region& damage) = 0;
[[nodiscard]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
@@ -360,6 +362,8 @@
hal::Error setCursorPosition(int32_t x, int32_t y) override;
hal::Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
const android::sp<android::Fence>& acquireFence) override;
+ hal::Error setBufferSlotsToClear(const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) override;
hal::Error setSurfaceDamage(const android::Region& damage) override;
hal::Error setBlendMode(hal::BlendMode mode) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 5f11cb8..10fde2a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -96,6 +96,7 @@
void HWComposer::setCallback(HWC2::ComposerCallback& callback) {
loadCapabilities();
loadLayerMetadataSupport();
+ loadOverlayProperties();
if (mRegisteredCallback) {
ALOGW("Callback already registered. Ignored extra registration attempt.");
@@ -652,9 +653,9 @@
return NO_ERROR;
}
-status_t HWComposer::getOverlaySupport(
- aidl::android::hardware::graphics::composer3::OverlayProperties* /*outProperties*/) {
- return NO_ERROR;
+const aidl::android::hardware::graphics::composer3::OverlayProperties&
+HWComposer::getOverlaySupport() const {
+ return mOverlayProperties;
}
int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
@@ -974,6 +975,10 @@
}
}
+void HWComposer::loadOverlayProperties() {
+ mComposer->getOverlaySupport(&mOverlayProperties);
+}
+
status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId,
std::chrono::milliseconds timeout) {
ATRACE_CALL();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 8235b88..78d4a68 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -179,8 +179,8 @@
// Fetches the HDR capabilities of the given display
virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
- virtual status_t getOverlaySupport(
- aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties) = 0;
+ virtual const aidl::android::hardware::graphics::composer3::OverlayProperties&
+ getOverlaySupport() const = 0;
virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
@@ -366,8 +366,8 @@
// Fetches the HDR capabilities of the given display
status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
- status_t getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
- outProperties) override;
+ const aidl::android::hardware::graphics::composer3::OverlayProperties& getOverlaySupport()
+ const override;
int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
@@ -489,11 +489,13 @@
void loadCapabilities();
void loadLayerMetadataSupport();
+ void loadOverlayProperties();
std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities;
+ aidl::android::hardware::graphics::composer3::OverlayProperties mOverlayProperties;
std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
bool mRegisteredCallback = false;
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 8238828..537d545 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
@@ -39,7 +40,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Error;
@@ -69,6 +69,7 @@
using PowerMode = IComposerClient::PowerMode;
using Vsync = IComposerClient::Vsync;
using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+using Hdr = aidl::android::hardware::graphics::common::Hdr;
} // namespace hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index f8522e2..c9e1e79 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -186,9 +186,22 @@
return out;
}
+sp<GraphicBuffer> allocateClearSlotBuffer() {
+ sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_SW_READ_OFTEN |
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ "HidlComposer");
+ if (!buffer || buffer->initCheck() != ::android::OK) {
+ return nullptr;
+ }
+ return std::move(buffer);
+}
+
} // anonymous namespace
-HidlComposer::HidlComposer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
+HidlComposer::HidlComposer(const std::string& serviceName)
+ : mClearSlotBuffer(allocateClearSlotBuffer()), mWriter(kWriterInitialSize) {
mComposer = V2_1::IComposer::getService(serviceName);
if (mComposer == nullptr) {
@@ -230,6 +243,11 @@
if (mClient == nullptr) {
LOG_ALWAYS_FATAL("failed to create composer client");
}
+
+ if (!mClearSlotBuffer) {
+ LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
+ return;
+ }
}
bool HidlComposer::isSupported(OptionalFeature feature) const {
@@ -496,13 +514,13 @@
"OptionalFeature::KernelIdleTimer is not supported on HIDL");
}
-Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) {
Error error = kDefaultError;
if (mClient_2_3) {
mClient_2_3->getHdrCapabilities_2_3(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -510,15 +528,15 @@
if (error != Error::NONE) {
return;
}
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
- *outTypes = tmpTypes;
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
*outMinLuminance = tmpMinLuminance;
});
} else {
mClient->getHdrCapabilities(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -526,11 +544,7 @@
if (error != Error::NONE) {
return;
}
-
- outTypes->clear();
- for (auto type : tmpTypes) {
- outTypes->push_back(static_cast<Hdr>(type));
- }
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
@@ -698,6 +712,32 @@
return Error::NONE;
}
+Error HidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer,
+ const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) {
+ if (slotsToClear.empty()) {
+ return Error::NONE;
+ }
+ // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
+ // buffer, using the slot that needs to cleared... tricky.
+ for (uint32_t slot : slotsToClear) {
+ // Don't clear the active buffer slot because we need to restore the active buffer after
+ // setting the requested buffer slots with a placeholder buffer.
+ if (slot != activeBufferSlot) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerBuffer(slot, mClearSlotBuffer->handle, /*fence*/ -1);
+ }
+ }
+ // Since we clear buffers by setting them to a placeholder buffer, we want to make sure that the
+ // last setLayerBuffer command is sent with the currently active buffer, not the placeholder
+ // buffer, so that there is no perceptual change.
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerBuffer(activeBufferSlot, /*buffer*/ nullptr, /*fence*/ -1);
+ return Error::NONE;
+}
+
Error HidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) {
mWriter.selectDisplay(display);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 48b720c..921add5 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -56,7 +56,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -209,7 +208,7 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
outProperties) override;
@@ -249,6 +248,9 @@
/* see setClientTarget for the purpose of slot */
Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
const sp<GraphicBuffer>& buffer, int acquireFence) override;
+ Error setLayerBufferSlotsToClear(Display display, Layer layer,
+ const std::vector<uint32_t>& slotsToClear,
+ uint32_t activeBufferSlot) override;
Error setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) override;
Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
@@ -363,6 +365,9 @@
sp<V2_3::IComposerClient> mClient_2_3;
sp<IComposerClient> mClient_2_4;
+ // Buffer slots for layers are cleared by setting the slot buffer to this buffer.
+ sp<GraphicBuffer> mClearSlotBuffer;
+
// 64KiB minus a small space for metadata such as read/write pointers
static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
// Max number of buffers that may be cached for a given layer
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index cb2c8c5..f05223c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -57,6 +57,7 @@
using android::hardware::power::IPower;
using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
+using android::hardware::power::SessionHint;
using android::hardware::power::WorkDuration;
PowerAdvisor::~PowerAdvisor() = default;
@@ -140,7 +141,7 @@
}
}
-void PowerAdvisor::notifyDisplayUpdateImminent() {
+void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
// Only start sending this notification once the system has booted so we don't introduce an
// early-boot dependency on Power HAL
if (!mBootFinished.load()) {
@@ -154,7 +155,7 @@
return;
}
- if (!halWrapper->notifyDisplayUpdateImminent()) {
+ if (!halWrapper->notifyDisplayUpdateImminentAndCpuReset()) {
// The HAL has become unavailable; attempt to reconnect later
mReconnectPowerHal = true;
return;
@@ -599,7 +600,7 @@
return ret.isOk();
}
- bool notifyDisplayUpdateImminent() override {
+ bool notifyDisplayUpdateImminentAndCpuReset() override {
// Power HAL 1.x doesn't have a notification for this
ALOGV("HIDL notifyUpdateImminent received but can't send");
return true;
@@ -675,8 +676,12 @@
return ret.isOk();
}
-bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
- ALOGV("AIDL notifyDisplayUpdateImminent");
+bool AidlPowerHalWrapper::notifyDisplayUpdateImminentAndCpuReset() {
+ ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
+ if (isPowerHintSessionRunning()) {
+ mPowerHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+ }
+
if (!mHasDisplayUpdateImminent) {
ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
return true;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 1c9d123..d45e7cb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -48,7 +48,7 @@
virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
- virtual void notifyDisplayUpdateImminent() = 0;
+ virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
// Checks both if it supports and if it's enabled
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
@@ -106,7 +106,7 @@
virtual ~HalWrapper() = default;
virtual bool setExpensiveRendering(bool enabled) = 0;
- virtual bool notifyDisplayUpdateImminent() = 0;
+ virtual bool notifyDisplayUpdateImminentAndCpuReset() = 0;
virtual bool supportsPowerHintSession() = 0;
virtual bool isPowerHintSessionRunning() = 0;
virtual void restartPowerHintSession() = 0;
@@ -126,7 +126,7 @@
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
- void notifyDisplayUpdateImminent() override;
+ void notifyDisplayUpdateImminentAndCpuReset() override;
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
bool isPowerHintSessionRunning() override;
@@ -289,7 +289,7 @@
static std::unique_ptr<HalWrapper> connect();
bool setExpensiveRendering(bool enabled) override;
- bool notifyDisplayUpdateImminent() override;
+ bool notifyDisplayUpdateImminentAndCpuReset() override;
bool supportsPowerHintSession() override;
bool isPowerHintSessionRunning() override;
void restartPowerHintSession() override;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 3803a78..d62075e 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -103,6 +103,10 @@
sink->setAsyncMode(true);
IGraphicBufferProducer::QueueBufferOutput output;
mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
VirtualDisplaySurface::~VirtualDisplaySurface() {
@@ -197,9 +201,9 @@
return NO_MEMORY;
}
- sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
- mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
- sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
+ sp<GraphicBuffer> const& fbBuffer =
+ mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
+ sp<GraphicBuffer> const& outBuffer = mProducerBuffers[mOutputProducerSlot];
VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
@@ -211,12 +215,14 @@
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
-
+ // assume that HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) {
+ mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId();
+ hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN);
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index e21095a..be06e2b 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,7 +20,7 @@
#include <string>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
#include <ui/DisplayId.h>
@@ -164,6 +164,10 @@
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
+ // Buffers that HWC has seen before, indexed by HWC slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
+
//
// Inter-frame state
//
@@ -260,8 +264,6 @@
bool mMustRecompose = false;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
-
bool mForceHwcCopy;
};
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
new file mode 100644
index 0000000..0c7b24a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/DisplayInfo.h>
+
+namespace android::surfaceflinger::frontend {
+
+// Display information needed to populate input and calculate layer geometry.
+struct DisplayInfo {
+ gui::DisplayInfo info;
+ ui::Transform transform;
+ bool receivesInput;
+ bool isSecure;
+ // TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
+ bool isPrimary;
+ ui::Transform::RotationFlags rotationFlags;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
new file mode 100644
index 0000000..db4e8af
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerHierarchy"
+
+#include "LayerHierarchy.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs,
+ const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
+ auto lhsLayer = lhs.first->getLayer();
+ auto rhsLayer = rhs.first->getLayer();
+ if (lhsLayer->layerStack != rhsLayer->layerStack) {
+ return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+ }
+ if (lhsLayer->z != rhsLayer->z) {
+ return lhsLayer->z < rhsLayer->z;
+ }
+ return lhsLayer->id < rhsLayer->id;
+};
+
+void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec,
+ std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) {
+ auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare);
+ vec.insert(it, std::move(value));
+}
+} // namespace
+
+LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {}
+
+LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) {
+ mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer;
+ mChildren = hierarchy.mChildren;
+}
+
+void LayerHierarchy::traverse(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ if (mLayer) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ }
+ if (traversalPath.hasRelZLoop()) {
+ LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
+ }
+ for (auto& [child, childVariant] : mChildren) {
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverse(visitor, traversalPath);
+ }
+}
+
+void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ bool traverseThisLayer = (mLayer != nullptr);
+ for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
+ auto& [child, childVariant] = *it;
+ if (traverseThisLayer && child->getLayer()->z >= 0) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ traverseThisLayer = false;
+ }
+ if (childVariant == LayerHierarchy::Variant::Detached) {
+ continue;
+ }
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverseInZOrder(visitor, traversalPath);
+ }
+
+ if (traverseThisLayer) {
+ visitor(*this, traversalPath);
+ }
+}
+
+void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) {
+ insertSorted(mChildren, {child, variant});
+}
+
+void LayerHierarchy::removeChild(LayerHierarchy* child) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [child](const std::pair<LayerHierarchy*, Variant>& x) {
+ return x.first == child;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ }
+ mChildren.erase(it);
+}
+
+void LayerHierarchy::sortChildrenByZOrder() {
+ std::sort(mChildren.begin(), mChildren.end(), layerZCompare);
+}
+
+void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
+ return child.first == hierarchy;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ } else {
+ it->second = variant;
+ }
+}
+
+const RequestedLayerState* LayerHierarchy::getLayer() const {
+ return mLayer;
+}
+
+std::string LayerHierarchy::getDebugStringShort() const {
+ std::string debug = "LayerHierarchy{";
+ debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " ";
+ if (mChildren.empty()) {
+ debug += "no children";
+ } else {
+ debug += std::to_string(mChildren.size()) + " children";
+ }
+ return debug + "}";
+}
+
+std::string LayerHierarchy::getDebugString(const char* prefix) const {
+ std::string debug = prefix + getDebugStringShort();
+ for (auto& [child, childVariant] : mChildren) {
+ std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant);
+ debug += "\n" + child->getDebugString(childPrefix.c_str());
+ }
+ return debug;
+}
+
+bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
+ outInvalidRelativeRoot = UNASSIGNED_LAYER_ID;
+ traverse([&outInvalidRelativeRoot](const LayerHierarchy&,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ if (traversalPath.hasRelZLoop()) {
+ outInvalidRelativeRoot = traversalPath.invalidRelativeRootId;
+ return false;
+ }
+ return true;
+ });
+ return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
+}
+
+LayerHierarchyBuilder::LayerHierarchyBuilder(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+ mHierarchies.reserve(layers.size());
+ mLayerIdToHierarchy.reserve(layers.size());
+ for (auto& layer : layers) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ for (const auto& layer : layers) {
+ onLayerAdded(layer.get());
+ }
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+}
+
+void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ LayerHierarchy::Variant type = layer->hasValidRelativeParent()
+ ? LayerHierarchy::Variant::Detached
+ : LayerHierarchy::Variant::Attached;
+
+ LayerHierarchy* parent;
+
+ if (layer->parentId != UNASSIGNED_LAYER_ID) {
+ parent = getHierarchyFromId(layer->parentId);
+ } else if (layer->canBeRoot) {
+ parent = &mRoot;
+ } else {
+ parent = &mOffscreenRoot;
+ }
+ parent->addChild(hierarchy, type);
+ hierarchy->mParent = parent;
+}
+
+void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) {
+ hierarchy->mParent->removeChild(hierarchy);
+ hierarchy->mParent = nullptr;
+}
+
+void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) {
+ return;
+ }
+
+ if (layer->relativeParentId != UNASSIGNED_LAYER_ID) {
+ hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId);
+ } else {
+ hierarchy->mRelativeParent = &mOffscreenRoot;
+ }
+ hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative);
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached);
+}
+
+void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) {
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->removeChild(hierarchy);
+ }
+ hierarchy->mRelativeParent = nullptr;
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ attachToRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ attachHierarchyToRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ detachFromRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ detachHierarchyFromRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ attachToParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+ hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
+ if (!hierarchy) {
+ // Layer was never part of the hierarchy if it was created and destroyed in the same
+ // transaction.
+ return;
+ }
+ // detach from parent
+ detachFromRelativeParent(hierarchy);
+ detachFromParent(hierarchy);
+
+ // detach children
+ for (auto& [child, variant] : hierarchy->mChildren) {
+ if (variant == LayerHierarchy::Variant::Attached ||
+ variant == LayerHierarchy::Variant::Detached) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mParent = &mOffscreenRoot;
+ } else if (variant == LayerHierarchy::Variant::Relative) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mRelativeParent = &mOffscreenRoot;
+ }
+ }
+
+ swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) {
+ return layerHierarchy.get() == hierarchy;
+ });
+ mLayerIdToHierarchy.erase(layer->id);
+}
+
+void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ auto it = hierarchy->mChildren.begin();
+ while (it != hierarchy->mChildren.end()) {
+ if (it->second == LayerHierarchy::Variant::Mirror) {
+ hierarchy->mChildren.erase(it);
+ break;
+ }
+ it++;
+ }
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::update(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
+ // rebuild map
+ for (auto& layer : layers) {
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ }
+
+ for (auto& layer : layers) {
+ if (layer->changes.get() == 0) {
+ continue;
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ onLayerAdded(layer.get());
+ continue;
+ }
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ if (layer->changes.test(RequestedLayerState::Changes::Parent)) {
+ detachFromParent(hierarchy);
+ attachToParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) {
+ detachFromRelativeParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Z)) {
+ hierarchy->mParent->sortChildrenByZOrder();
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->sortChildrenByZOrder();
+ }
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Mirror)) {
+ updateMirrorLayer(layer.get());
+ }
+ }
+
+ for (auto& layer : destroyedLayers) {
+ onLayerDestroyed(layer.get());
+ }
+ // When moving from onscreen to offscreen and vice versa, we need to attach and detach
+ // from our relative parents. This walks down both trees to do so. We can optimize this
+ // further by tracking onscreen, offscreen state in LayerHierarchy.
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+ attachHierarchyToRelativeParent(&mRoot);
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
+ return mRoot;
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const {
+ return mOffscreenRoot;
+}
+
+std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const {
+ if (depth > 10) return "too deep, loop?";
+ if (layerId == UNASSIGNED_LAYER_ID) return "";
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return "not found";
+
+ LayerHierarchy* hierarchy = it->second;
+ if (!hierarchy->mLayer) return "none";
+
+ std::string debug =
+ "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name;
+ if (hierarchy->mRelativeParent) {
+ debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort();
+ }
+ if (hierarchy->mParent) {
+ debug += " Parent:" + hierarchy->mParent->getDebugStringShort();
+ }
+ return debug;
+}
+
+LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId,
+ bool childrenOnly) const {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return {nullptr};
+
+ LayerHierarchy hierarchy(*it->second, childrenOnly);
+ return hierarchy;
+}
+
+LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) {
+ if (crashOnFailure) {
+ LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
+ }
+ return nullptr;
+ };
+
+ return it->second;
+}
+
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID =
+ {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
+
+std::string LayerHierarchy::TraversalPath::toString() const {
+ std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+
+ if (!mirrorRootIds.empty()) {
+ debugString += ", .mirrorRootIds=";
+ for (auto rootId : mirrorRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (!relativeRootIds.empty()) {
+ debugString += ", .relativeRootIds=";
+ for (auto rootId : relativeRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (hasRelZLoop()) {
+ debugString += ", hasRelZLoop=true invalidRelativeRootId=";
+ debugString += std::to_string(invalidRelativeRootId) + ",";
+ }
+
+ debugString += "}";
+ return debugString;
+}
+
+// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
+// of scope the TraversalPath is reset to its original state.
+LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
+ uint32_t layerId,
+ LayerHierarchy::Variant variant)
+ : mTraversalPath(traversalPath),
+ mParentId(traversalPath.id),
+ mParentVariant(traversalPath.variant) {
+ // Update the traversal id with the child layer id and variant. Parent id and variant are
+ // stored to reset the id upon destruction.
+ traversalPath.id = layerId;
+ traversalPath.variant = variant;
+ if (variant == LayerHierarchy::Variant::Mirror) {
+ traversalPath.mirrorRootIds.emplace_back(layerId);
+ }
+ if (variant == LayerHierarchy::Variant::Relative) {
+ if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
+ layerId) != traversalPath.relativeRootIds.end()) {
+ traversalPath.invalidRelativeRootId = layerId;
+ }
+ traversalPath.relativeRootIds.emplace_back(layerId);
+ }
+}
+LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
+ // Reset the traversal id to its original parent state using the state that was saved in
+ // the constructor.
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+ mTraversalPath.mirrorRootIds.pop_back();
+ }
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
+ mTraversalPath.relativeRootIds.pop_back();
+ }
+ if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
+ mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ }
+ mTraversalPath.id = mParentId;
+ mTraversalPath.variant = mParentVariant;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
new file mode 100644
index 0000000..f83a859
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "ftl/small_vector.h"
+
+namespace android::surfaceflinger::frontend {
+class LayerHierarchyBuilder;
+
+// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
+// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
+// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
+//
+// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
+// traversing the hierarchy, a new concept called Variant can be used to understand the
+// relationship of the layer to its parent. The following variants are possible:
+// Attached - child of the parent
+// Detached - child of the parent but currently relative parented to another layer
+// Relative - relative child of the parent
+// Mirror - mirrored from another layer
+//
+// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
+// cloning the layer requested state. The mirrored hierarchy and its corresponding
+// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
+// states.
+class LayerHierarchy {
+public:
+ enum Variant {
+ Attached,
+ Detached,
+ Relative,
+ Mirror,
+ };
+ // Represents a unique path to a node.
+ struct TraversalPath {
+ uint32_t id;
+ LayerHierarchy::Variant variant;
+ // Mirrored layers can have a different geometry than their parents so we need to track
+ // the mirror roots in the traversal.
+ ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+ // Relative layers can be visited twice, once by their parent and then once again by
+ // their relative parent. We keep track of the roots here to detect any loops in the
+ // hierarchy. If a relative root already exists in the list while building the
+ // TraversalPath, it means that somewhere in the hierarchy two layers are relatively
+ // parented to each other.
+ ftl::SmallVector<uint32_t, 5> relativeRootIds;
+ // First duplicate relative root id found. If this is a valid layer id that means we are
+ // in a loop.
+ uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; }
+ bool isRelative() { return !relativeRootIds.empty(); }
+
+ bool operator==(const TraversalPath& other) const {
+ return id == other.id && mirrorRootIds == other.mirrorRootIds;
+ }
+ std::string toString() const;
+
+ static TraversalPath ROOT_TRAVERSAL_ID;
+ };
+
+ // Helper class to add nodes to an existing traversal id and removes the
+ // node when it goes out of scope.
+ class ScopedAddToTraversalPath {
+ public:
+ ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
+ LayerHierarchy::Variant variantArg);
+ ~ScopedAddToTraversalPath();
+
+ private:
+ TraversalPath& mTraversalPath;
+ uint32_t mParentId;
+ LayerHierarchy::Variant mParentVariant;
+ };
+ LayerHierarchy(RequestedLayerState* layer);
+
+ // Visitor function that provides the hierarchy node and a traversal id which uniquely
+ // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
+ // Return false to stop traversing down the hierarchy.
+ typedef std::function<bool(const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath)>
+ Visitor;
+
+ // Traverse the hierarchy and visit all child variants.
+ void traverse(const Visitor& visitor) const {
+ traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ // Traverse the hierarchy in z-order, skipping children that have relative parents.
+ void traverseInZOrder(const Visitor& visitor) const {
+ traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ const RequestedLayerState* getLayer() const;
+ std::string getDebugString(const char* prefix = "") const;
+ std::string getDebugStringShort() const;
+ // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
+ // will contain the first relative root that was visited twice in a traversal.
+ bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
+ std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
+
+private:
+ friend LayerHierarchyBuilder;
+ LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
+ void addChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void removeChild(LayerHierarchy*);
+ void sortChildrenByZOrder();
+ void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+
+ const RequestedLayerState* mLayer;
+ LayerHierarchy* mParent = nullptr;
+ LayerHierarchy* mRelativeParent = nullptr;
+};
+
+// Given a list of RequestedLayerState, this class will build a root hierarchy and an
+// offscreen hierarchy. The builder also has an update method which can update an existing
+// hierarchy from a list of RequestedLayerState and associated change flags.
+class LayerHierarchyBuilder {
+public:
+ LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+ void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+ LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
+ const LayerHierarchy& getHierarchy() const;
+ const LayerHierarchy& getOffscreenHierarchy() const;
+ std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+
+private:
+ void onLayerAdded(RequestedLayerState* layer);
+ void attachToParent(LayerHierarchy*);
+ void detachFromParent(LayerHierarchy*);
+ void attachToRelativeParent(LayerHierarchy*);
+ void detachFromRelativeParent(LayerHierarchy*);
+ void attachHierarchyToRelativeParent(LayerHierarchy*);
+ void detachHierarchyFromRelativeParent(LayerHierarchy*);
+
+ void onLayerDestroyed(RequestedLayerState* layer);
+ void updateMirrorLayer(RequestedLayerState* layer);
+ LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+ std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
+ std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
+ LayerHierarchy mRoot{nullptr};
+ LayerHierarchy mOffscreenRoot{nullptr};
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
new file mode 100644
index 0000000..fdf60b3
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "LayerLifecycleManager"
+
+#include "LayerLifecycleManager.h"
+#include "Layer.h" // temporarily needed for LayerHandle
+#include "LayerHandle.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+using namespace ftl::flag_operators;
+
+void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) {
+ if (newLayers.empty()) {
+ return;
+ }
+
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+ for (auto& newLayer : newLayers) {
+ RequestedLayerState& layer = *newLayer.get();
+ auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
+ if (!inserted) {
+ LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
+ it->second.owner.getDebugString().c_str());
+ }
+
+ layer.parentId = linkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
+
+ mLayers.emplace_back(std::move(newLayer));
+ }
+}
+
+void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles) {
+ std::vector<uint32_t> layersToBeDestroyed;
+ for (const auto& layerId : destroyedHandles) {
+ auto it = mIdToLayer.find(layerId);
+ if (it == mIdToLayer.end()) {
+ LOG_ALWAYS_FATAL("%s Layerid not found %d", __func__, layerId);
+ continue;
+ }
+ RequestedLayerState& layer = it->second.owner;
+ layer.handleAlive = false;
+ if (!layer.canBeDestroyed()) {
+ continue;
+ }
+ layer.changes |= RequestedLayerState::Changes::Destroyed;
+ layersToBeDestroyed.emplace_back(layerId);
+ }
+
+ if (layersToBeDestroyed.empty()) {
+ return;
+ }
+
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+ for (size_t i = 0; i < layersToBeDestroyed.size(); i++) {
+ uint32_t layerId = layersToBeDestroyed[i];
+ auto it = mIdToLayer.find(layerId);
+ if (it == mIdToLayer.end()) {
+ LOG_ALWAYS_FATAL("%s Layer with id %d not found", __func__, layerId);
+ continue;
+ }
+
+ RequestedLayerState& layer = it->second.owner;
+
+ layer.parentId = unlinkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
+
+ auto& references = it->second.references;
+ for (uint32_t linkedLayerId : references) {
+ RequestedLayerState* linkedLayer = getLayerFromId(linkedLayerId);
+ if (!linkedLayer) {
+ LOG_ALWAYS_FATAL("%s Layerid reference %d not found for %d", __func__,
+ linkedLayerId, layer.id);
+ continue;
+ };
+ if (linkedLayer->parentId == layer.id) {
+ linkedLayer->parentId = UNASSIGNED_LAYER_ID;
+ if (linkedLayer->canBeDestroyed()) {
+ linkedLayer->changes |= RequestedLayerState::Changes::Destroyed;
+ layersToBeDestroyed.emplace_back(linkedLayer->id);
+ }
+ }
+ if (linkedLayer->relativeParentId == layer.id) {
+ linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID;
+ }
+ if (linkedLayer->mirrorId == layer.id) {
+ linkedLayer->mirrorId = UNASSIGNED_LAYER_ID;
+ }
+ if (linkedLayer->touchCropId == layer.id) {
+ linkedLayer->touchCropId = UNASSIGNED_LAYER_ID;
+ }
+ }
+ mIdToLayer.erase(it);
+ }
+
+ auto it = mLayers.begin();
+ while (it != mLayers.end()) {
+ RequestedLayerState* layer = it->get();
+ if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
+ ALOGV("%s destroyed layer %s", __func__, layer->getDebugStringShort().c_str());
+ std::iter_swap(it, mLayers.end() - 1);
+ mDestroyedLayers.emplace_back(std::move(mLayers.back()));
+ if (it == mLayers.end() - 1) {
+ it = mLayers.erase(mLayers.end() - 1);
+ } else {
+ mLayers.erase(mLayers.end() - 1);
+ }
+ } else {
+ it++;
+ }
+ }
+}
+
+void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions) {
+ for (const auto& transaction : transactions) {
+ for (const auto& resolvedComposerState : transaction.states) {
+ const auto& clientState = resolvedComposerState.state;
+ uint32_t layerId = LayerHandle::getLayerId(clientState.surface);
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get());
+ continue;
+ }
+
+ RequestedLayerState* layer = getLayerFromId(layerId);
+ if (layer == nullptr) {
+ LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__,
+ clientState.surface.get(), layerId);
+ continue;
+ }
+
+ if (!layer->handleAlive) {
+ LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of "
+ "order LayerLifecycleManager updates",
+ __func__, clientState.surface.get(), layerId);
+ continue;
+ }
+
+ uint32_t oldParentId = layer->parentId;
+ uint32_t oldRelativeParentId = layer->relativeParentId;
+ uint32_t oldTouchCropId = layer->touchCropId;
+ layer->merge(resolvedComposerState);
+
+ if (layer->what & layer_state_t::eBackgroundColorChanged) {
+ if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColorAlpha != 0) {
+ LayerCreationArgs backgroundLayerArgs{nullptr,
+ nullptr,
+ layer->name + "BackgroundColorLayer",
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ {}};
+ std::vector<std::unique_ptr<RequestedLayerState>> newLayers;
+ newLayers.emplace_back(
+ std::make_unique<RequestedLayerState>(backgroundLayerArgs));
+ RequestedLayerState* backgroundLayer = newLayers.back().get();
+ backgroundLayer->handleAlive = false;
+ backgroundLayer->parentId = layer->id;
+ backgroundLayer->z = std::numeric_limits<int32_t>::min();
+ backgroundLayer->color.rgb = layer->color.rgb;
+ backgroundLayer->color.a = layer->bgColorAlpha;
+ backgroundLayer->dataspace = layer->bgColorDataspace;
+
+ layer->bgColorLayerId = backgroundLayer->id;
+ addLayers({std::move(newLayers)});
+ } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID &&
+ layer->bgColorAlpha == 0) {
+ RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
+ bgColorLayer->parentId = UNASSIGNED_LAYER_ID;
+ onHandlesDestroyed({layer->bgColorLayerId});
+ } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) {
+ RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
+ bgColorLayer->color.rgb = layer->color.rgb;
+ bgColorLayer->color.a = layer->bgColorAlpha;
+ bgColorLayer->dataspace = layer->bgColorDataspace;
+ mGlobalChanges |= RequestedLayerState::Changes::Content;
+ }
+ }
+
+ if (oldParentId != layer->parentId) {
+ unlinkLayer(oldParentId, layer->id);
+ layer->parentId = linkLayer(layer->parentId, layer->id);
+ }
+ if (oldRelativeParentId != layer->relativeParentId) {
+ unlinkLayer(oldRelativeParentId, layer->id);
+ layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id);
+ }
+ if (oldTouchCropId != layer->touchCropId) {
+ unlinkLayer(oldTouchCropId, layer->id);
+ layer->touchCropId = linkLayer(layer->touchCropId, layer->id);
+ }
+
+ mGlobalChanges |= layer->changes &
+ (RequestedLayerState::Changes::Hierarchy |
+ RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::Content);
+ }
+ }
+}
+
+void LayerLifecycleManager::commitChanges() {
+ for (auto& layer : mLayers) {
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ for (auto listener : mListeners) {
+ listener->onLayerAdded(*layer);
+ }
+ }
+ layer->what = 0;
+ layer->changes.clear();
+ }
+
+ for (auto& destroyedLayer : mDestroyedLayers) {
+ if (destroyedLayer->changes.test(RequestedLayerState::Changes::Created)) {
+ for (auto listener : mListeners) {
+ listener->onLayerAdded(*destroyedLayer);
+ }
+ }
+
+ for (auto listener : mListeners) {
+ listener->onLayerDestroyed(*destroyedLayer);
+ }
+ }
+ mDestroyedLayers.clear();
+ mGlobalChanges.clear();
+}
+
+void LayerLifecycleManager::addLifecycleListener(std::shared_ptr<ILifecycleListener> listener) {
+ mListeners.emplace_back(std::move(listener));
+}
+
+void LayerLifecycleManager::removeLifecycleListener(std::shared_ptr<ILifecycleListener> listener) {
+ swapErase(mListeners, listener);
+}
+
+const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getLayers() const {
+ return mLayers;
+}
+
+const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getDestroyedLayers()
+ const {
+ return mDestroyedLayers;
+}
+
+const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const {
+ return mGlobalChanges;
+}
+
+RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) {
+ if (id == UNASSIGNED_LAYER_ID) {
+ return nullptr;
+ }
+ auto it = mIdToLayer.find(id);
+ if (it == mIdToLayer.end()) {
+ return nullptr;
+ }
+ return &it->second.owner;
+}
+
+std::vector<uint32_t>* LayerLifecycleManager::getLinkedLayersFromId(uint32_t id) {
+ if (id == UNASSIGNED_LAYER_ID) {
+ return nullptr;
+ }
+ auto it = mIdToLayer.find(id);
+ if (it == mIdToLayer.end()) {
+ return nullptr;
+ }
+ return &it->second.references;
+}
+
+uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ return UNASSIGNED_LAYER_ID;
+ }
+
+ std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+ if (!linkedLayers) {
+ ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId,
+ layerToLink);
+ return UNASSIGNED_LAYER_ID;
+ }
+ linkedLayers->emplace_back(layerToLink);
+ return layerId;
+}
+
+uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) {
+ std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+ if (!linkedLayers) {
+ return UNASSIGNED_LAYER_ID;
+ }
+ swapErase(*linkedLayers, linkedLayer);
+ return UNASSIGNED_LAYER_ID;
+}
+
+std::string LayerLifecycleManager::References::getDebugString() const {
+ std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:";
+ std::for_each(references.begin(), references.end(),
+ [&debugInfo = debugInfo](const uint32_t& reference) mutable {
+ debugInfo += std::to_string(reference) + ",";
+ });
+ return debugInfo;
+}
+
+void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) {
+ auto it = mIdToLayer.find(relativeRootId);
+ if (it == mIdToLayer.end()) {
+ return;
+ }
+ RequestedLayerState& layer = it->second.owner;
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.changes |=
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent;
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
new file mode 100644
index 0000000..63a7afc
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "RequestedLayerState.h"
+#include "TransactionState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Owns a collection of RequestedLayerStates and manages their lifecycle
+// and state changes.
+//
+// RequestedLayerStates are tracked and destroyed if they have no parent and
+// no handle left to keep them alive. The handle does not keep a reference to
+// the RequestedLayerState but a layer id associated with the RequestedLayerState.
+// If the handle is destroyed and the RequestedLayerState does not have a parent,
+// the LayerLifecycleManager destroys the RequestedLayerState.
+//
+// Threading: This class is not thread safe, it requires external synchronization.
+//
+// Typical usage: Input states (new layers, transactions, destroyed layer handles)
+// are collected in the background passed into the LayerLifecycleManager to update
+// layer lifecycle and layer state at start of composition.
+class LayerLifecycleManager {
+public:
+ // External state changes should be updated in the following order:
+ void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>);
+ void applyTransactions(const std::vector<TransactionState>&);
+ void onHandlesDestroyed(const std::vector<uint32_t>&);
+
+ // Detaches the layer from its relative parent to prevent a loop in the
+ // layer hierarchy. This overrides the RequestedLayerState and leaves
+ // the system in an invalid state. This is always a client error that
+ // needs to be fixed but overriding the state allows us to fail gracefully.
+ void fixRelativeZLoop(uint32_t relativeRootId);
+
+ // Destroys RequestedLayerStates that are marked to be destroyed. Invokes all
+ // ILifecycleListener callbacks and clears any change flags from previous state
+ // updates. This function should be called outside the hot path since it's not
+ // critical to composition.
+ void commitChanges();
+
+ class ILifecycleListener {
+ public:
+ virtual ~ILifecycleListener() = default;
+ // Called on commitChanges when a layer is added. The callback includes
+ // the layer state the client was created with as well as any state updates
+ // until changes were committed.
+ virtual void onLayerAdded(const RequestedLayerState&) = 0;
+ // Called on commitChanges when a layer has been destroyed. The callback
+ // includes the final state before the layer was destroyed.
+ virtual void onLayerDestroyed(const RequestedLayerState&) = 0;
+ };
+ void addLifecycleListener(std::shared_ptr<ILifecycleListener>);
+ void removeLifecycleListener(std::shared_ptr<ILifecycleListener>);
+ const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const;
+ const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const;
+ const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
+
+private:
+ friend class LayerLifecycleManagerTest;
+ friend class HierarchyBuilderTest;
+ friend class android::SurfaceFlinger;
+
+ RequestedLayerState* getLayerFromId(uint32_t);
+ std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
+ uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
+ uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
+
+ struct References {
+ // Lifetime tied to mLayers
+ RequestedLayerState& owner;
+ std::vector<uint32_t> references;
+ std::string getDebugString() const;
+ };
+ std::unordered_map<uint32_t, References> mIdToLayer;
+ // Listeners are invoked once changes are committed.
+ std::vector<std::shared_ptr<ILifecycleListener>> mListeners;
+
+ // Aggregation of changes since last commit.
+ ftl::Flags<RequestedLayerState::Changes> mGlobalChanges;
+ std::vector<std::unique_ptr<RequestedLayerState>> mLayers;
+ // Layers pending destruction. Layers will be destroyed once changes are committed.
+ std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
new file mode 100644
index 0000000..054382c
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrontEnd/LayerCreationArgs.h"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "RequestedLayerState"
+
+#include <private/android_filesystem_config.h>
+#include <sys/types.h>
+
+#include "Layer.h"
+#include "LayerHandle.h"
+#include "RequestedLayerState.h"
+
+namespace android::surfaceflinger::frontend {
+using ftl::Flags;
+using namespace ftl::flag_operators;
+
+namespace {
+uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
+ if (!surfaceControl) {
+ return UNASSIGNED_LAYER_ID;
+ }
+
+ return LayerHandle::getLayerId(surfaceControl->getHandle());
+}
+
+std::string layerIdToString(uint32_t layerId) {
+ return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
+}
+
+} // namespace
+
+RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
+ : id(args.sequence),
+ name(args.name),
+ canBeRoot(args.addToRoot),
+ layerCreationFlags(args.flags),
+ textureName(args.textureName),
+ ownerUid(args.ownerUid),
+ ownerPid(args.ownerPid) {
+ layerId = static_cast<int32_t>(args.sequence);
+ changes |= RequestedLayerState::Changes::Created;
+ metadata.merge(args.metadata);
+ changes |= RequestedLayerState::Changes::Metadata;
+ handleAlive = true;
+ parentId = LayerHandle::getLayerId(args.parentHandle.promote());
+ mirrorId = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
+ if (mirrorId != UNASSIGNED_LAYER_ID) {
+ changes |= RequestedLayerState::Changes::Mirror;
+ }
+
+ flags = 0;
+ if (args.flags & ISurfaceComposerClient::eHidden) flags |= layer_state_t::eLayerHidden;
+ if (args.flags & ISurfaceComposerClient::eOpaque) flags |= layer_state_t::eLayerOpaque;
+ if (args.flags & ISurfaceComposerClient::eSecure) flags |= layer_state_t::eLayerSecure;
+ if (args.flags & ISurfaceComposerClient::eSkipScreenshot) {
+ flags |= layer_state_t::eLayerSkipScreenshot;
+ }
+ premultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+ potentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+ protectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
+ if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+ // Set an invalid color so there is no color fill.
+ // (b/259981098) use an explicit flag instead of relying on invalid values.
+ color.r = -1.0_hf;
+ color.g = -1.0_hf;
+ color.b = -1.0_hf;
+ } else {
+ color.rgb = {0.0_hf, 0.0_hf, 0.0_hf};
+ }
+ color.a = 1.0f;
+
+ crop.makeInvalid();
+ z = 0;
+ layerStack = ui::DEFAULT_LAYER_STACK;
+ transformToDisplayInverse = false;
+ dataspace = ui::Dataspace::UNKNOWN;
+ dataspaceRequested = false;
+ hdrMetadata.validTypes = 0;
+ surfaceDamageRegion = Region::INVALID_REGION;
+ cornerRadius = 0.0f;
+ backgroundBlurRadius = 0;
+ api = -1;
+ hasColorTransform = false;
+ bufferTransform = 0;
+ requestedTransform.reset();
+ bufferData = std::make_shared<BufferData>();
+ bufferData->frameNumber = 0;
+ bufferData->acquireFence = sp<Fence>::make(-1);
+ acquireFenceTime = std::make_shared<FenceTime>(bufferData->acquireFence);
+ colorSpaceAgnostic = false;
+ frameRateSelectionPriority = Layer::PRIORITY_UNSET;
+ shadowRadius = 0.f;
+ fixedTransformHint = ui::Transform::ROT_INVALID;
+ destinationFrame.makeInvalid();
+ isTrustedOverlay = false;
+ dropInputMode = gui::DropInputMode::NONE;
+ dimmingEnabled = true;
+ defaultFrameRateCompatibility =
+ static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
+ dataspace = ui::Dataspace::V0_SRGB;
+}
+
+void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
+ bool oldFlags = flags;
+ Rect oldBufferSize = getBufferSize();
+ const layer_state_t& clientState = resolvedComposerState.state;
+
+ uint64_t clientChanges = what | layer_state_t::diff(clientState);
+ layer_state_t::merge(clientState);
+ what = clientChanges;
+
+ if (clientState.what & layer_state_t::eFlagsChanged) {
+ if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
+ changes |= RequestedLayerState::Changes::Visibility;
+ }
+ if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) {
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+ }
+ if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize()) {
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+ if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
+ changes |= RequestedLayerState::Changes::Hierarchy;
+ if (clientChanges & layer_state_t::CONTENT_CHANGES)
+ changes |= RequestedLayerState::Changes::Content;
+ if (clientChanges & layer_state_t::GEOMETRY_CHANGES)
+ changes |= RequestedLayerState::Changes::Geometry;
+
+ if (clientState.what & layer_state_t::eColorTransformChanged) {
+ static const mat4 identityMatrix = mat4();
+ hasColorTransform = colorTransform != identityMatrix;
+ }
+ if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
+ changes |= RequestedLayerState::Changes::Z;
+ }
+ if (clientState.what & layer_state_t::eReparent) {
+ changes |= RequestedLayerState::Changes::Parent;
+ parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
+ parentSurfaceControlForChild = nullptr;
+ // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
+ // but thats the existing logic and until we make this behavior more explicit, we need
+ // to maintain this logic.
+ canBeRoot = false;
+ }
+ if (clientState.what & layer_state_t::eRelativeLayerChanged) {
+ changes |= RequestedLayerState::Changes::RelativeParent;
+ relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl);
+ isRelativeOf = true;
+ relativeLayerSurfaceControl = nullptr;
+ }
+ if ((clientState.what & layer_state_t::eLayerChanged ||
+ (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) &&
+ isRelativeOf) {
+ // clear out relz data
+ relativeParentId = UNASSIGNED_LAYER_ID;
+ isRelativeOf = false;
+ changes |= RequestedLayerState::Changes::RelativeParent;
+ }
+ if (clientState.what & layer_state_t::eReparent && parentId == relativeParentId) {
+ // provide a hint that we are are now a direct child and not a relative child.
+ changes |= RequestedLayerState::Changes::RelativeParent;
+ }
+ if (clientState.what & layer_state_t::eInputInfoChanged) {
+ wp<IBinder>& touchableRegionCropHandle =
+ windowInfoHandle->editInfo()->touchableRegionCropHandle;
+ touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote());
+ changes |= RequestedLayerState::Changes::Input;
+ touchableRegionCropHandle.clear();
+ }
+ if (clientState.what & layer_state_t::eStretchChanged) {
+ stretchEffect.sanitize();
+ }
+
+ if (clientState.what & layer_state_t::eHasListenerCallbacksChanged) {
+ // TODO(b/238781169) handle callbacks
+ }
+
+ if (clientState.what & layer_state_t::eBufferChanged) {
+ externalTexture = resolvedComposerState.externalTexture;
+ }
+
+ if (clientState.what & layer_state_t::ePositionChanged) {
+ requestedTransform.set(x, y);
+ }
+
+ if (clientState.what & layer_state_t::eMatrixChanged) {
+ requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+ }
+}
+
+ui::Transform RequestedLayerState::getTransform() const {
+ if ((flags & layer_state_t::eIgnoreDestinationFrame) || destinationFrame.isEmpty()) {
+ // If destination frame is not set, use the requested transform set via
+ // Transaction::setPosition and Transaction::setMatrix.
+ return requestedTransform;
+ }
+
+ Rect destRect = destinationFrame;
+ int32_t destW = destRect.width();
+ int32_t destH = destRect.height();
+ if (destRect.left < 0) {
+ destRect.left = 0;
+ destRect.right = destW;
+ }
+ if (destRect.top < 0) {
+ destRect.top = 0;
+ destRect.bottom = destH;
+ }
+
+ if (!externalTexture) {
+ ui::Transform transform;
+ transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top));
+ return transform;
+ }
+
+ uint32_t bufferWidth = externalTexture->getWidth();
+ uint32_t bufferHeight = externalTexture->getHeight();
+ // Undo any transformations on the buffer.
+ if (bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ // TODO(b/238781169) remove dep
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (transformToDisplayInverse) {
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ float sx = static_cast<float>(destW) / static_cast<float>(bufferWidth);
+ float sy = static_cast<float>(destH) / static_cast<float>(bufferHeight);
+ ui::Transform transform;
+ transform.set(sx, 0, 0, sy);
+ transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top));
+ return transform;
+}
+
+std::string RequestedLayerState::getDebugString() const {
+ return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) +
+ ",relativeParent=" + layerIdToString(relativeParentId) +
+ ",isRelativeOf=" + std::to_string(isRelativeOf) +
+ ",mirrorId=" + layerIdToString(mirrorId) +
+ ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
+}
+
+std::string RequestedLayerState::getDebugStringShort() const {
+ return "[" + std::to_string(id) + "]" + name;
+}
+
+bool RequestedLayerState::canBeDestroyed() const {
+ return !handleAlive && parentId == UNASSIGNED_LAYER_ID;
+}
+bool RequestedLayerState::isRoot() const {
+ return canBeRoot && parentId == UNASSIGNED_LAYER_ID;
+}
+bool RequestedLayerState::isHiddenByPolicy() const {
+ return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden;
+};
+half4 RequestedLayerState::getColor() const {
+ if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ return {0._hf, 0._hf, 0._hf, color.a};
+ }
+ return color;
+}
+Rect RequestedLayerState::getBufferSize() const {
+ // for buffer state layers we use the display frame size as the buffer size.
+ if (!externalTexture) {
+ return Rect::INVALID_RECT;
+ }
+
+ uint32_t bufWidth = externalTexture->getWidth();
+ uint32_t bufHeight = externalTexture->getHeight();
+
+ // Undo any transformations on the buffer and return the result.
+ if (bufferTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
+ if (transformToDisplayInverse) {
+ // TODO(b/238781169) pass in display metrics (would be useful for input info as well
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+ }
+
+ return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
+}
+
+Rect RequestedLayerState::getCroppedBufferSize() const {
+ Rect size = getBufferSize();
+ if (!crop.isEmpty() && size.isValid()) {
+ size.intersect(crop, &size);
+ } else if (!crop.isEmpty()) {
+ size = crop;
+ }
+ return size;
+}
+
+Rect RequestedLayerState::getBufferCrop() const {
+ // this is the crop rectangle that applies to the buffer
+ // itself (as opposed to the window)
+ if (!bufferCrop.isEmpty()) {
+ // if the buffer crop is defined, we use that
+ return bufferCrop;
+ } else if (externalTexture != nullptr) {
+ // otherwise we use the whole buffer
+ return externalTexture->getBounds();
+ } else {
+ // if we don't have a buffer yet, we use an empty/invalid crop
+ return Rect();
+ }
+}
+
+aidl::android::hardware::graphics::composer3::Composition RequestedLayerState::getCompositionType()
+ const {
+ using aidl::android::hardware::graphics::composer3::Composition;
+ // TODO(b/238781169) check about sidestream ready flag
+ if (sidebandStream.get()) {
+ return Composition::SIDEBAND;
+ }
+ if (!externalTexture) {
+ return Composition::SOLID_COLOR;
+ }
+ if (flags & layer_state_t::eLayerIsDisplayDecoration) {
+ return Composition::DISPLAY_DECORATION;
+ }
+ if (potentialCursor) {
+ return Composition::CURSOR;
+ }
+ return Composition::DEVICE;
+}
+
+Rect RequestedLayerState::reduce(const Rect& win, const Region& exclude) {
+ if (CC_LIKELY(exclude.isEmpty())) {
+ return win;
+ }
+ if (exclude.isRect()) {
+ return win.reduce(exclude.getBounds());
+ }
+ return Region(win).subtract(exclude).getBounds();
+}
+
+// Returns true if the layer has a relative parent that is not its own parent. This is an input
+// error from the client, and this check allows us to handle it gracefully. If both parentId and
+// relativeParentId is unassigned then the layer does not have a valid relative parent.
+// If the relative parentid is unassigned, the layer will be considered relative but won't be
+// reachable.
+bool RequestedLayerState::hasValidRelativeParent() const {
+ return isRelativeOf && parentId != relativeParentId;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
new file mode 100644
index 0000000..7849165
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <ftl/flags.h>
+#include <gui/LayerState.h>
+#include <renderengine/ExternalTexture.h>
+
+#include "LayerCreationArgs.h"
+#include "TransactionState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Stores client requested states for a layer.
+// This struct does not store any other states or states pertaining to
+// other layers. Links to other layers that are part of the client
+// requested state such as parent are translated to layer id so
+// we can avoid extending the lifetime of layer handles.
+struct RequestedLayerState : layer_state_t {
+ // Changes in state after merging with new state. This includes additional state
+ // changes found in layer_state_t::what.
+ enum class Changes : uint32_t {
+ Created = 1u << 0,
+ Destroyed = 1u << 1,
+ Hierarchy = 1u << 2,
+ Geometry = 1u << 3,
+ Content = 1u << 4,
+ Input = 1u << 5,
+ Z = 1u << 6,
+ Mirror = 1u << 7,
+ Parent = 1u << 8,
+ RelativeParent = 1u << 9,
+ Metadata = 1u << 10,
+ Visibility = 1u << 11,
+ };
+ static Rect reduce(const Rect& win, const Region& exclude);
+ RequestedLayerState(const LayerCreationArgs&);
+ void merge(const ResolvedComposerState&);
+ ui::Transform getTransform() const;
+ bool canBeDestroyed() const;
+ bool isRoot() const;
+ bool isHiddenByPolicy() const;
+ half4 getColor() const;
+ Rect getBufferSize() const;
+ Rect getCroppedBufferSize() const;
+ Rect getBufferCrop() const;
+ std::string getDebugString() const;
+ std::string getDebugStringShort() const;
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
+ bool hasValidRelativeParent() const;
+
+ // Layer serial number. This gives layers an explicit ordering, so we
+ // have a stable sort order when their layer stack and Z-order are
+ // the same.
+ const uint32_t id;
+ const std::string name;
+ bool canBeRoot = false;
+ const uint32_t layerCreationFlags;
+ const uint32_t textureName;
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ const uid_t ownerUid;
+ // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+ // If created from a system process, the value can be passed in.
+ const pid_t ownerPid;
+ bool dataspaceRequested;
+ bool hasColorTransform;
+ bool premultipliedAlpha{true};
+ // This layer can be a cursor on some displays.
+ bool potentialCursor{false};
+ bool protectedByApp{false}; // application requires protected path to external sink
+ ui::Transform requestedTransform;
+ std::shared_ptr<FenceTime> acquireFenceTime;
+ std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+
+ // book keeping states
+ bool handleAlive = true;
+ bool isRelativeOf = false;
+ uint32_t parentId = UNASSIGNED_LAYER_ID;
+ uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
+ uint32_t mirrorId = UNASSIGNED_LAYER_ID;
+ uint32_t touchCropId = UNASSIGNED_LAYER_ID;
+ uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
+ ftl::Flags<RequestedLayerState::Changes> changes;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h
new file mode 100644
index 0000000..f672f99
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/SwapErase.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+namespace android::surfaceflinger::frontend {
+// Erases the first element in vec that matches value. This is a more optimal way to
+// remove an element from a vector that avoids relocating all the elements after the one
+// that is erased.
+template <typename T>
+void swapErase(std::vector<T>& vec, const T& value) {
+ auto it = std::find(vec.begin(), vec.end(), value);
+ if (it != vec.end()) {
+ std::iter_swap(it, vec.end() - 1);
+ vec.erase(vec.end() - 1);
+ }
+}
+
+// Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element
+// that returns true for predicate.
+template <typename T, class P>
+void swapErase(std::vector<T>& vec, P predicate) {
+ auto it = std::find_if(vec.begin(), vec.end(), predicate);
+ if (it != vec.end()) {
+ std::iter_swap(it, vec.end() - 1);
+ vec.erase(vec.end() - 1);
+ }
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 95961fe..c2109b3 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -24,7 +24,7 @@
#include "TransactionHandler.h"
-namespace android {
+namespace android::surfaceflinger::frontend {
void TransactionHandler::queueTransaction(TransactionState&& state) {
mLocklessTransactionQueue.push(std::move(state));
@@ -177,7 +177,7 @@
}
mStalledTransactions.push_back(transactionId);
- listener->onTransactionQueueStalled(String8(reason.c_str()));
+ listener->onTransactionQueueStalled(reason);
}
void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
@@ -186,4 +186,4 @@
mStalledTransactions.erase(it);
}
}
-} // namespace android
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 2b6f07d..475ff1b 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -18,9 +18,6 @@
#include <semaphore.h>
#include <cstdint>
-#include <mutex>
-#include <queue>
-#include <thread>
#include <vector>
#include <LocklessQueue.h>
@@ -30,6 +27,11 @@
#include <ftl/small_vector.h>
namespace android {
+
+class TestableSurfaceFlinger;
+using gui::IListenerHash;
+namespace surfaceflinger::frontend {
+
class TransactionHandler {
public:
struct TransactionFlushState {
@@ -60,7 +62,7 @@
private:
// For unit tests
- friend class TestableSurfaceFlinger;
+ friend class ::android::TestableSurfaceFlinger;
int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
TransactionReadiness applyFilters(TransactionFlushState&);
@@ -71,5 +73,5 @@
ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
std::vector<uint64_t> mStalledTransactions;
};
-
+} // namespace surfaceflinger::frontend
} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.cpp b/services/surfaceflinger/HwcSlotGenerator.cpp
deleted file mode 100644
index 939c35b..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "HwcSlotGenerator"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-HwcSlotGenerator::HwcSlotGenerator() {
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mFreeHwcCacheSlots.push(i);
- }
-}
-
-void HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
- std::lock_guard lock(mMutex);
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, failed to erase buffer");
- return;
- }
- eraseBufferLocked(clientCacheId);
-}
-
-int HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- std::lock_guard<std::mutex> lock(mMutex);
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return addCachedBuffer(clientCacheId);
- }
- auto& [hwcCacheSlot, counter] = itr->second;
- counter = mCounter++;
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, returning invalid slot");
- return BufferQueue::INVALID_BUFFER_SLOT;
- }
-
- ClientCache::getInstance().registerErasedRecipient(clientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-
- int hwcCacheSlot = getFreeHwcCacheSlot();
- mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
- if (mFreeHwcCacheSlots.empty()) {
- evictLeastRecentlyUsed();
- }
-
- int hwcCacheSlot = mFreeHwcCacheSlots.top();
- mFreeHwcCacheSlots.pop();
- return hwcCacheSlot;
-}
-
-void HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
- uint64_t minCounter = UINT_MAX;
- client_cache_t minClientCacheId = {};
- for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
- const auto& [hwcCacheSlot, counter] = slotCounter;
- if (counter < minCounter) {
- minCounter = counter;
- minClientCacheId = clientCacheId;
- }
- }
- eraseBufferLocked(minClientCacheId);
-
- ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-}
-
-void HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return;
- }
- auto& [hwcCacheSlot, counter] = itr->second;
-
- // TODO send to hwc cache and resources
-
- mFreeHwcCacheSlots.push(hwcCacheSlot);
- mCachedBuffers.erase(clientCacheId);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.h b/services/surfaceflinger/HwcSlotGenerator.h
deleted file mode 100644
index 5a1b6d7..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <functional>
-#include <mutex>
-#include <stack>
-#include <unordered_map>
-
-#include "ClientCache.h"
-
-namespace android {
-
-class HwcSlotGenerator : public ClientCache::ErasedRecipient {
-public:
- HwcSlotGenerator();
- void bufferErased(const client_cache_t& clientCacheId);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
-
-private:
- friend class SlotGenerationTest;
- int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
- int getFreeHwcCacheSlot() REQUIRES(mMutex);
- void evictLeastRecentlyUsed() REQUIRES(mMutex);
- void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-
- struct CachedBufferHash {
- std::size_t operator()(const client_cache_t& clientCacheId) const {
- return std::hash<uint64_t>{}(clientCacheId.id);
- }
- };
-
- std::mutex mMutex;
-
- std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
- CachedBufferHash>
- mCachedBuffers GUARDED_BY(mMutex);
- std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
-
- // The cache increments this counter value when a slot is updated or used.
- // Used to track the least recently-used buffer
- uint64_t mCounter = 0;
-};
-} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index be5fffc..1f159ae 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -144,7 +144,6 @@
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
mTextureName(args.textureName),
- mHwcSlotGenerator(sp<HwcSlotGenerator>::make()),
mLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
@@ -217,6 +216,9 @@
}
Layer::~Layer() {
+ LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId,
+ "Layer destructor called off the main thread.");
+
// The original layer and the clone layer share the same texture and buffer. Therefore, only
// one of the layers, in this case the original layer, needs to handle the deletion. The
// original layer and the clone should be removed at the same time so there shouldn't be any
@@ -573,9 +575,6 @@
}
snapshot->buffer = getBuffer();
- snapshot->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
- ? 0
- : mBufferInfo.mBufferSlot;
snapshot->acquireFence = mBufferInfo.mFence;
snapshot->frameNumber = mBufferInfo.mFrameNumber;
snapshot->sidebandStreamHasFrame = false;
@@ -1123,7 +1122,7 @@
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
// the same reason we are allowing touch boost for those layers. See
- // RefreshRateSelector::rankRefreshRates for details.
+ // RefreshRateSelector::rankFrameRates for details.
const auto layerVotedWithDefaultCompatibility =
frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
@@ -1472,8 +1471,9 @@
mFrameTracker.getStats(outStats);
}
-void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) ownerPid:%d ownerUid:%d\n", getName().c_str(), getType(),
+void Layer::dumpOffscreenDebugInfo(std::string& result) const {
+ std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
+ StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(),
mOwnerPid, mOwnerUid);
}
@@ -2604,9 +2604,12 @@
return;
}
ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
- listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd,
+ static_cast<int32_t>(currentMaxAcquiredBufferCount));
}
void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
@@ -2840,7 +2843,7 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
- const FrameTimelineInfo& info, int hwcBufferSlot) {
+ const FrameTimelineInfo& info) {
ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
if (!buffer) {
return false;
@@ -2886,7 +2889,6 @@
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.clientCacheId = bufferData.cachedBuffer;
- mDrawingState.hwcBufferSlot = hwcBufferSlot;
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
? bufferData.acquireFence
: Fence::NO_FENCE;
@@ -3185,7 +3187,6 @@
mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
mBufferInfo.mApi = mDrawingState.api;
mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
- mBufferInfo.mBufferSlot = mDrawingState.hwcBufferSlot;
}
Rect Layer::computeBufferCrop(const State& s) {
@@ -3597,7 +3598,7 @@
}
if (display) {
- const Fps refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps();
+ const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
const std::optional<Fps> renderRate =
mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -3968,22 +3969,15 @@
}
}
-int Layer::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- return mHwcSlotGenerator->getHwcCacheSlot(clientCacheId);
-}
-
LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
- LOG_ALWAYS_FATAL_IF(!mLayer, "LayerSnapshotGuard received a null layer.");
- mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
- LOG_ALWAYS_FATAL_IF(!mLayer->mLayerFE->mSnapshot,
- "LayerFE snapshot null after taking ownership from layer");
+ if (mLayer) {
+ mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
+ }
}
LayerSnapshotGuard::~LayerSnapshotGuard() {
if (mLayer) {
mLayer->mSnapshot = std::move(mLayer->mLayerFE->mSnapshot);
- LOG_ALWAYS_FATAL_IF(!mLayer->mSnapshot,
- "Layer snapshot null after taking ownership from LayerFE");
}
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7669bab..08a13a3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -51,7 +51,6 @@
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
-#include "HwcSlotGenerator.h"
#include "LayerFE.h"
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
@@ -83,6 +82,7 @@
} // namespace frametimeline
class Layer : public virtual RefBase {
+public:
// The following constants represent priority of the window. SF uses this information when
// deciding which window has a priority when deciding about the refresh rate of the screen.
// Priority 0 is considered the highest priority. -1 means that the priority is unset.
@@ -94,7 +94,6 @@
// Windows that are not in focus, but voted for a specific mode ID.
static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
-public:
enum { // flags for doTransaction()
eDontUpdateGeometryState = 0x00000001,
eVisibleRegion = 0x00000002,
@@ -146,7 +145,6 @@
bool transformToDisplayInverse;
Region transparentRegionHint;
std::shared_ptr<renderengine::ExternalTexture> buffer;
- int hwcBufferSlot;
client_cache_t clientCacheId;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
@@ -298,8 +296,7 @@
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/,
- int /* hwcBufferSlot */);
+ std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
@@ -500,7 +497,6 @@
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
- int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
bool mFrameLatencyNeeded{false};
};
@@ -634,7 +630,7 @@
void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
- void dumpCallingUidPid(std::string& result) const;
+ void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
@@ -813,7 +809,6 @@
void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
std::unordered_set<Layer*>& visited);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
protected:
// For unit tests
@@ -1124,8 +1119,6 @@
// not specify a destination frame.
ui::Transform mRequestedTransform;
- sp<HwcSlotGenerator> mHwcSlotGenerator;
-
sp<LayerFE> mLayerFE;
std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9b19afb..7aa7e17 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -42,8 +42,8 @@
constexpr int kDigitHeight = 100;
constexpr int kDigitSpace = 16;
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
constexpr int kBufferHeight = kDigitHeight;
SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
@@ -121,16 +121,10 @@
drawSegment(Segment::Bottom, left, color, canvas);
}
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
ui::Transform::RotationFlags rotation,
- bool showSpinner) -> Buffers {
- if (number < 0 || number > 1000) return {};
-
- const auto hundreds = number / 100;
- const auto tens = (number / 10) % 10;
- const auto ones = number % 10;
-
- const size_t loopCount = showSpinner ? 6 : 1;
+ ftl::Flags<Features> features) -> Buffers {
+ const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
Buffers buffers;
buffers.reserve(loopCount);
@@ -169,20 +163,9 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- if (tens != 0) {
- drawDigit(tens, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- drawDigit(ones, left, color, *canvas);
- left += kDigitWidth + kDigitSpace;
-
- if (showSpinner) {
+ drawNumber(displayFps, left, color, *canvas);
+ left += 3 * (kDigitWidth + kDigitSpace);
+ if (features.test(Features::Spinner)) {
switch (i) {
case 0:
drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +188,13 @@
}
}
+ left += kDigitWidth + kDigitSpace;
+
+ if (features.test(Features::RenderRate)) {
+ drawNumber(renderFps, left, color, *canvas);
+ }
+ left += 3 * (kDigitWidth + kDigitSpace);
+
void* pixels = nullptr;
buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
@@ -219,6 +209,23 @@
return buffers;
}
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+ SkCanvas& canvas) {
+ if (number < 0 || number >= 1000) return;
+
+ if (number >= 100) {
+ drawDigit(number / 100, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ if (number >= 10) {
+ drawDigit((number / 10) % 10, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ drawDigit(number % 10, left, color, canvas);
+}
+
std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
sp<SurfaceControl> surfaceControl =
SurfaceComposerClient::getDefault()
@@ -228,10 +235,8 @@
return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
}
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
- : mFpsRange(fpsRange),
- mShowSpinner(showSpinner),
- mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+ : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
@@ -243,10 +248,15 @@
.apply();
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
+ // avoid caching different render rates if RenderRate is anyway not visible
+ if (!mFeatures.test(Features::RenderRate)) {
+ renderFps = 0_Hz;
+ }
+
const auto transformHint =
static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
@@ -266,17 +276,20 @@
.setTransform(mSurfaceControl->get(), transform)
.apply();
- BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+ BufferCache::const_iterator it =
+ mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
const int minFps = mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
- // Clamp to the range. The current fps may be outside of this range if the display has
- // changed its set of supported refresh rates.
- const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+ // Clamp to the range. The current displayFps may be outside of this range if the display
+ // has changed its set of supported refresh rates.
+ const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+ const int renderIntFps = renderFps.getIntValue();
// Ensure non-zero range to avoid division by zero.
- const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+ const float fpsScale =
+ static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
constexpr SkColor kMinFpsColor = SK_ColorRED;
constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +305,11 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
- it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+ auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+ mFeatures);
+ it = mBufferCache
+ .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+ .first;
}
return it->second;
@@ -303,7 +319,7 @@
constexpr int32_t kMaxWidth = 1000;
const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
const auto height = 2 * width;
- Rect frame((3 * width) >> 4, height >> 5);
+ Rect frame((5 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
createTransaction(mSurfaceControl->get())
@@ -317,16 +333,17 @@
createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
- mCurrentFps = fps;
- const auto buffer = getOrCreateBuffers(fps)[mFrame];
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+ mDisplayFps = displayFps;
+ mRenderFps = renderFps;
+ const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
- if (!mShowSpinner || !mCurrentFps) return;
+ if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
- const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+ const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..d6f828f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,6 +19,7 @@
#include <SkColor.h>
#include <vector>
+#include <ftl/flags.h>
#include <ftl/small_map.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
@@ -50,11 +51,16 @@
class RefreshRateOverlay {
public:
- RefreshRateOverlay(FpsRange, bool showSpinner);
+ enum class Features {
+ Spinner = 1 << 0,
+ RenderRate = 1 << 1,
+ };
+
+ RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
- void changeRefreshRate(Fps);
+ void changeRefreshRate(Fps, Fps);
void animate();
private:
@@ -62,32 +68,39 @@
class SevenSegmentDrawer {
public:
- static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+ static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+ ftl::Flags<Features>);
private:
enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
static void drawSegment(Segment, int left, SkColor, SkCanvas&);
static void drawDigit(int digit, int left, SkColor, SkCanvas&);
+ static void drawNumber(int number, int left, SkColor, SkCanvas&);
};
- const Buffers& getOrCreateBuffers(Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps);
struct Key {
- int fps;
+ int displayFps;
+ int renderFps;
ui::Transform::RotationFlags flags;
- bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+ bool operator==(Key other) const {
+ return displayFps == other.displayFps && renderFps == other.renderFps &&
+ flags == other.flags;
+ }
};
using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
BufferCache mBufferCache;
- std::optional<Fps> mCurrentFps;
+ std::optional<Fps> mDisplayFps;
+ std::optional<Fps> mRenderFps;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
- const bool mShowSpinner;
+ const ftl::Flags<Features> mFeatures;
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 1acf15a..008d8c4 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -124,12 +124,12 @@
return event;
}
-DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
+DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
- systemTime()};
- event.modeChange.modeId = mode->getId().value();
- event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ mode.modePtr->getPhysicalDisplayId(), systemTime()};
+ event.modeChange.modeId = mode.modePtr->getId().value();
+ event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
return event;
}
@@ -405,7 +405,7 @@
mCondition.notify_all();
}
-void EventThread::onModeChanged(DisplayModePtr mode) {
+void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeModeChanged(mode));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 7a5a348..43c3598 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
+#include <scheduler/FrameRateMode.h>
#include <condition_variable>
#include <cstdint>
#include <deque>
@@ -134,7 +135,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active mode and apps needs to be notified about the change
- virtual void onModeChanged(DisplayModePtr) = 0;
+ virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
// called when SF updates the Frame Rate Override list
virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -185,7 +186,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onModeChanged(DisplayModePtr) override;
+ void onModeChanged(const scheduler::FrameRateMode&) override;
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 8d4ea05..a05d3df 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <cmath>
#include <deque>
+#include <map>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -31,6 +32,7 @@
#include <ftl/fake_guard.h>
#include <ftl/match.h>
#include <ftl/unit.h>
+#include <scheduler/FrameRateMode.h>
#include <utils/Trace.h>
#include "../SurfaceFlingerProperties.h"
@@ -43,7 +45,7 @@
namespace {
struct RefreshRateScore {
- DisplayModeIterator modeIt;
+ FrameRateMode frameRateMode;
float overallScore;
struct {
float modeBelowThreshold;
@@ -77,19 +79,11 @@
return knownFrameRates;
}
-// The Filter is a `bool(const DisplayMode&)` predicate.
-template <typename Filter>
-std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) {
std::vector<DisplayModeIterator> sortedModes;
sortedModes.reserve(modes.size());
-
for (auto it = modes.begin(); it != modes.end(); ++it) {
- const auto& [id, mode] = *it;
-
- if (filter(*mode)) {
- ALOGV("%s: including mode %d", __func__, id.value());
- sortedModes.push_back(it);
- }
+ sortedModes.push_back(it);
}
std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
@@ -106,6 +100,22 @@
return sortedModes;
}
+std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range,
+ RefreshRateSelector::Config::FrameRateOverride config) {
+ if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) {
+ return {1, 1};
+ }
+
+ using fps_approx_ops::operator/;
+ // use signed type as `fps / range.max` might be 0
+ const auto start = std::max(1, static_cast<int>(fps / range.max) - 1);
+ const auto end = fps /
+ std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
+ fps_approx_ops::operator<);
+
+ return {start, end};
+}
+
bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
for (const auto it1 : sortedModes) {
const auto& mode1 = it1->second;
@@ -136,27 +146,109 @@
} // namespace
+auto RefreshRateSelector::createFrameRateModes(
+ std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
+ -> std::vector<FrameRateMode> {
+ struct Key {
+ Fps fps;
+ int32_t group;
+ };
+
+ struct KeyLess {
+ bool operator()(const Key& a, const Key& b) const {
+ using namespace fps_approx_ops;
+ if (a.fps != b.fps) {
+ return a.fps < b.fps;
+ }
+
+ // For the same fps the order doesn't really matter, but we still
+ // want the behaviour of a strictly less operator.
+ // We use the group id as the secondary ordering for that.
+ return a.group < b.group;
+ }
+ };
+
+ std::map<Key, DisplayModeIterator, KeyLess> ratesMap;
+ for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+ const auto& [id, mode] = *it;
+
+ if (!filterModes(*mode)) {
+ continue;
+ }
+ const auto [start, end] =
+ divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride);
+ for (auto divisor = start; divisor <= end; divisor++) {
+ const auto fps = mode->getFps() / divisor;
+ using fps_approx_ops::operator<;
+ if (fps < kMinSupportedFrameRate) {
+ break;
+ }
+
+ if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled &&
+ !renderRange.includes(fps)) {
+ continue;
+ }
+
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+ !isNativeRefreshRate(fps)) {
+ continue;
+ }
+
+ const auto [existingIter, emplaceHappened] =
+ ratesMap.try_emplace(Key{fps, mode->getGroup()}, it);
+ if (emplaceHappened) {
+ ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
+ to_string(mode->getFps()).c_str());
+ } else {
+ // We might need to update the map as we found a lower refresh rate
+ if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+ existingIter->second = it;
+ ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
+ to_string(mode->getFps()).c_str());
+ }
+ }
+ }
+ }
+
+ std::vector<FrameRateMode> frameRateModes;
+ frameRateModes.reserve(ratesMap.size());
+ for (const auto& [key, mode] : ratesMap) {
+ frameRateModes.emplace_back(FrameRateMode{key.fps, ftl::as_non_null(mode->second)});
+ }
+
+ // We always want that the lowest frame rate will be corresponding to the
+ // lowest mode for power saving.
+ const auto lowestRefreshRateIt =
+ std::min_element(frameRateModes.begin(), frameRateModes.end(),
+ [](const FrameRateMode& lhs, const FrameRateMode& rhs) {
+ return isStrictlyLess(lhs.modePtr->getFps(),
+ rhs.modePtr->getFps());
+ });
+ frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt);
+
+ return frameRateModes;
+}
+
struct RefreshRateSelector::RefreshRateScoreComparator {
bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
- const auto& [modeIt, overallScore, _] = lhs;
+ const auto& [frameRateMode, overallScore, _] = lhs;
- std::string name = to_string(modeIt->second->getFps());
+ std::string name = to_string(frameRateMode);
+
ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
-
ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
- if (!ScoredRefreshRate::scoresEqual(overallScore, rhs.overallScore)) {
+ if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) {
return overallScore > rhs.overallScore;
}
- // If overallScore tie we will pick the higher refresh rate if
- // high refresh rate is the priority else the lower refresh rate.
if (refreshRateOrder == RefreshRateOrder::Descending) {
using fps_approx_ops::operator>;
- return modeIt->second->getFps() > rhs.modeIt->second->getFps();
+ return frameRateMode.fps > rhs.frameRateMode.fps;
} else {
using fps_approx_ops::operator<;
- return modeIt->second->getFps() < rhs.modeIt->second->getFps();
+ return frameRateMode.fps < rhs.frameRateMode.fps;
}
}
@@ -210,7 +302,15 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
- if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
+ const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
+
+ // We only want to score this layer as a fractional pair if the content is not
+ // significantly faster than the display rate, at it would cause a significant frame drop.
+ // It is more appropriate to choose a higher display rate even if
+ // a pull-down will be required.
+ constexpr float kMinMultiplier = 0.25f;
+ if (multiplier >= kMinMultiplier &&
+ isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
return kScoreForFractionalPairs;
}
@@ -245,9 +345,9 @@
return 0;
}
-float RefreshRateSelector::calculateRefreshRateScoreForFps(Fps refreshRate) const {
- const float ratio =
- refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
+float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const {
+ const auto& maxFps = mAppRequestFrameRates.back().fps;
+ const float ratio = refreshRate.getValue() / maxFps.getValue();
// Use ratio^2 to get a lower score the more we get further from peak
return ratio * ratio;
}
@@ -260,12 +360,12 @@
// If the layer wants Max, give higher score to the higher refresh rate
if (layer.vote == LayerVoteType::Max) {
- return calculateRefreshRateScoreForFps(refreshRate);
+ return calculateDistanceScoreFromMax(refreshRate);
}
if (layer.vote == LayerVoteType::ExplicitExact) {
const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
- if (supportsFrameRateOverrideByContent()) {
+ if (supportsAppFrameRateOverrideByContent()) {
// Since we support frame rate override, allow refresh rates which are
// multiples of the layer's request, as those apps would be throttled
// down to run at the desired refresh rate.
@@ -289,33 +389,33 @@
kNonExactMatchingPenalty;
}
-auto RefreshRateSelector::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const -> RankedFrameRates {
std::lock_guard lock(mLock);
- if (mGetRankedRefreshRatesCache &&
- mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) {
- return mGetRankedRefreshRatesCache->result;
+ if (mGetRankedFrameRatesCache &&
+ mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+ return mGetRankedFrameRatesCache->result;
}
- const auto result = getRankedRefreshRatesLocked(layers, signals);
- mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result};
+ const auto result = getRankedFrameRatesLocked(layers, signals);
+ mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
return result;
}
-auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
- -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const
+ -> RankedFrameRates {
using namespace fps_approx_ops;
ATRACE_CALL();
ALOGV("%s: %zu layers", __func__, layers.size());
- const auto& activeMode = *getActiveModeItLocked()->second;
+ const auto& activeMode = *getActiveModeLocked().modePtr;
- // Keep the display at max refresh rate for the duration of powering on the display.
+ // Keep the display at max frame rate for the duration of powering on the display.
if (signals.powerOnImminent) {
ALOGV("Power On Imminent");
- return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Descending),
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending),
GlobalSignals{.powerOnImminent = true}};
}
@@ -375,7 +475,7 @@
// selected a refresh rate to see if we should apply touch boost.
if (signals.touch && !hasExplicitVoteLayers) {
ALOGV("Touch Boost");
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending),
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending),
GlobalSignals{.touch = true}};
}
@@ -387,27 +487,27 @@
if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
ALOGV("Idle");
- return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
GlobalSignals{.idle = true}};
}
if (layers.empty() || noVoteLayers == layers.size()) {
ALOGV("No layers with votes");
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
}
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
ALOGV("All layers Min");
- return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
}
// Find the best refresh rate based on score
std::vector<RefreshRateScore> scores;
- scores.reserve(mAppRequestRefreshRates.size());
+ scores.reserve(mAppRequestFrameRates.size());
- for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
- scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
+ for (const FrameRateMode& it : mAppRequestFrameRates) {
+ scores.emplace_back(RefreshRateScore{it, 0.0f});
}
for (const auto& layer : layers) {
@@ -420,13 +520,13 @@
const auto weight = layer.weight;
- for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
- const auto& [id, mode] = *modeIt;
- const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup();
+ for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ const auto& [fps, modePtr] = mode;
+ const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup();
if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
to_string(activeMode).c_str());
continue;
}
@@ -435,7 +535,7 @@
!layer.focused) {
ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
" Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
to_string(activeMode).c_str());
continue;
}
@@ -445,14 +545,14 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
+ const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup;
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
- to_string(*mode).c_str(), to_string(activeMode).c_str());
+ to_string(*modePtr).c_str(), to_string(activeMode).c_str());
continue;
}
- const bool inPrimaryRange = policy->primaryRanges.physical.includes(mode->getFps());
+ const bool inPrimaryRange = policy->primaryRanges.physical.includes(modePtr->getFps());
if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
!(layer.focused &&
(layer.vote == LayerVoteType::ExplicitDefault ||
@@ -462,8 +562,7 @@
continue;
}
- const float layerScore =
- calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
+ const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch);
const float weightedLayerScore = weight * layerScore;
// Layer with fixed source has a special consideration which depends on the
@@ -491,21 +590,21 @@
Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
if (fixedSourceLayer && layerBelowThreshold) {
const bool modeAboveThreshold =
- mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+ modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
if (modeAboveThreshold) {
- ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
- formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
- layerScore);
+ ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+ to_string(modePtr->getFps()).c_str(), layerScore);
fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
} else {
- ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
- formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
- layerScore);
+ ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+ to_string(modePtr->getFps()).c_str(), layerScore);
fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
}
} else {
- ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
- to_string(mode->getFps()).c_str(), layerScore);
+ ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(),
+ to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore);
overallScore += weightedLayerScore;
}
}
@@ -524,25 +623,26 @@
const auto maxScoreIt =
std::max_element(scores.begin(), scores.end(),
[](RefreshRateScore max, RefreshRateScore current) {
- const auto& [modeIt, overallScore, _] = current;
- return overallScore > max.overallScore;
+ return current.overallScore > max.overallScore;
});
- ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
+ ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the "
+ "threshold for "
"refresh rate multiples",
- to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
+ to_string(maxScoreIt->frameRateMode.fps).c_str(),
+ to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(),
maxScoreAboveThreshold ? "above" : "below");
- return maxScoreIt->modeIt->second->getFps() >=
+ return maxScoreIt->frameRateMode.modePtr->getFps() >=
Fps::fromValue(mConfig.frameRateMultipleThreshold);
}();
// Now we can add the fixed rate layers score
- for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
if (maxScoreAboveThreshold) {
overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
}
- ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
- overallScore);
+ ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
+ to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore);
}
// Now that we scored all the refresh rates we need to pick the one that got the highest
@@ -552,12 +652,12 @@
std::sort(scores.begin(), scores.end(),
RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
- RefreshRateRanking ranking;
+ FrameRateRanking ranking;
ranking.reserve(scores.size());
std::transform(scores.begin(), scores.end(), back_inserter(ranking),
[](const RefreshRateScore& score) {
- return ScoredRefreshRate{score.modeIt->second, score.overallScore};
+ return ScoredFrameRate{score.frameRateMode, score.overallScore};
});
const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
@@ -569,7 +669,7 @@
// range instead of picking a random score from the app range.
if (noLayerScore) {
ALOGV("Layers not scored");
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
} else {
return {ranking, kNoSignals};
}
@@ -580,7 +680,7 @@
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
const bool touchBoostForExplicitExact = [&] {
- if (supportsFrameRateOverrideByContent()) {
+ if (supportsAppFrameRateOverrideByContent()) {
// Enable touch boost if there are other layers besides exact
return explicitExact + noVoteLayers != layers.size();
} else {
@@ -589,12 +689,11 @@
}
}();
- const auto touchRefreshRates = rankRefreshRates(anchorGroup, RefreshRateOrder::Descending);
-
+ const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- scores.front().modeIt->second->getFps() < touchRefreshRates.front().modePtr->getFps()) {
+ scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
return {touchRefreshRates, GlobalSignals{.touch = true}};
}
@@ -603,7 +702,7 @@
// current config
if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
const auto preferredDisplayMode = activeMode.getId();
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
kNoSignals};
}
@@ -649,20 +748,13 @@
GlobalSignals globalSignals) const
-> UidToFrameRateOverride {
ATRACE_CALL();
- ALOGV("%s: %zu layers", __func__, layers.size());
-
- std::lock_guard lock(mLock);
-
- // Prepare a set of supported display refresh rates for easy lookup
- constexpr size_t kStaticCapacity = 8;
- ftl::SmallMap<Fps, ftl::Unit, kStaticCapacity, FpsApproxEqual> supportedDisplayRefreshRates;
- if (mConfig.enableFrameRateOverride ==
- Config::FrameRateOverride::EnabledForNativeRefreshRates) {
- for (const auto& [_, modePtr] : mDisplayModes) {
- supportedDisplayRefreshRates.try_emplace(modePtr->getFps(), ftl::unit);
- }
+ if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) {
+ return {};
}
+ ALOGV("%s: %zu layers", __func__, layers.size());
+ std::lock_guard lock(mLock);
+
const auto* policyPtr = getCurrentPolicyLocked();
// We don't want to run lower than 30fps
const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);
@@ -676,8 +768,8 @@
for (unsigned n = numMultiples; n > 0; n--) {
const Fps divisor = displayRefreshRate / n;
if (mConfig.enableFrameRateOverride ==
- Config::FrameRateOverride::EnabledForNativeRefreshRates &&
- !supportedDisplayRefreshRates.contains(divisor)) {
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+ !isNativeRefreshRate(divisor)) {
continue;
}
@@ -736,7 +828,7 @@
[](const auto& lhsPair, const auto& rhsPair) {
const float lhs = lhsPair.second;
const float rhs = rhsPair.second;
- return lhs < rhs && !ScoredRefreshRate::scoresEqual(lhs, rhs);
+ return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
});
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
frameRateOverrides.emplace(uid, overrideFps);
@@ -745,30 +837,33 @@
return frameRateOverrides;
}
-std::optional<Fps> RefreshRateSelector::onKernelTimerChanged(
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
std::lock_guard lock(mLock);
- const DisplayModePtr& current = desiredActiveModeId
- ? mDisplayModes.get(*desiredActiveModeId)->get()
- : getActiveModeItLocked()->second;
+ const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
+ if (desiredActiveModeId) {
+ const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
+ return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+ }
+
+ return getActiveModeLocked();
+ }();
const DisplayModePtr& min = mMinRefreshRateModeIt->second;
- if (current == min) {
+ if (current.modePtr->getId() == min->getId()) {
return {};
}
- const auto& mode = timerExpired ? min : current;
- return mode->getFps();
+ return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
}
const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
- const auto& activeMode = *getActiveModeItLocked()->second;
+ const auto& activeMode = *getActiveModeLocked().modePtr;
- for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
- const auto& mode = modeIt->second;
- if (activeMode.getGroup() == mode->getGroup()) {
- return mode;
+ for (const FrameRateMode& mode : mPrimaryFrameRates) {
+ if (activeMode.getGroup() == mode.modePtr->getGroup()) {
+ return mode.modePtr.get();
}
}
@@ -776,55 +871,72 @@
to_string(activeMode).c_str());
// Default to the lowest refresh rate.
- return mPrimaryRefreshRates.front()->second;
+ return mPrimaryFrameRates.front().modePtr.get();
}
const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
- for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
- const auto& mode = (*it)->second;
- if (anchorGroup == mode->getGroup()) {
- return mode;
+ const ftl::NonNull<DisplayModePtr>* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
+ const ftl::NonNull<DisplayModePtr>* max = &mPrimaryFrameRates.back().modePtr;
+
+ bool maxByAnchorFound = false;
+ for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
+ using namespace fps_approx_ops;
+ if (it->modePtr->getFps() > (*max)->getFps()) {
+ max = &it->modePtr;
}
+
+ if (anchorGroup == it->modePtr->getGroup() &&
+ it->modePtr->getFps() >= (*maxByAnchor)->getFps()) {
+ maxByAnchorFound = true;
+ maxByAnchor = &it->modePtr;
+ }
+ }
+
+ if (maxByAnchorFound) {
+ return maxByAnchor->get();
}
ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
// Default to the highest refresh rate.
- return mPrimaryRefreshRates.back()->second;
+ return max->get();
}
-auto RefreshRateSelector::rankRefreshRates(
- std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking {
- std::deque<ScoredRefreshRate> ranking;
-
- const auto rankRefreshRate = [&](DisplayModeIterator it) REQUIRES(mLock) {
- const auto& mode = it->second;
- if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
+auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt) const
+ -> FrameRateRanking {
+ const char* const whence = __func__;
+ std::deque<ScoredFrameRate> ranking;
+ const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+ const auto& modePtr = frameRateMode.modePtr;
+ if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
return;
}
- float score = calculateRefreshRateScoreForFps(mode->getFps());
+ float score = calculateDistanceScoreFromMax(frameRateMode.fps);
const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
if (inverseScore) {
score = 1.0f / score;
}
if (preferredDisplayModeOpt) {
- if (*preferredDisplayModeOpt == mode->getId()) {
+ if (*preferredDisplayModeOpt == modePtr->getId()) {
constexpr float kScore = std::numeric_limits<float>::max();
- ranking.push_front(ScoredRefreshRate{mode, kScore});
+ ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
return;
}
constexpr float kNonPreferredModePenalty = 0.95f;
score *= kNonPreferredModePenalty;
}
- ranking.push_back(ScoredRefreshRate{mode, score});
+ ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
+ to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
+ ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
};
if (refreshRateOrder == RefreshRateOrder::Ascending) {
- std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), rankRefreshRate);
+ std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate);
} else {
- std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), rankRefreshRate);
+ std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate);
}
if (!ranking.empty() || !anchorGroupOpt) {
@@ -836,34 +948,29 @@
refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
constexpr std::optional<int> kNoAnchorGroup = std::nullopt;
- return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
+ return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
}
-DisplayModePtr RefreshRateSelector::getActiveModePtr() const {
+FrameRateMode RefreshRateSelector::getActiveMode() const {
std::lock_guard lock(mLock);
- return getActiveModeItLocked()->second;
+ return getActiveModeLocked();
}
-const DisplayMode& RefreshRateSelector::getActiveMode() const {
- // Reads from kMainThreadContext do not require mLock.
- ftl::FakeGuard guard(mLock);
- return *mActiveModeIt->second;
+const FrameRateMode& RefreshRateSelector::getActiveModeLocked() const {
+ return *mActiveModeOpt;
}
-DisplayModeIterator RefreshRateSelector::getActiveModeItLocked() const {
- // Reads under mLock do not require kMainThreadContext.
- return FTL_FAKE_GUARD(kMainThreadContext, mActiveModeIt);
-}
-
-void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) {
+void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
std::lock_guard lock(mLock);
- // Invalidate the cached invocation to getRankedRefreshRates. This forces
- // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
- mGetRankedRefreshRatesCache.reset();
+ // Invalidate the cached invocation to getRankedFrameRates. This forces
+ // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+ mGetRankedFrameRatesCache.reset();
- mActiveModeIt = mDisplayModes.find(modeId);
- LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
+ const auto activeModeOpt = mDisplayModes.get(modeId);
+ LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+
+ mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())});
}
RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId,
@@ -895,16 +1002,17 @@
void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
std::lock_guard lock(mLock);
- // Invalidate the cached invocation to getRankedRefreshRates. This forces
- // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
- mGetRankedRefreshRatesCache.reset();
+ // Invalidate the cached invocation to getRankedFrameRates. This forces
+ // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+ mGetRankedFrameRatesCache.reset();
mDisplayModes = std::move(modes);
- mActiveModeIt = mDisplayModes.find(activeModeId);
- LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
+ const auto activeModeOpt = mDisplayModes.get(activeModeId);
+ LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+ mActiveModeOpt =
+ FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())};
- const auto sortedModes =
- sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
+ const auto sortedModes = sortByRefreshRate(mDisplayModes);
mMinRefreshRateModeIt = sortedModes.front();
mMaxRefreshRateModeIt = sortedModes.back();
@@ -915,15 +1023,23 @@
mFrameRateOverrideConfig = [&] {
switch (mConfig.enableFrameRateOverride) {
case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverride:
case Config::FrameRateOverride::Enabled:
return mConfig.enableFrameRateOverride;
- case Config::FrameRateOverride::EnabledForNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
return shouldEnableFrameRateOverride(sortedModes)
- ? Config::FrameRateOverride::EnabledForNativeRefreshRates
+ ? Config::FrameRateOverride::AppOverrideNativeRefreshRates
: Config::FrameRateOverride::Disabled;
}
}();
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates) {
+ for (const auto& [_, mode] : mDisplayModes) {
+ mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit);
+ }
+ }
+
constructAvailableRefreshRates();
}
@@ -939,13 +1055,22 @@
return false;
}
- using namespace fps_approx_ops;
- return policy.appRequestRanges.physical.min <= policy.primaryRanges.physical.min &&
- policy.appRequestRanges.physical.max >= policy.primaryRanges.physical.max;
+ const auto& primaryRanges = policy.primaryRanges;
+ const auto& appRequestRanges = policy.appRequestRanges;
+ ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
+ "Physical range is invalid: primary: %s appRequest: %s",
+ to_string(primaryRanges.physical).c_str(),
+ to_string(appRequestRanges.physical).c_str());
+ ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render),
+ "Render range is invalid: primary: %s appRequest: %s",
+ to_string(primaryRanges.render).c_str(), to_string(appRequestRanges.render).c_str());
+
+ return primaryRanges.valid() && appRequestRanges.valid();
}
auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
Policy oldPolicy;
+ PhysicalDisplayId displayId;
{
std::lock_guard lock(mLock);
oldPolicy = *getCurrentPolicyLocked();
@@ -979,15 +1104,16 @@
return SetPolicyResult::Invalid;
}
- mGetRankedRefreshRatesCache.reset();
+ mGetRankedFrameRatesCache.reset();
if (*getCurrentPolicyLocked() == oldPolicy) {
return SetPolicyResult::Unchanged;
}
constructAvailableRefreshRates();
+
+ displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId();
}
- const auto displayId = getActiveMode().getPhysicalDisplayId();
const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u);
ALOGI("Display %s policy changed\n"
@@ -1014,12 +1140,10 @@
return mDisplayManagerPolicy;
}
-bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const {
+bool RefreshRateSelector::isModeAllowed(const FrameRateMode& mode) const {
std::lock_guard lock(mLock);
- return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
- [modeId](DisplayModeIterator modeIt) {
- return modeIt->second->getId() == modeId;
- });
+ return std::find(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(), mode) !=
+ mAppRequestFrameRates.end();
}
void RefreshRateSelector::constructAvailableRefreshRates() {
@@ -1029,33 +1153,35 @@
const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
- const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
- const auto filter = [&](const DisplayMode& mode) {
+ const auto filterRefreshRates = [&](const FpsRanges& ranges,
+ const char* rangeName) REQUIRES(mLock) {
+ const auto filterModes = [&](const DisplayMode& mode) {
return mode.getResolution() == defaultMode->getResolution() &&
mode.getDpi() == defaultMode->getDpi() &&
(policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
- range.includes(mode.getFps());
+ ranges.physical.includes(mode.getFps()) &&
+ (supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
};
- const auto modes = sortByRefreshRate(mDisplayModes, filter);
- LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
- to_string(range).c_str());
+ const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
+ "No matching frame rate modes for %s range. policy: %s", rangeName,
+ policy->toString().c_str());
const auto stringifyModes = [&] {
std::string str;
- for (const auto modeIt : modes) {
- str += to_string(modeIt->second->getFps());
- str.push_back(' ');
+ for (const auto& frameRateMode : frameRateModes) {
+ str += to_string(frameRateMode) + " ";
}
return str;
};
- ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
+ ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str());
- return modes;
+ return frameRateModes;
};
- mPrimaryRefreshRates = filterRefreshRates(policy->primaryRanges.physical, "primary");
- mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRanges.physical, "app request");
+ mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
+ mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
}
Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
@@ -1091,7 +1217,7 @@
}
const DisplayModePtr& maxByPolicy =
- getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+ getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup());
if (minByPolicy == maxByPolicy) {
// Turn on the timer when the min of the primary range is below the device min.
if (const Policy* currentPolicy = getCurrentPolicyLocked();
@@ -1137,8 +1263,8 @@
std::lock_guard lock(mLock);
- const auto activeModeId = getActiveModeItLocked()->first;
- dumper.dump("activeModeId"sv, std::to_string(activeModeId.value()));
+ const auto activeMode = getActiveModeLocked();
+ dumper.dump("activeMode"sv, to_string(activeMode));
dumper.dump("displayModes"sv);
{
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 65ee487..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -24,9 +24,11 @@
#include <ftl/concat.h>
#include <ftl/optional.h>
+#include <ftl/unit.h>
#include <gui/DisplayEventReceiver.h>
#include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/Seamlessness.h>
#include "DisplayHardware/DisplayMode.h"
@@ -58,6 +60,9 @@
static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
std::chrono::nanoseconds(800us).count();
+ // The lowest Render Frame Rate that will ever be selected
+ static constexpr Fps kMinSupportedFrameRate = 20_Hz;
+
class Policy {
static constexpr int kAllowGroupSwitchingDefault = false;
@@ -128,7 +133,7 @@
Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
// Returns true if mode is allowed by the current policy.
- bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock);
+ bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock);
// Describes the different options the layer voted for refresh rate
enum class LayerVoteType {
@@ -196,12 +201,12 @@
}
};
- struct ScoredRefreshRate {
- DisplayModePtr modePtr;
+ struct ScoredFrameRate {
+ FrameRateMode frameRateMode;
float score = 0.0f;
- bool operator==(const ScoredRefreshRate& other) const {
- return modePtr == other.modePtr && score == other.score;
+ bool operator==(const ScoredFrameRate& other) const {
+ return frameRateMode == other.frameRateMode && score == other.score;
}
static bool scoresEqual(float lhs, float rhs) {
@@ -210,39 +215,39 @@
}
struct DescendingScore {
- bool operator()(const ScoredRefreshRate& lhs, const ScoredRefreshRate& rhs) const {
+ bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const {
return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
}
};
};
- using RefreshRateRanking = std::vector<ScoredRefreshRate>;
+ using FrameRateRanking = std::vector<ScoredFrameRate>;
- struct RankedRefreshRates {
- RefreshRateRanking ranking; // Ordered by descending score.
+ struct RankedFrameRates {
+ FrameRateRanking ranking; // Ordered by descending score.
GlobalSignals consideredSignals;
- bool operator==(const RankedRefreshRates& other) const {
+ bool operator==(const RankedFrameRates& other) const {
return ranking == other.ranking && consideredSignals == other.consideredSignals;
}
};
- RankedRefreshRates getRankedRefreshRates(const std::vector<LayerRequirement>&,
- GlobalSignals) const EXCLUDES(mLock);
+ RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
+ EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
}
- std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
- bool timerExpired) const EXCLUDES(mLock);
+ ftl::Optional<FrameRateMode> onKernelTimerChanged(
+ std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+ EXCLUDES(mLock);
- void setActiveModeId(DisplayModeId) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+ void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
- // See mActiveModeIt for thread safety.
- DisplayModePtr getActiveModePtr() const EXCLUDES(mLock);
- const DisplayMode& getActiveMode() const REQUIRES(kMainThreadContext);
+ // See mActiveModeOpt for thread safety.
+ FrameRateMode getActiveMode() const EXCLUDES(mLock);
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
@@ -257,9 +262,12 @@
// Override the frame rate for an app to a value which is also
// a display refresh rate
- EnabledForNativeRefreshRates,
+ AppOverrideNativeRefreshRates,
// Override the frame rate for an app to any value
+ AppOverride,
+
+ // Override the frame rate for all apps and all values.
Enabled,
ftl_last = Enabled
@@ -291,10 +299,13 @@
// Returns whether switching modes (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
- // differ in resolution.
+ // differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default,
+ // we can probably remove canSwitch altogether since all devices will be able
+ // to switch to a frame rate divisor.
bool canSwitch() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
- return mDisplayModes.size() > 1;
+ return mDisplayModes.size() > 1 ||
+ mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
}
// Class to enumerate options around toggling the kernel timer on and off.
@@ -307,10 +318,14 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
- bool supportsFrameRateOverrideByContent() const {
+ bool supportsAppFrameRateOverrideByContent() const {
return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled;
}
+ bool supportsFrameRateOverride() const {
+ return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
+ }
+
// Return the display refresh rate divisor to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
// layer refresh rate.
@@ -384,11 +399,11 @@
void constructAvailableRefreshRates() REQUIRES(mLock);
- // See mActiveModeIt for thread safety.
- DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
+ // See mActiveModeOpt for thread safety.
+ const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
- RankedRefreshRates getRankedRefreshRatesLocked(const std::vector<LayerRequirement>&,
- GlobalSignals) const REQUIRES(mLock);
+ RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const REQUIRES(mLock);
// Returns number of display frames and remainder when dividing the layer refresh period by
// display refresh period.
@@ -404,18 +419,24 @@
struct RefreshRateScoreComparator;
- enum class RefreshRateOrder { Ascending, Descending };
+ enum class RefreshRateOrder {
+ Ascending,
+ Descending,
+
+ ftl_last = Descending
+ };
// Only uses the primary range, not the app request range.
- RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt =
- std::nullopt) const REQUIRES(mLock);
+ FrameRateRanking rankFrameRates(
+ std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
+ REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
// Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
- float calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock);
+ float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock);
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
@@ -436,21 +457,35 @@
: mIdleTimerCallbacks->platform;
}
+ bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) {
+ LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride !=
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+ "should only be called when "
+ "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used");
+ return mAppOverrideNativeRefreshRates.contains(fps);
+ }
+
+ std::vector<FrameRateMode> createFrameRateModes(
+ std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
+ REQUIRES(mLock);
+
// The display modes of the active display. The DisplayModeIterators below are pointers into
// this container, so must be invalidated whenever the DisplayModes change. The Policy below
// is also dependent, so must be reset as well.
DisplayModes mDisplayModes GUARDED_BY(mLock);
- // Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
- // need not be under mLock.
- DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
+ // Set of supported display refresh rates for easy lookup
+ // when FrameRateOverride::AppOverrideNativeRefreshRates is in use.
+ ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
+
+ ftl::Optional<FrameRateMode> mActiveModeOpt GUARDED_BY(mLock);
DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
// Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
- std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
- std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
@@ -466,11 +501,11 @@
const Config mConfig;
Config::FrameRateOverride mFrameRateOverrideConfig;
- struct GetRankedRefreshRatesCache {
+ struct GetRankedFrameRatesCache {
std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
- RankedRefreshRates result;
+ RankedFrameRates result;
};
- mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
+ mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
// Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
std::mutex mIdleTimerCallbacksMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f1fcc88..856fda0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -152,7 +152,7 @@
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -174,7 +174,7 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps;
const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
const auto frameRate = getFrameRateOverride(uid);
@@ -268,7 +268,7 @@
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -282,7 +282,7 @@
thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
}
-void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
// Cache the last reported modes for primary display.
@@ -297,7 +297,7 @@
void Scheduler::dispatchCachedReportedMode() {
// Check optional fields first.
- if (!mPolicy.mode) {
+ if (!mPolicy.modeOpt) {
ALOGW("No mode ID found, not dispatching cached mode.");
return;
}
@@ -309,21 +309,21 @@
// If the mode is not the current mode, this means that a
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
- if (leaderSelectorPtr()->getActiveModePtr() != mPolicy.mode) {
+ if (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
return;
}
// If there is no change from cached mode, there is no need to dispatch an event
- if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
+ if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
return;
}
- mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+ mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
mPolicy.cachedModeChangedParams->mode);
}
-void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -395,6 +395,24 @@
setVsyncPeriod(refreshRate.getPeriodNsecs());
}
+void Scheduler::setRenderRate(Fps renderFrameRate) {
+ const auto mode = leaderSelectorPtr()->getActiveMode();
+
+ using fps_approx_ops::operator!=;
+ LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
+ "Mismatch in render frame rates. Selector: %s, Scheduler: %s",
+ to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str());
+
+ ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
+ to_string(mode.modePtr->getFps()).c_str());
+
+ const auto divisor = RefreshRateSelector::getFrameRateDivisor(mode.modePtr->getFps(), mode.fps);
+ LOG_ALWAYS_FATAL_IF(divisor == 0, "%s <> %s -- not divisors", to_string(mode.fps).c_str(),
+ to_string(mode.fps).c_str());
+
+ mVsyncSchedule->getTracker().setDivisor(static_cast<unsigned>(divisor));
+}
+
void Scheduler::resync() {
static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
@@ -402,7 +420,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- const auto refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
+ const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
resyncToHardwareVsync(false, refreshRate);
}
}
@@ -517,7 +535,7 @@
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
+ const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps();
constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
using namespace fps_approx_ops;
@@ -657,7 +675,7 @@
currentState = std::forward<T>(newState);
DisplayModeChoiceMap modeChoices;
- DisplayModePtr modePtr;
+ ftl::Optional<FrameRateMode> modeOpt;
{
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
@@ -666,10 +684,10 @@
// TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
// to go through. Fix this by tracking per-display Scheduler::Policy and timers.
- std::tie(modePtr, consideredSignals) =
+ std::tie(modeOpt, consideredSignals) =
modeChoices.get(*mLeaderDisplayId)
.transform([](const DisplayModeChoice& choice) {
- return std::make_pair(choice.modePtr, choice.consideredSignals);
+ return std::make_pair(choice.mode, choice.consideredSignals);
})
.value();
}
@@ -677,15 +695,14 @@
modeRequests.reserve(modeChoices.size());
for (auto& [id, choice] : modeChoices) {
modeRequests.emplace_back(
- display::DisplayModeRequest{.modePtr =
- ftl::as_non_null(std::move(choice.modePtr)),
+ display::DisplayModeRequest{.mode = std::move(choice.mode),
.emitEvent = !choice.consideredSignals.idle});
}
- frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modePtr->getFps());
+ frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps);
- if (mPolicy.mode != modePtr) {
- mPolicy.mode = modePtr;
+ if (mPolicy.modeOpt != modeOpt) {
+ mPolicy.modeOpt = modeOpt;
refreshRateChanged = true;
} else {
// We don't need to change the display mode, but we might need to send an event
@@ -707,7 +724,7 @@
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
ATRACE_CALL();
- using RankedRefreshRates = RefreshRateSelector::RankedRefreshRates;
+ using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
// Tallies the score of a refresh rate across `displayCount` displays.
@@ -725,11 +742,11 @@
const auto globalSignals = makeGlobalSignals();
for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
- auto rankedRefreshRates =
- selectorPtr->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals);
+ auto rankedFrameRates =
+ selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
- for (const auto& [modePtr, score] : rankedRefreshRates.ranking) {
- const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score);
+ for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
+ const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);
if (!inserted) {
auto& tally = it->second;
@@ -738,7 +755,7 @@
}
}
- perDisplayRanking.push_back(std::move(rankedRefreshRates));
+ perDisplayRanking.push_back(std::move(rankedFrameRates));
}
auto maxScoreIt = refreshRateTallies.cbegin();
@@ -771,16 +788,16 @@
for (auto& [ranking, signals] : perDisplayRanking) {
if (!chosenFps) {
- auto& [modePtr, _] = ranking.front();
- modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{std::move(modePtr), signals});
+ const auto& [frameRateMode, _] = ranking.front();
+ modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{frameRateMode, signals});
continue;
}
- for (auto& [modePtr, _] : ranking) {
- if (modePtr->getFps() == *chosenFps) {
- modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{std::move(modePtr), signals});
+ for (auto& [frameRateMode, _] : ranking) {
+ if (frameRateMode.fps == *chosenFps) {
+ modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{frameRateMode, signals});
break;
}
}
@@ -798,18 +815,18 @@
.powerOnImminent = powerOnImminent};
}
-DisplayModePtr Scheduler::getPreferredDisplayMode() {
+FrameRateMode Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
- // Make sure the stored mode is up to date.
- if (mPolicy.mode) {
- const auto ranking =
- leaderSelectorPtr()
- ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
- .ranking;
+ const auto frameRateMode =
+ leaderSelectorPtr()
+ ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
+ .ranking.front()
+ .frameRateMode;
- mPolicy.mode = ranking.front().modePtr;
- }
- return mPolicy.mode;
+ // Make sure the stored mode is up to date.
+ mPolicy.modeOpt = frameRateMode;
+
+ return frameRateMode;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index fb23071..f189426 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -149,8 +149,8 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
- void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
+ void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -161,6 +161,9 @@
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
+ // Sets the render rate for the scheduler to run at.
+ void setRenderRate(Fps);
+
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
@@ -206,8 +209,8 @@
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
- // Get the appropriate refresh for current conditions.
- DisplayModePtr getPreferredDisplayMode();
+ // Returns the preferred refresh rate and frame rate for the leader display.
+ FrameRateMode getPreferredDisplayMode();
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -235,7 +238,7 @@
std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
- return leaderSelectorPtr()->getActiveModePtr()->getFps().getPeriodNsecs();
+ return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs();
}
// Returns the framerate of the layer with the given sequence ID
@@ -283,19 +286,19 @@
GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
struct DisplayModeChoice {
- DisplayModeChoice(DisplayModePtr modePtr, GlobalSignals consideredSignals)
- : modePtr(std::move(modePtr)), consideredSignals(consideredSignals) {}
+ DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
+ : mode(std::move(mode)), consideredSignals(consideredSignals) {}
- DisplayModePtr modePtr;
+ FrameRateMode mode;
GlobalSignals consideredSignals;
bool operator==(const DisplayModeChoice& other) const {
- return modePtr == other.modePtr && consideredSignals == other.consideredSignals;
+ return mode == other.mode && consideredSignals == other.consideredSignals;
}
// For tests.
friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) {
- return stream << '{' << to_string(*choice.modePtr) << " considering "
+ return stream << '{' << to_string(*choice.mode.modePtr) << " considering "
<< choice.consideredSignals.toString().c_str() << '}';
}
};
@@ -382,11 +385,11 @@
hal::PowerMode displayPowerMode = hal::PowerMode::ON;
// Chosen display mode.
- DisplayModePtr mode;
+ ftl::Optional<FrameRateMode> modeOpt;
struct ModeChangedParams {
ConnectionHandle handle;
- DisplayModePtr mode;
+ FrameRateMode mode;
};
// Parameters for latest dispatch of mode change event.
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 0ad4236..ed4d25e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -253,7 +253,13 @@
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard lock(mMutex);
- return nextAnticipatedVSyncTimeFromLocked(timePoint);
+
+ // TODO(b/246164114): This implementation is not efficient at all. Refactor.
+ nsecs_t nextVsync = nextAnticipatedVSyncTimeFromLocked(timePoint);
+ while (!isVSyncInPhaseLocked(nextVsync, mDivisor)) {
+ nextVsync = nextAnticipatedVSyncTimeFromLocked(nextVsync + 1);
+ }
+ return nextVsync;
}
/*
@@ -265,6 +271,13 @@
* isVSyncInPhase(50.0, 30) = true
*/
bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
+ std::lock_guard lock(mMutex);
+ const auto divisor =
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+ return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
+}
+
+bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
struct VsyncError {
nsecs_t vsyncTimestamp;
float error;
@@ -272,9 +285,6 @@
bool operator<(const VsyncError& other) const { return error < other.error; }
};
- std::lock_guard lock(mMutex);
- const auto divisor =
- RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
if (divisor <= 1 || timePoint == 0) {
return true;
}
@@ -312,6 +322,12 @@
return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
}
+void VSyncPredictor::setDivisor(unsigned divisor) {
+ ALOGV("%s: %d", __func__, divisor);
+ std::lock_guard lock(mMutex);
+ mDivisor = divisor;
+}
+
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
std::lock_guard lock(mMutex);
const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3181102..4a3ba67 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -67,6 +67,8 @@
bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
+ void setDivisor(unsigned divisor) final EXCLUDES(mMutex);
+
void dump(std::string& result) const final EXCLUDES(mMutex);
private:
@@ -89,6 +91,8 @@
nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
+ bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
+
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
@@ -100,6 +104,8 @@
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
+
+ unsigned mDivisor GUARDED_BY(mMutex) = 1;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 76315d2..8d1629f 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -79,6 +79,17 @@
*/
virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
+ /*
+ * Sets a divisor on the rate (which is a multiplier of the period).
+ * The tracker will continue to track the vsync timeline and expect it
+ * to match the current period, however, nextAnticipatedVSyncTimeFrom will
+ * return vsyncs according to the divisor set. Setting a divisor is useful
+ * when a display is running at 120Hz but the render frame rate is 60Hz.
+ *
+ * \param [in] divisor The rate divisor the tracker should operate at.
+ */
+ virtual void setDivisor(unsigned divisor) = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 31b1d69..5522ff8 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -66,6 +66,7 @@
Fps max = Fps::fromValue(std::numeric_limits<float>::max());
bool includes(Fps) const;
+ bool includes(FpsRange) const;
};
struct FpsRanges {
@@ -75,6 +76,8 @@
// the range of frame rates that refers to the render rate, which is
// the rate that frames are swapped.
FpsRange render;
+
+ bool valid() const;
};
static_assert(std::is_trivially_copyable_v<Fps>);
@@ -159,6 +162,16 @@
return min <= fps && fps <= max;
}
+inline bool FpsRange::includes(FpsRange range) const {
+ using namespace fps_approx_ops;
+ return min <= range.min && max >= range.max;
+}
+
+inline bool FpsRanges::valid() const {
+ using fps_approx_ops::operator>=;
+ return physical.max >= render.max;
+}
+
struct FpsApproxEqual {
bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
};
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
new file mode 100644
index 0000000..db38ebe
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/non_null.h>
+#include <scheduler/Fps.h>
+
+// TODO(b/241285191): Pull this to <ui/DisplayMode.h>
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::scheduler {
+
+struct FrameRateMode {
+ Fps fps; // The render frame rate, which is a divisor of modePtr->getFps().
+ ftl::NonNull<DisplayModePtr> modePtr;
+
+ bool operator==(const FrameRateMode& other) const {
+ return isApproxEqual(fps, other.fps) && modePtr == other.modePtr;
+ }
+
+ bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+};
+
+inline std::string to_string(const FrameRateMode& mode) {
+ return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc9129c..3dd86aa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -112,6 +112,7 @@
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
+#include "ClientCache.h"
#include "Colorizer.h"
#include "Display/DisplayMap.h"
#include "DisplayDevice.h"
@@ -180,6 +181,7 @@
using base::StringAppendF;
using display::PhysicalDisplay;
using display::PhysicalDisplays;
+using frontend::TransactionHandler;
using gui::DisplayInfo;
using gui::GameMode;
using gui::IDisplayEventConnection;
@@ -197,6 +199,9 @@
namespace {
+static constexpr int FOUR_K_WIDTH = 3840;
+static constexpr int FOUR_K_HEIGHT = 2160;
+
// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
@@ -244,6 +249,44 @@
return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
}
+bool isAbove4k30(const ui::DisplayMode& outMode) {
+ using fps_approx_ops::operator>;
+ Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+ return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
+ outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
+}
+
+void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
+ ui::DisplayMode& outMode) {
+ if (isAbove4k30(outMode) &&
+ std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ } else {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ }
+}
+
+HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
+ std::vector<ui::Hdr> hdrTypes;
+ for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ hdrTypes.push_back(type);
+ }
+ }
+ return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
+ displayHdrCapabilities.getDesiredMaxAverageLuminance(),
+ displayHdrCapabilities.getDesiredMinLuminance()};
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -256,6 +299,7 @@
const String16 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
+const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
@@ -289,20 +333,12 @@
}
}
-bool callingThreadHasRotateSurfaceFlingerAccess() {
+bool callingThreadHasPermission(const String16& permission) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
-}
-
-bool callingThreadHasInternalSystemWindowAccess() {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(sInternalSystemWindow, pid, uid);
+ PermissionCache::checkPermission(permission, pid, uid);
}
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
@@ -383,8 +419,6 @@
bool supportsBlurs = atoi(value);
mSupportsBlur = supportsBlurs;
ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
- property_get("ro.sf.blurs_are_expensive", value, "0");
- mBlursAreExpensive = atoi(value);
const size_t defaultListSize = MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
@@ -406,6 +440,9 @@
property_get("debug.sf.treat_170m_as_sRGB", value, "0");
mTreat170mAsSrgb = atoi(value);
+ mIgnoreHwcPhysicalDisplayOrientation =
+ base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
+
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
@@ -421,7 +458,9 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlayRenderRate =
+ property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
mTransactionTracing.emplace();
@@ -845,8 +884,6 @@
}
}
- onActiveDisplaySizeChanged(display);
-
// Inform native graphics APIs whether the present timestamp is supported:
const bool presentFenceReliable =
@@ -932,17 +969,14 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
- ui::StaticDisplayInfo* info) {
- if (!displayToken || !info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
+ if (!info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
@@ -969,26 +1003,10 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
- ui::DynamicDisplayInfo* info) {
- if (!displayToken || !info) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
- if (!displayOpt) {
- return NAME_NOT_FOUND;
- }
-
- const auto& [display, snapshotRef] = *displayOpt;
- const auto& snapshot = snapshotRef.get();
-
+void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
+ const sp<DisplayDevice>& display,
+ const display::DisplaySnapshot& snapshot) {
const auto& displayModes = snapshot.displayModes();
-
info->supportedDisplayModes.clear();
info->supportedDisplayModes.reserve(displayModes.size());
@@ -1032,7 +1050,8 @@
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
-
+ excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
+ outMode);
info->supportedDisplayModes.push_back(outMode);
}
@@ -1040,9 +1059,11 @@
const PhysicalDisplayId displayId = snapshot.displayId();
- info->activeDisplayModeId = display->refreshRateSelector().getActiveModePtr()->getId().value();
+ const auto mode = display->refreshRateSelector().getActiveMode();
+ info->activeDisplayModeId = mode.modePtr->getId().value();
+ info->renderFrameRate = mode.fps.getValue();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
- info->hdrCapabilities = display->getHdrCapabilities();
+ info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
info->autoLowLatencyModeSupported =
getHwComposer().hasDisplayCapability(displayId,
@@ -1059,7 +1080,47 @@
}
}
}
+}
+status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
+ ui::DynamicDisplayInfo* info) {
+ if (!info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto id_ =
+ DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* info) {
+ if (!displayToken || !info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+ .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+ .and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
return NO_ERROR;
}
@@ -1074,29 +1135,44 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
+void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
ATRACE_CALL();
- auto display = getDisplayDeviceLocked(request.modePtr->getPhysicalDisplayId());
+ auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
if (!display) {
ALOGW("%s: display is no longer valid", __func__);
return;
}
- const Fps refreshRate = request.modePtr->getFps();
+ const auto mode = request.mode;
+ const bool emitEvent = request.emitEvent;
- if (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
- scheduleComposite(FrameHint::kNone);
+ switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)),
+ force)) {
+ case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
+ scheduleComposite(FrameHint::kNone);
- // Start receiving vsync samples now, so that we can detect a period
- // switch.
- mScheduler->resyncToHardwareVsync(true, refreshRate);
- // As we called to set period, we will call to onRefreshRateChangeCompleted once
- // VsyncController model is locked.
- modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+ // Start receiving vsync samples now, so that we can detect a period
+ // switch.
+ mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
+ // As we called to set period, we will call to onRefreshRateChangeCompleted once
+ // VsyncController model is locked.
+ modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- updatePhaseConfiguration(refreshRate);
- mScheduler->setModeChangePending(true);
+ updatePhaseConfiguration(mode.fps);
+ mScheduler->setModeChangePending(true);
+ break;
+ case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
+ mScheduler->setRenderRate(mode.fps);
+ updatePhaseConfiguration(mode.fps);
+ mRefreshRateStats->setRefreshRate(mode.fps);
+ if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+ }
+
+ break;
+ case DisplayDevice::DesiredActiveModeAction::None:
+ break;
}
}
@@ -1158,18 +1234,19 @@
}
const auto upcomingModeInfo = display->getUpcomingActiveMode();
- if (!upcomingModeInfo.mode) {
+ if (!upcomingModeInfo.modeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
return;
}
- if (display->getActiveMode().getResolution() != upcomingModeInfo.mode->getResolution()) {
+ if (display->getActiveMode().modePtr->getResolution() !=
+ upcomingModeInfo.modeOpt->modePtr->getResolution()) {
auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
- state.physical->activeMode = upcomingModeInfo.mode;
+ state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get();
processDisplayChangesLocked();
// processDisplayChangesLocked will update all necessary components so we're done here.
@@ -1180,15 +1257,17 @@
.transform(&PhysicalDisplay::snapshotRef)
.transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(upcomingModeInfo.mode->getId(), snapshot));
+ display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(),
+ upcomingModeInfo.modeOpt->modePtr->getFps(),
+ upcomingModeInfo.modeOpt->fps));
}));
- const Fps refreshRate = upcomingModeInfo.mode->getFps();
+ const Fps refreshRate = upcomingModeInfo.modeOpt->fps;
mRefreshRateStats->setRefreshRate(refreshRate);
updatePhaseConfiguration(refreshRate);
if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt);
}
}
@@ -1200,10 +1279,12 @@
}
void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
- const auto refreshRate = display->getDesiredActiveMode()->mode->getFps();
+ const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps();
+ const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps;
clearDesiredActiveModeState(display);
- mScheduler->resyncToHardwareVsync(true, refreshRate);
- updatePhaseConfiguration(refreshRate);
+ mScheduler->resyncToHardwareVsync(true, displayFps);
+ mScheduler->setRenderRate(renderFps);
+ updatePhaseConfiguration(renderFps);
}
void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
@@ -1234,13 +1315,10 @@
continue;
}
- const auto desiredModeId = desiredActiveMode->mode->getId();
- const auto refreshRateOpt =
- snapshot.displayModes()
- .get(desiredModeId)
- .transform([](const DisplayModePtr& mode) { return mode->getFps(); });
+ const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
+ const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId);
- if (!refreshRateOpt) {
+ if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
desiredModeId.value());
clearDesiredActiveModeState(display);
@@ -1248,9 +1326,10 @@
}
ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
- to_string(*refreshRateOpt).c_str(), to_string(display->getId()).c_str());
+ to_string(displayModePtrOpt->get()->getFps()).c_str(),
+ to_string(display->getId()).c_str());
- if (display->getActiveMode().getId() == desiredModeId) {
+ if (display->getActiveMode() == desiredActiveMode->modeOpt) {
// we are already in the requested mode, there is nothing left to do
desiredActiveModeChangeDone(display);
continue;
@@ -1259,7 +1338,8 @@
// Desired active mode was set, it is different than the mode currently in use, however
// allowed modes might have changed by the time we process the refresh.
// Make sure the desired mode is still allowed
- const auto displayModeAllowed = display->refreshRateSelector().isModeAllowed(desiredModeId);
+ const auto displayModeAllowed =
+ display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt);
if (!displayModeAllowed) {
clearDesiredActiveModeState(display);
continue;
@@ -1300,8 +1380,7 @@
const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
const auto desiredActiveMode = display->getDesiredActiveMode();
- if (desiredActiveMode &&
- display->getActiveMode().getId() == desiredActiveMode->mode->getId()) {
+ if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
desiredActiveModeChangeDone(display);
}
}
@@ -1393,7 +1472,26 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* /*outProperties*/) const {
+status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const {
+ const auto& aidlProperties = getHwComposer().getOverlaySupport();
+ // convert aidl OverlayProperties to gui::OverlayProperties
+ outProperties->combinations.reserve(aidlProperties.combinations.size());
+ for (const auto& combination : aidlProperties.combinations) {
+ std::vector<int32_t> pixelFormats;
+ pixelFormats.reserve(combination.pixelFormats.size());
+ std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(),
+ std::back_inserter(pixelFormats),
+ [](const auto& val) { return static_cast<int32_t>(val); });
+ std::vector<int32_t> dataspaces;
+ dataspaces.reserve(combination.dataspaces.size());
+ std::transform(combination.dataspaces.cbegin(), combination.dataspaces.cend(),
+ std::back_inserter(dataspaces),
+ [](const auto& val) { return static_cast<int32_t>(val); });
+ gui::OverlayProperties::SupportedBufferCombinations outCombination;
+ outCombination.pixelFormats = std::move(pixelFormats);
+ outCombination.dataspaces = std::move(dataspaces);
+ outProperties->combinations.emplace_back(outCombination);
+ }
return NO_ERROR;
}
@@ -1477,7 +1575,8 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
+ bool* success) {
*success = mTimeStats->onPullAtom(atomId, pulledData);
return NO_ERROR;
}
@@ -1805,7 +1904,7 @@
if (hint == FrameHint::kActive) {
mScheduler->resetIdleTimer();
}
- mPowerAdvisor->notifyDisplayUpdateImminent();
+ mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
mScheduler->scheduleFrame();
}
@@ -2051,7 +2150,7 @@
activeDisplay->getPowerMode() == hal::PowerMode::ON;
if (mPowerHintSessionEnabled) {
const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
- const Period vsyncPeriod = Period::fromNs(display->getActiveMode().getVsyncPeriod());
+ const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs());
mPowerAdvisor->setCommitStart(frameTime);
mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
@@ -2176,6 +2275,8 @@
});
}
+ refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
+
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
@@ -2190,17 +2291,15 @@
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
- std::vector<sp<Layer>> layers;
+ std::vector<Layer*> layers;
mDrawingState.traverseInZOrder([&refreshArgs, &layers](Layer* layer) {
- auto strongLayer = sp<Layer>::fromExisting(layer);
if (auto layerFE = layer->getCompositionEngineLayerFE()) {
layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
refreshArgs.layers.push_back(layerFE);
- layers.push_back(std::move(strongLayer));
+ layers.push_back(layer);
}
});
- refreshArgs.blursAreExpensive = mBlursAreExpensive;
refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
@@ -2229,8 +2328,8 @@
{
std::vector<LayerSnapshotGuard> layerSnapshotGuards;
- for (auto& layer : layers) {
- layerSnapshotGuards.emplace_back(layer.get());
+ for (Layer* layer : layers) {
+ layerSnapshotGuards.emplace_back(layer);
}
mCompositionEngine->present(refreshArgs);
}
@@ -2346,7 +2445,8 @@
if (!id) {
return ui::ROTATION_0;
}
- if (getHwComposer().getComposer()->isSupported(
+ if (!mIgnoreHwcPhysicalDisplayOrientation &&
+ getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
case Hwc2::AidlTransform::ROT_90:
@@ -2793,7 +2893,11 @@
}
if (sysprop::frame_rate_override_for_native_rates(true)) {
- return Config::FrameRateOverride::EnabledForNativeRefreshRates;
+ return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
+ }
+
+ if (!sysprop::frame_rate_override_global(false)) {
+ return Config::FrameRateOverride::AppOverride;
}
return Config::FrameRateOverride::Enabled;
@@ -2877,7 +2981,9 @@
.transform(&PhysicalDisplay::snapshotRef)
.transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
FTL_FAKE_GUARD(kMainThreadContext,
- display->setActiveMode(physical->activeMode->getId(), snapshot));
+ display->setActiveMode(physical->activeMode->getId(),
+ physical->activeMode->getFps(),
+ physical->activeMode->getFps()));
}));
}
@@ -3073,7 +3179,7 @@
void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
mVsyncConfiguration->reset();
- const Fps refreshRate = activeDisplay->getActiveMode().getFps();
+ const Fps refreshRate = activeDisplay->getActiveMode().fps;
updatePhaseConfiguration(refreshRate);
mRefreshRateStats->setRefreshRate(refreshRate);
}
@@ -3124,6 +3230,10 @@
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
if (displayTransactionNeeded) {
processDisplayChangesLocked();
+ mFrontEndDisplayInfos.clear();
+ for (const auto& [_, display] : mDisplays) {
+ mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
+ }
}
mForceTransactionDisplayChange = displayTransactionNeeded;
@@ -3293,29 +3403,6 @@
void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
std::vector<DisplayInfo>& outDisplayInfos) {
- display::DisplayMap<ui::LayerStack, DisplayDevice::InputInfo> displayInputInfos;
-
- for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
- const auto layerStack = display->getLayerStack();
- const auto info = display->getInputInfo();
-
- const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info);
- if (emplaced) {
- continue;
- }
-
- // If the layer stack is mirrored on multiple displays, the first display that is configured
- // to receive input takes precedence.
- auto& otherInfo = it->second;
- if (otherInfo.receivesInput) {
- ALOGW_IF(display->receivesInput(),
- "Multiple displays claim to accept input for the same layer stack: %u",
- layerStack.id);
- } else {
- otherInfo = info;
- }
- }
-
static size_t sNumWindowInfos = 0;
outWindowInfos.reserve(sNumWindowInfos);
sNumWindowInfos = 0;
@@ -3323,8 +3410,8 @@
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- const auto opt = displayInputInfos.get(layer->getLayerStack())
- .transform([](const DisplayDevice::InputInfo& info) {
+ const auto opt = mFrontEndDisplayInfos.get(layer->getLayerStack())
+ .transform([](const frontend::DisplayInfo& info) {
return Layer::InputDisplayArgs{&info.transform, info.isSecure};
});
@@ -3333,8 +3420,8 @@
sNumWindowInfos = outWindowInfos.size();
- outDisplayInfos.reserve(displayInputInfos.size());
- for (const auto& [_, info] : displayInputInfos) {
+ outDisplayInfos.reserve(mFrontEndDisplayInfos.size());
+ for (const auto& [_, info] : mFrontEndDisplayInfos) {
outDisplayInfos.push_back(info.info);
}
}
@@ -3349,12 +3436,7 @@
std::vector<LayerSnapshotGuard> layerSnapshotGuards;
mDrawingState.traverse([&layerSnapshotGuards](Layer* layer) {
- auto strongLayer = sp<Layer>::fromExisting(layer);
- const LayerSnapshot* snapshot = layer->getLayerSnapshot();
- if (!snapshot) {
- LOG_ALWAYS_FATAL("Layer snapshot unexpectedly null");
- }
- if (snapshot->compositionType ==
+ if (layer->getLayerSnapshot()->compositionType ==
aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
layer->updateSnapshot(false /* updateGeometry */);
layerSnapshotGuards.emplace_back(layer);
@@ -3379,12 +3461,23 @@
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
for (auto& request : modeRequests) {
- const auto& modePtr = request.modePtr;
- const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());
+ const auto& modePtr = request.mode.modePtr;
+
+ const auto displayId = modePtr->getPhysicalDisplayId();
+ const auto display = getDisplayDeviceLocked(displayId);
if (!display) continue;
- if (display->refreshRateSelector().isModeAllowed(modePtr->getId())) {
+ const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+
+ if (isInternalDisplay && displayId != mActiveDisplayId) {
+ ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ continue;
+ }
+
+ if (display->refreshRateSelector().isModeAllowed(request.mode)) {
setDesiredActiveMode(std::move(request));
} else {
ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
@@ -3405,8 +3498,8 @@
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
LOG_ALWAYS_FATAL_IF(mScheduler);
- const auto activeModePtr = display->refreshRateSelector().getActiveModePtr();
- const Fps activeRefreshRate = activeModePtr->getFps();
+ const auto activeMode = display->refreshRateSelector().getActiveMode();
+ const Fps activeRefreshRate = activeMode.fps;
mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
hal::PowerMode::OFF);
@@ -3458,14 +3551,6 @@
sp<RegionSamplingThread>::make(*this,
RegionSamplingThread::EnvironmentTimingTunables());
mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
- // Dispatch a mode change request for the primary display on scheduler
- // initialization, so that the EventThreads always contain a reference to a
- // prior configuration.
- //
- // This is a bit hacky, but this avoids a back-pointer into the main SF
- // classes from EventThread, and there should be no run-time binder cost
- // anyway since there are no connected apps at this point.
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
}
void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3913,18 +3998,23 @@
// Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
// permissions.
if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
- callingThreadHasRotateSurfaceFlingerAccess()) {
+ callingThreadHasPermission(sRotateSurfaceFlinger)) {
permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER;
}
- if (callingThreadHasInternalSystemWindowAccess()) {
+ if (callingThreadHasPermission(sInternalSystemWindow)) {
permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW;
}
- if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) &&
- (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
- flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+ const bool hasPermission =
+ (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
+ callingThreadHasPermission(sWakeupSurfaceFlinger);
+ if (!hasPermission) {
+ ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
+ "eEarlyWakeup[Start|End] flags");
+ flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ }
}
const int64_t postTime = systemTime();
@@ -3947,10 +4037,6 @@
getExternalTextureFromBufferData(*resolvedState.state.bufferData,
layerName.c_str(), transactionId);
mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
- if (layer) {
- resolvedState.hwcBufferSlot =
- layer->getHwcCacheSlot(resolvedState.state.bufferData->cachedBuffer);
- }
}
}
@@ -3982,7 +4068,7 @@
bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
std::vector<ResolvedComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer,
@@ -3991,7 +4077,8 @@
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
- for (const DisplayState& display : displays) {
+ for (DisplayState& display : displays) {
+ display.sanitize(permissions);
transactionFlags |= setDisplayStateLocked(display);
}
@@ -4026,7 +4113,10 @@
}
if (uncacheBuffer.isValid()) {
- ClientCache::getInstance().erase(uncacheBuffer);
+ sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+ if (buffer != nullptr) {
+ mBufferIdsToUncache.push_back(buffer->getId());
+ }
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4417,7 +4507,7 @@
if (what & layer_state_t::eBufferChanged) {
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo, composerState.hwcBufferSlot)) {
+ frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -4648,8 +4738,6 @@
{}, mPid, getuid(), transactionId);
setPowerModeInternal(display, hal::PowerMode::ON);
-
- mActiveDisplayTransformHint = display->getTransformHint();
}
void SurfaceFlinger::initializeDisplays() {
@@ -4668,8 +4756,8 @@
const auto displayId = display->getPhysicalId();
ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
- std::optional<hal::PowerMode> currentMode = display->getPowerMode();
- if (currentMode.has_value() && mode == *currentMode) {
+ const auto currentModeOpt = display->getPowerMode();
+ if (currentModeOpt == mode) {
return;
}
@@ -4679,19 +4767,34 @@
.value_or(false);
const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
- if (isInternalDisplay && activeDisplay != display && activeDisplay &&
- activeDisplay->isPoweredOn()) {
- ALOGW("Trying to change power mode on non active display while the active display is ON");
- }
+
+ ALOGW_IF(display != activeDisplay && isInternalDisplay && activeDisplay &&
+ activeDisplay->isPoweredOn(),
+ "Trying to change power mode on inactive display without powering off active display");
display->setPowerMode(mode);
- const auto refreshRate = display->refreshRateSelector().getActiveMode().getFps();
- if (*currentMode == hal::PowerMode::OFF) {
+ const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
+ if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
// Turn on the display
- if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
- onActiveDisplayChangedLocked(display);
+
+ // Activate the display (which involves a modeset to the active mode):
+ // 1) When the first (a.k.a. primary) display is powered on during boot.
+ // 2) When the inner or outer display of a foldable is powered on. This condition relies
+ // on the above DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the
+ // same display, then the `activeDisplay->isPoweredOn()` below is true, such that the
+ // display is not activated every time it is powered on.
+ //
+ // TODO(b/255635821): Remove the concept of active display.
+ const bool activeDisplayChanged =
+ isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn());
+
+ static bool sPrimaryDisplay = true;
+ if (sPrimaryDisplay || activeDisplayChanged) {
+ onActiveDisplayChangedLocked(activeDisplay, display);
+ sPrimaryDisplay = false;
}
+
// Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
// We can merge the syscall later.
if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
@@ -4717,7 +4820,7 @@
if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
}
- if (isActiveDisplay && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
+ if (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
mScheduler->disableHardwareVsync(true);
mScheduler->onScreenReleased(mAppConnectionHandle);
}
@@ -4731,7 +4834,7 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (isActiveDisplay && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
+ if (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
mVisibleRegionsDirty = true;
scheduleRepaint();
@@ -5144,7 +5247,7 @@
std::string result;
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+ [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
}
return result;
});
@@ -5256,7 +5359,8 @@
if (const auto display = getDefaultDisplayDeviceLocked()) {
std::string fps, xDpi, yDpi;
- if (const auto activeModePtr = display->refreshRateSelector().getActiveModePtr()) {
+ if (const auto activeModePtr =
+ display->refreshRateSelector().getActiveMode().modePtr.get()) {
fps = to_string(activeModePtr->getFps());
const auto dpi = activeModePtr->getDpi();
@@ -5928,7 +6032,7 @@
if (!updateOverlay) return;
// Update the overlay on the main thread to avoid race conditions with
- // RefreshRateSelector::getActiveMode.
+ // RefreshRateSelector::getActiveMode
static_cast<void>(mScheduler->schedule([=] {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
@@ -5939,7 +6043,8 @@
const auto desiredActiveMode = display->getDesiredActiveMode();
const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
- ? std::make_optional(desiredActiveMode->mode->getId())
+ ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId())
+
: std::nullopt;
const bool timerExpired = mKernelIdleTimerEnabled && expired;
@@ -6411,8 +6516,10 @@
std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
if (!renderArea) {
ALOGW("Skipping screen capture because of invalid render area.");
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
- captureListener->onScreenCaptureCompleted(captureResults);
+ if (captureListener) {
+ captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ }
return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
}
@@ -6498,7 +6605,6 @@
layers.reserve(layerCount);
std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
traverseLayers([&](Layer* layer) {
- auto strongLayer = sp<Layer>::fromExisting(layer);
captureResults.capturedHdrLayers |= isHdrLayer(layer);
// Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
// LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
@@ -6632,11 +6738,11 @@
}
}
-std::optional<ftl::NonNull<DisplayModePtr>> SurfaceFlinger::getPreferredDisplayMode(
+ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
- schedulerMode && schedulerMode->getPhysicalDisplayId() == displayId) {
- return ftl::as_non_null(schedulerMode);
+ schedulerMode.modePtr->getPhysicalDisplayId() == displayId) {
+ return schedulerMode;
}
return mPhysicalDisplays.get(displayId)
@@ -6644,7 +6750,9 @@
.and_then([&](const display::DisplaySnapshot& snapshot) {
return snapshot.displayModes().get(defaultModeId);
})
- .transform(&ftl::as_non_null<const DisplayModePtr&>);
+ .transform([](const DisplayModePtr& modePtr) {
+ return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+ });
}
status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
@@ -6671,16 +6779,31 @@
break;
}
+ const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+
+ if (isInternalDisplay && displayId != mActiveDisplayId) {
+ // The policy will be be applied when the display becomes active.
+ ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ return NO_ERROR;
+ }
+
+ return applyRefreshRateSelectorPolicy(displayId, selector);
+}
+
+status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
+ PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
// TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
// be depending in this callback.
- if (const auto activeModePtr = selector.getActiveModePtr(); displayId == mActiveDisplayId) {
- mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
+ if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
toggleKernelIdleTimer();
} else {
- mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeModePtr);
+ mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
}
auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -6690,17 +6813,17 @@
}
auto preferredMode = std::move(*preferredModeOpt);
- const auto preferredModeId = preferredMode->getId();
+ const auto preferredModeId = preferredMode.modePtr->getId();
ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
- to_string(preferredMode->getFps()).c_str());
+ to_string(preferredMode.fps).c_str());
- if (!selector.isModeAllowed(preferredModeId)) {
+ if (!selector.isModeAllowed(preferredMode)) {
ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), .emitEvent = true});
+ setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
return NO_ERROR;
}
@@ -6868,7 +6991,8 @@
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+ device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate);
}
}
}
@@ -6907,7 +7031,7 @@
refreshRate = *frameRateOverride;
} else if (!getHwComposer().isHeadless()) {
if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
- refreshRate = display->refreshRateSelector().getActiveModePtr()->getFps();
+ refreshRate = display->refreshRateSelector().getActiveMode().fps;
}
}
@@ -6981,17 +7105,14 @@
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
}
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay) {
ATRACE_CALL();
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
- display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+ if (inactiveDisplay) {
+ inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
}
- if (!activeDisplay) {
- ALOGE("%s: activeDisplay is null", __func__);
- return;
- }
mActiveDisplayId = activeDisplay->getPhysicalId();
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
@@ -7002,9 +7123,12 @@
onActiveDisplaySizeChanged(activeDisplay);
mActiveDisplayTransformHint = activeDisplay->getTransformHint();
- // Update the kernel timer for the current active display, since the policy
- // for this display might have changed when it was not the active display.
- toggleKernelIdleTimer();
+ // The policy of the new active/leader display may have changed while it was inactive. In that
+ // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
+ // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
+ // and the kernel idle timer of the newly active display must be toggled.
+ constexpr bool kForce = true;
+ applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector(), kForce);
}
status_t SurfaceFlinger::addWindowInfosListener(
@@ -7030,8 +7154,7 @@
layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
ALOGD("%s", errorMessage.c_str());
if (bufferData.releaseBufferListener) {
- bufferData.releaseBufferListener->onTransactionQueueStalled(
- String8(errorMessage.c_str()));
+ bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage);
}
return nullptr;
}
@@ -7049,7 +7172,7 @@
if (bufferData.releaseBufferListener) {
bufferData.releaseBufferListener->onTransactionQueueStalled(
- String8("Buffer processing hung due to full buffer cache"));
+ "Buffer processing hung due to full buffer cache");
}
}
@@ -7223,6 +7346,10 @@
binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId,
sp<IBinder>* outDisplay) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
*outDisplay = mFlinger->getPhysicalDisplayToken(*id);
return binder::Status::ok();
@@ -7273,11 +7400,12 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(const sp<IBinder>& display,
+binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) {
using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
ui::StaticDisplayInfo info;
- status_t status = mFlinger->getStaticDisplayInfo(display, &info);
+
+ status_t status = mFlinger->getStaticDisplayInfo(displayId, &info);
if (status == NO_ERROR) {
// convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
@@ -7316,53 +7444,71 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo) {
+ // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
+ for (const auto& mode : info.supportedDisplayModes) {
+ gui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const ui::Hdr& value) { return static_cast<int32_t>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = info.activeDisplayModeId;
+ outInfo->renderFrameRate = info.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
+ for (const auto& cmode : info.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
+
+ gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
+ hdrCapabilities.supportedHdrTypes.clear();
+ hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size());
+ for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
+ hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
+ }
+ hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
+ hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
+ hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
+
+ outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken(
+ const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) {
ui::DynamicDisplayInfo info;
- status_t status = mFlinger->getDynamicDisplayInfo(display, &info);
+ status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info);
if (status == NO_ERROR) {
- // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
- for (const auto& mode : info.supportedDisplayModes) {
- gui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(info, outInfo);
+ }
+ return binderStatusFromStatusT(status);
+}
- outInfo->activeDisplayModeId = info.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
- for (const auto& cmode : info.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
-
- gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
- hdrCapabilities.supportedHdrTypes.clear();
- hdrCapabilities.supportedHdrTypes.reserve(
- info.hdrCapabilities.getSupportedHdrTypes().size());
- for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
- hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
- }
- hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
- hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
- hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
-
- outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) {
+ ui::DynamicDisplayInfo info;
+ status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info);
+ if (status == NO_ERROR) {
+ getDynamicDisplayInfoInternal(info, outInfo);
}
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c2d3343..23ac19e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -26,6 +26,7 @@
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <ftl/future.h>
@@ -34,8 +35,8 @@
#include <gui/CompositorTiming.h>
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerDebugInfo.h>
+
#include <gui/LayerState.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
@@ -66,6 +67,7 @@
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FlagManager.h"
+#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
@@ -95,6 +97,7 @@
#include <unordered_map>
#include <unordered_set>
#include <utility>
+#include <vector>
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include "Client.h"
@@ -120,10 +123,13 @@
class ScreenCapturer;
class WindowInfosListenerInvoker;
+using frontend::TransactionHandler;
using gui::CaptureArgs;
using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
+using gui::ITransactionCompletedListener;
using gui::LayerCaptureArgs;
+
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -305,6 +311,11 @@
// on this behavior to increase contrast for some media sources.
bool mTreat170mAsSrgb = false;
+ // Allows to ignore physical orientation provided through hwc API in favour of
+ // 'ro.surface_flinger.primary_display_orientation'.
+ // TODO(b/246793311): Clean up a temporary property
+ bool mIgnoreHwcPhysicalDisplayOrientation = false;
+
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
@@ -510,10 +521,13 @@
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
EXCLUDES(mStateLock);
- status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
+ status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*)
EXCLUDES(mStateLock);
- status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
- EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo*) EXCLUDES(mStateLock);
+ void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&,
+ const display::DisplaySnapshot&);
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
status_t getBootDisplayModeSupport(bool* outSupport) const;
@@ -525,7 +539,7 @@
void setPowerMode(const sp<IBinder>& displayToken, int mode);
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes);
- status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success);
+ status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
status_t getColorManagement(bool* outGetColorManagement) const;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
@@ -637,8 +651,11 @@
bool mKernelIdleTimerEnabled = false;
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
+ // Show render rate with refresh rate overlay
+ bool mRefreshRateOverlayRenderRate = false;
- void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
+ REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
// Sets the active mode and a new refresh rate in SF.
@@ -658,13 +675,19 @@
// Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
// display. Falls back to the display's defaultModeId otherwise.
- std::optional<ftl::NonNull<DisplayModePtr>> getPreferredDisplayMode(
+ ftl::Optional<scheduler::FrameRateMode> getPreferredDisplayMode(
PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock);
status_t setDesiredDisplayModeSpecsInternal(
const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+ // TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
+ status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
+ const scheduler::RefreshRateSelector&,
+ bool force = false)
+ REQUIRES(mStateLock, kMainThreadContext);
+
void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
void commitTransactionsLocked(uint32_t transactionFlags)
REQUIRES(mStateLock, kMainThreadContext);
@@ -693,7 +716,7 @@
*/
bool applyTransactionState(const FrameTimelineInfo& info,
std::vector<ResolvedComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
@@ -993,7 +1016,10 @@
VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
- void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+ // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr`
+ // in tests, and `activeDisplay` must not be `nullptr` as a precondition.
+ void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
@@ -1082,6 +1108,10 @@
std::atomic<uint32_t> mUniqueTransactionId = 1;
SortedVector<sp<Layer>> mLayersPendingRemoval;
+ // Buffers that have been discarded by clients and need to be evicted from per-layer caches so
+ // the graphics memory can be immediately freed.
+ std::vector<uint64_t> mBufferIdsToUncache;
+
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
@@ -1164,7 +1194,9 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
// The inner or outer display for foldables, assuming they have mutually exclusive power states.
- PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock);
+ // Atomic because writes from onActiveDisplayChangedLocked are not always under mStateLock, but
+ // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
+ std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
struct {
DisplayIdGenerator<GpuVirtualDisplayId> gpu;
@@ -1194,8 +1226,6 @@
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
- // If blurs are considered expensive and should require high GPU frequency.
- bool mBlursAreExpensive = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
std::atomic<uint32_t> mHwcFrameMissedCount = 0;
std::atomic<uint32_t> mGpuFrameMissedCount = 0;
@@ -1366,6 +1396,7 @@
} mPowerHintSessionMode;
TransactionHandler mTransactionHandler;
+ display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1388,10 +1419,12 @@
gui::DisplayStatInfo* outStatInfo) override;
binder::Status getDisplayState(const sp<IBinder>& display,
gui::DisplayState* outState) override;
- binder::Status getStaticDisplayInfo(const sp<IBinder>& display,
+ binder::Status getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) override;
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ gui::DynamicDisplayInfo* outInfo) override;
binder::Status getDisplayNativePrimaries(const sp<IBinder>& display,
gui::DisplayPrimaries* outPrimaries) override;
binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override;
@@ -1476,6 +1509,8 @@
status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
status_t checkControlDisplayBrightnessPermission();
status_t checkReadFrameBufferPermission();
+ static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo);
private:
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index c8c71df..5b73030 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,6 +371,10 @@
return SurfaceFlingerProperties::frame_rate_override_for_native_rates().value_or(defaultValue);
}
+bool frame_rate_override_global(bool defaultValue) {
+ return SurfaceFlingerProperties::frame_rate_override_global().value_or(defaultValue);
+}
+
bool enable_layer_caching(bool defaultValue) {
return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5e316cf..09629cf 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -98,6 +98,8 @@
bool frame_rate_override_for_native_rates(bool defaultValue);
+bool frame_rate_override_global(bool defaultValue);
+
bool enable_layer_caching(bool defaultValue);
bool enable_sdr_dimming(bool defaultValue);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index e5a9dd4..630cef1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -27,6 +27,7 @@
#include <algorithm>
#include <chrono>
+#include <cmath>
#include <unordered_map>
#include "TimeStats.h"
@@ -68,6 +69,8 @@
return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
case GameMode::Battery:
return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
+ case GameMode::Custom:
+ return SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM;
default:
return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
}
@@ -88,7 +91,7 @@
}
} // namespace
-bool TimeStats::populateGlobalAtom(std::string* pulledData) {
+bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStartLegacy == 0) {
@@ -136,10 +139,11 @@
// Always clear data.
clearGlobalLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
-bool TimeStats::populateLayerAtom(std::string* pulledData) {
+bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -177,6 +181,12 @@
*atom->mutable_present_to_present() =
histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
}
+ const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta");
+ if (present2PresentDeltaHist != layer->deltas.cend()) {
+ *atom->mutable_present_to_present_delta() =
+ histogramToProto(present2PresentDeltaHist->second.hist,
+ mMaxPulledHistogramBuckets);
+ }
const auto& post2presentHist = layer->deltas.find("post2present");
if (post2presentHist != layer->deltas.cend()) {
*atom->mutable_post_to_present() =
@@ -227,7 +237,8 @@
// Always clear data.
clearLayersLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
@@ -243,7 +254,7 @@
}
}
-bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) {
bool success = false;
if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
success = populateGlobalAtom(pulledData);
@@ -448,6 +459,7 @@
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
+ std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
const int32_t refreshRateBucket =
clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -525,6 +537,12 @@
ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, presentToPresentMs);
timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
+ if (prevPresentToPresentMs) {
+ const int32_t presentToPresentDeltaMs =
+ std::abs(presentToPresentMs - *prevPresentToPresentMs);
+ timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs);
+ }
+ prevPresentToPresentMs = presentToPresentMs;
}
prevTimeRecord = timeRecords[0];
timeRecords.pop_front();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 61d7c22..5f58657 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -47,7 +47,7 @@
virtual ~TimeStats() = default;
// Process a pull request from statsd.
- virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0;
+ virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
@@ -219,6 +219,7 @@
uint32_t lateAcquireFrames = 0;
uint32_t badDesiredPresentFrames = 0;
TimeRecord prevTimeRecord;
+ std::optional<int32_t> prevPresentToPresentMs;
std::deque<TimeRecord> timeRecords;
};
@@ -244,7 +245,7 @@
TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
- bool onPullAtom(const int atomId, std::string* pulledData) override;
+ bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -292,8 +293,8 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- bool populateGlobalAtom(std::string* pulledData);
- bool populateLayerAtom(std::string* pulledData);
+ bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
+ bool populateLayerAtom(std::vector<uint8_t>* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate, SetFrameRateVote,
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index e45757d..8615947 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -173,6 +173,7 @@
GAME_MODE_STANDARD = 2;
GAME_MODE_PERFORMANCE = 3;
GAME_MODE_BATTERY = 4;
+ GAME_MODE_CUSTOM = 5;
}
// Game mode that the layer was running at. Used to track user engagement
@@ -288,7 +289,11 @@
// Introduced in Android 12.
optional FrameTimingHistogram app_deadline_misses = 25;
- // Next ID: 27
+ // Variability histogram of present_to_present timings.
+ // Introduced in Android 14.
+ optional FrameTimingHistogram present_to_present_delta = 27;
+
+ // Next ID: 28
}
/**
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index e8fe734..b6435a8 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -25,8 +25,8 @@
name: "layertracegenerator",
defaults: [
"libsurfaceflinger_mocks_defaults",
+ "librenderengine_deps",
"surfaceflinger_defaults",
- "skia_renderengine_deps",
],
srcs: [
":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 61ff9bc..c09bcce 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -26,14 +26,27 @@
#include <unordered_set>
#include <android-base/thread_annotations.h>
+#include <android/gui/ITransactionCompletedListener.h>
+
#include <binder/IBinder.h>
-#include <ftl/future.h>
-#include <gui/ITransactionCompletedListener.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
+#include <renderengine/RenderEngine.h>
#include <ui/Fence.h>
#include <ui/FenceResult.h>
namespace android {
+using gui::CallbackId;
+using gui::FrameEventHistoryStats;
+using gui::IListenerHash;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
+using gui::ListenerCallbacks;
+using gui::ListenerStats;
+using gui::ReleaseCallbackId;
+using gui::TransactionStats;
+
class CallbackHandle : public RefBase {
public:
CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 7bde2c1..380301f 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -27,13 +27,12 @@
namespace android {
-// Extends the client side composer state by resolving buffer cache ids.
+// Extends the client side composer state by resolving buffer.
class ResolvedComposerState : public ComposerState {
public:
ResolvedComposerState() = default;
ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- int hwcBufferSlot = 0;
};
struct TransactionState {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 577f84e..81ca659 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -81,7 +81,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -231,8 +230,7 @@
: Scheduler(*this, callback, Feature::kContentDetection) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
- const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
- selectorPtr->getActiveMode().getPhysicalDisplayId());
+ const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr));
}
@@ -273,7 +271,7 @@
mPolicy.cachedModeChangedParams.reset();
}
- void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) {
return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
@@ -421,7 +419,7 @@
void onPullAtom(FuzzedDataProvider *fdp) {
const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
- std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+ std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>();
bool success = fdp->ConsumeBool();
mFlinger->onPullAtom(atomId, &pulledData, &success);
}
@@ -493,14 +491,14 @@
mFlinger->getDisplayState(display, &displayState);
}
- void getStaticDisplayInfo(sp<IBinder> &display) {
+ void getStaticDisplayInfo(int64_t displayId) {
ui::StaticDisplayInfo staticDisplayInfo;
- mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+ mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo);
}
- void getDynamicDisplayInfo(sp<IBinder> &display) {
+ void getDynamicDisplayInfo(int64_t displayId) {
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
- mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+ mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
void getDisplayNativePrimaries(sp<IBinder> &display) {
android::ui::DisplayPrimaries displayPrimaries;
@@ -524,7 +522,7 @@
return ids.front();
}
- sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+ std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -551,13 +549,13 @@
mFlinger->bootFinished();
- return display;
+ return {display, physicalDisplayId.value};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- sp<IBinder> display = fuzzBoot(&mFdp);
+ auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -565,8 +563,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(display);
- getDynamicDisplayInfo(display);
+ getStaticDisplayInfo(displayId);
+ getDynamicDisplayInfo(displayId);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
@@ -658,8 +656,7 @@
}
mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
- const auto fps =
- FTL_FAKE_GUARD(kMainThreadContext, mRefreshRateSelector->getActiveMode().getFps());
+ const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
mFlinger->mVsyncConfiguration->getCurrentConfigs());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index c5b3fa6..acfc1d4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -160,7 +160,7 @@
layer->setBuffer(texture, {} /*bufferData*/, mFdp.ConsumeIntegral<nsecs_t>() /*postTime*/,
mFdp.ConsumeIntegral<nsecs_t>() /*desiredTime*/,
mFdp.ConsumeBool() /*isAutoTimestamp*/,
- {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/, 0 /* hwcslot */);
+ {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/);
LayerRenderArea layerArea(*(flinger.flinger()), layer, getFuzzedRect(),
{mFdp.ConsumeIntegral<int32_t>(),
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index bd11a37..7959e52 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -353,7 +353,7 @@
const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
- refreshRateSelector.getRankedRefreshRates(layers, globalSignals);
+ refreshRateSelector.getRankedFrameRates(layers, globalSignals);
layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
@@ -381,7 +381,8 @@
mFdp.ConsumeFloatingPoint<float>())}});
refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
- refreshRateSelector.setActiveModeId(modeId);
+ refreshRateSelector.setActiveMode(modeId,
+ Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
}
RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 88e32e1..2bc5b46 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -116,6 +116,8 @@
return true;
}
+ void setDivisor(unsigned) override {}
+
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
return timePoint;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 28da81f..8540c3d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -447,8 +447,8 @@
# Limits the frame rate override feature (enable_frame_rate_override) to override the refresh rate
# to native display refresh rates only. Before introducing this flag, native display refresh rates
-# was the default behvaiour. With this flag we can control which behaviour we want explicitly.
-# This flag is intoruduced as a fail-safe machanism and planned to be defaulted to false.
+# was the default behaviour. With this flag we can control which behaviour we want explicitly.
+# This flag is introduced as a fail-safe mechanism and planned to be defaulted to false.
prop {
api_name: "frame_rate_override_for_native_rates"
type: Boolean
@@ -457,6 +457,16 @@
prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
}
+# Enables the frame rate override feature (enable_frame_rate_override) to
+# override the frame rate globally instead of only for individual apps.
+prop {
+ api_name: "frame_rate_override_global"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.frame_rate_override_global"
+}
+
# Enables Layer Caching
prop {
api_name: "enable_layer_caching"
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0dfb80e..9338133 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -65,6 +65,10 @@
prop_name: "ro.surface_flinger.frame_rate_override_for_native_rates"
}
prop {
+ api_name: "frame_rate_override_global"
+ prop_name: "ro.surface_flinger.frame_rate_override_global"
+ }
+ prop {
api_name: "has_HDR_display"
prop_name: "ro.surface_flinger.has_HDR_display"
}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 1676844..4a45eb5 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -83,6 +83,15 @@
return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
}
+ static std::optional<uint64_t> getFirstDisplayId() {
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ return std::nullopt;
+ }
+
+ return ids.front().value;
+ }
+
void setupBackgroundSurface() {
mDisplay = getFirstDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
@@ -169,29 +178,25 @@
TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
// Anyone can access display information.
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
// The following methods are tested with a UID that is not root, graphics,
// or system, to show that anyone can access them.
UIDFaker f(AID_BIN);
- const auto display = getFirstDisplayToken();
- ASSERT_TRUE(display != nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
- Vector<ui::DisplayMode> modes;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info));
}
TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
- const auto display = getFirstDisplayToken();
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
std::function<status_t()> condition = [=]() {
ui::DynamicDisplayInfo info;
- return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
@@ -335,8 +340,10 @@
status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
ASSERT_EQ(NO_ERROR, error);
bool hasWideColorMode = false;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
const auto& colorModes = info.supportedColorModes;
for (ColorMode colorMode : colorModes) {
switch (colorMode) {
@@ -363,10 +370,10 @@
}
TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
- const auto display = getFirstDisplayToken();
- ASSERT_FALSE(display == nullptr);
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
ColorMode colorMode = info.activeColorMode;
ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 10dae46..4be961b 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -45,6 +45,7 @@
void SetUp() override {
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
+ mDisplayId = ids.front().value;
mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
ASSERT_EQ(res, NO_ERROR);
@@ -58,11 +59,14 @@
void testSetAllowGroupSwitching(bool allowGroupSwitching);
sp<IBinder> mDisplayToken;
+ uint64_t mDisplayId;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
ui::DynamicDisplayInfo info;
- status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info);
+ status_t res =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+ &info);
const auto& modes = info.supportedDisplayModes;
ASSERT_EQ(res, NO_ERROR);
ASSERT_GT(modes.size(), 0);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index d88da4d..6d1b3fe 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -70,7 +70,6 @@
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
"AidlPowerHalWrapperTest.cpp",
- "CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
"DisplayIdGeneratorTest.cpp",
@@ -85,6 +84,7 @@
"FpsTest.cpp",
"FramebufferSurfaceTest.cpp",
"FrameRateOverrideMappingsTest.cpp",
+ "FrameRateSelectionPriorityTest.cpp",
"FrameTimelineTest.cpp",
"GameModeTest.cpp",
"HWComposerTest.cpp",
@@ -92,6 +92,8 @@
"LayerHistoryTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
+ "LayerHierarchyTest.cpp",
+ "LayerLifecycleManagerTest.cpp",
"LayerTest.cpp",
"LayerTestUtils.cpp",
"MessageQueueTest.cpp",
@@ -109,10 +111,10 @@
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SchedulerTest.cpp",
"SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
- "RefreshRateSelectionTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
"TimeStatsTest.cpp",
@@ -138,6 +140,7 @@
defaults: [
"android.hardware.graphics.common-ndk_static",
"android.hardware.graphics.composer3-ndk_static",
+ "librenderengine_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
deleted file mode 100644
index c1cbbfb..0000000
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2019 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 "CachingTest"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-class SlotGenerationTest : public testing::Test {
-protected:
- sp<HwcSlotGenerator> mHwcSlotGenerator = sp<HwcSlotGenerator>::make();
- sp<GraphicBuffer> mBuffer1 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer2 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer3 =
- sp<GraphicBuffer>::make(10u, 10u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
-};
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) {
- sp<IBinder> binder = sp<BBinder>::make();
- // test getting invalid client_cache_id
- client_cache_t id;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) {
- sp<IBinder> binder = sp<BBinder>::make();
- client_cache_t id;
- id.token = binder;
- id.id = 0;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-
- client_cache_t idB;
- idB.token = binder;
- idB.id = 1;
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) {
- sp<IBinder> binder = sp<BBinder>::make();
- std::vector<client_cache_t> ids;
- uint32_t cacheId = 0;
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- ids.push_back(id);
-
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- }
-
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 982b9ff..60ad7a3 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include "DisplayTransactionTestHelpers.h"
+#include "mock/MockFrameRateMode.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -29,6 +30,7 @@
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
+ using Action = DisplayDevice::DesiredActiveModeAction;
using Event = scheduler::DisplayModeEvent;
void SetUp() override {
@@ -56,31 +58,42 @@
static constexpr DisplayModeId kModeId90{1};
static constexpr DisplayModeId kModeId120{2};
- static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
- static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
- static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
+ static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+ ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+ ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
};
TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
- EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None}));
+ EXPECT_EQ(Action::None,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None}));
EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
}
TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
- // Setting another mode should be cached but return false
- EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+ // Setting another mode should be cached but return None
+ EXPECT_EQ(Action::None,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
}
TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
mDisplay->clearDesiredActiveModeState();
@@ -88,9 +101,11 @@
}
TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -101,18 +116,27 @@
EXPECT_EQ(OK,
mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
&timeline));
- EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
mDisplay->clearDesiredActiveModeState();
ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
}
+TEST_F(InitiateModeChangeTest, initiateRenderRateChange) {
+ EXPECT_EQ(Action::InitiateRenderRateSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None}));
+ EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
NO_THREAD_SAFETY_ANALYSIS {
- EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -123,21 +147,23 @@
EXPECT_EQ(OK,
mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
&timeline));
- EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
- EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+ EXPECT_EQ(Action::None,
+ mDisplay->setDesiredActiveMode(
+ {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
- EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
EXPECT_EQ(OK,
mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
&timeline));
- EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
mDisplay->clearDesiredActiveModeState();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 19c7d5c..d58e644 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -359,6 +359,7 @@
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
const auto displayId = DisplayVariant::DISPLAY_ID::get();
ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -367,18 +368,21 @@
.setHwcDisplayId(HWC_DISPLAY_ID)
.setResolution(DisplayVariant::RESOLUTION)
.setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
+ .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
.inject(&test->mFlinger, test->mComposer);
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplay(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
- .WillOnce(Return(Error::NONE));
- injectHwcDisplayWithNoDefaultCapabilities(test);
+ if constexpr (kInitPowerMode) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ .WillOnce(Return(Error::NONE));
+ }
+ injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
}
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index a5beaba..dd87f9d 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -633,9 +633,10 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
@@ -644,9 +645,10 @@
.setId(DisplayModeId(5))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
@@ -655,8 +657,9 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
+ const Fps fps = mode->getFps() / 2;
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -669,9 +672,10 @@
.setId(DisplayModeId(9))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
auto args = suppressConnectionEventRecorder.waitForCall();
ASSERT_FALSE(args.has_value());
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
similarity index 100%
rename from services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
rename to services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 342c646..8f89a8c 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -146,6 +146,8 @@
{kMetadata2Name, kMetadata2Mandatory},
}),
Return(hardware::graphics::composer::V2_4::Error::NONE)));
+ EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE));
+
EXPECT_CALL(*mHal, registerCallback(_));
mHwc.setCallback(mCallback);
@@ -162,6 +164,7 @@
EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<aidl::Capability>{}));
EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
.WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+ EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED));
EXPECT_CALL(*mHal, registerCallback(_));
mHwc.setCallback(mCallback);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
new file mode 100644
index 0000000..8560902
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+#define UPDATE_AND_VERIFY(HIERARCHY) \
+ ({ \
+ SCOPED_TRACE(""); \
+ updateAndVerify((HIERARCHY)); \
+ })
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = canBeRoot;
+ args.parentHandle = parent;
+ args.mirrorLayerHandle = mirror;
+ return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+ /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+ --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \
+ --gtest_shuffle \
+ --gtest_brief=1
+*/
+
+class LayerHierarchyTest : public testing::Test {
+protected:
+ LayerHierarchyTest() {
+ // tree with 3 levels of children
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(12, 1);
+ createLayer(13, 1);
+ createLayer(111, 11);
+ createLayer(121, 12);
+ createLayer(122, 12);
+ createLayer(1221, 122);
+ mLifecycleManager.commitChanges();
+ }
+ std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverseInZOrder(
+ [&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ void createRootLayer(uint32_t id) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
+ /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void reparentLayer(uint32_t id, uint32_t newParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (newParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
+ } else {
+ auto parentHandle = mHandles[newParentId];
+ transactions.back().states.front().state.parentSurfaceControlForChild =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(newParentId), "Test");
+ }
+ transactions.back().states.front().state.what = layer_state_t::eReparent;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (relativeParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ } else {
+ auto parentHandle = mHandles[relativeParentId];
+ transactions.back().states.front().state.relativeLayerSurfaceControl =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(relativeParentId), "test");
+ transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ }
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
+ auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
+ auto mirrorHandle =
+ (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
+
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
+ /*mirror=*/mHandles[layerToMirror])));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void updateBackgroundColor(uint32_t id, half alpha) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.bgColorAlpha = alpha;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+
+ void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
+ if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ hierarchyBuilder.update(mLifecycleManager.getLayers(),
+ mLifecycleManager.getDestroyedLayers());
+ }
+ mLifecycleManager.commitChanges();
+
+ // rebuild layer hierarchy from scratch and verify that it matches the updated state.
+ LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
+ getTraversalPath(newBuilder.getHierarchy()));
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
+ getTraversalPathInZOrder(newBuilder.getHierarchy()));
+ EXPECT_FALSE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ }
+
+ void setZ(uint32_t id, int32_t z) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().state.z = z;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+ LayerLifecycleManager mLifecycleManager;
+ std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+// reparenting tests
+TEST_F(LayerHierarchyTest, addLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ createRootLayer(3);
+ createLayer(112, 11);
+ createLayer(12211, 1221);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, 11);
+ reparentLayer(111, 12);
+ reparentLayer(1221, 1);
+ reparentLayer(1221, 13);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, 13);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ destroyLayerHandle(2);
+ destroyLayerHandle(11);
+ destroyLayerHandle(1221);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ destroyLayerHandle(111);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // handle is destroyed but layer is kept alive and reachable by parent
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // destroy parent layer and the child gets destroyed
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, 1);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// offscreen tests
+TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 112};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// rel-z tests
+TEST_F(LayerHierarchyTest, setRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror tests
+TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ createLayer(1111, 111);
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 1111, 112, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 1111, 112, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror & relatives tests
+TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(111, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 111 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 11 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 111, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // 111 is not reachable in the mirror
+ expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 1221 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 12 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 1221,
+ 13, 14, 12, 121, 122, 1221, 1221, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // relative layer 1221 is traversable in the mirrored hierarchy as well
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentRelativeLayer(11, 2);
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(2);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // remove relative parent so layer becomes onscreen again
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // verify relz path
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // offscreen layer cannot be reached as a relative child
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {12, 121, 122, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // layer when onscreen can be reached as a relative child again
+ reparentLayer(12, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ updateBackgroundColor(1, 0.5);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// cycle tests
+TEST_F(LayerHierarchyTest, ParentBecomesTheChild) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createLayer(11, 1);
+ reparentLayer(1, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, RelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ reparentRelativeLayer(11, 2);
+ reparentRelativeLayer(2, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ // fix loop
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+ EXPECT_TRUE(hasRelZLoop);
+ mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+ EXPECT_EQ(invalidRelativeRoot, 11u);
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, IndirectRelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(111, 11);
+ createLayer(21, 2);
+ createLayer(22, 2);
+ createLayer(221, 22);
+ reparentRelativeLayer(22, 111);
+ reparentRelativeLayer(11, 221);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ // fix loop
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+ EXPECT_TRUE(hasRelZLoop);
+ mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 2, 21};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(1, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ createRootLayer(1);
+ destroyLayerHandle(1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// traversal path test
+TEST_F(LayerHierarchyTest, traversalPathId) {
+ setZ(122, -1);
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ auto checkTraversalPathIdVisitor =
+ [](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id);
+ return true;
+ };
+ hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor);
+ hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
new file mode 100644
index 0000000..89440a6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+using namespace android::surfaceflinger;
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = canBeRoot;
+ args.parentHandle = parent;
+ args.mirrorLayerHandle = mirror;
+ return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+ /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+ --gtest_filter="LayerLifecycleManagerTest.*" --gtest_repeat=100 \
+ --gtest_shuffle \
+ --gtest_brief=1
+*/
+class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener {
+public:
+ void onLayerAdded(const RequestedLayerState& layer) override {
+ mActualLayersAdded.emplace(layer.id);
+ };
+ void onLayerDestroyed(const RequestedLayerState& layer) override {
+ mActualLayersDestroyed.emplace(layer.id);
+ };
+
+ void expectLayersAdded(const std::unordered_set<uint32_t>& expectedLayersAdded) {
+ EXPECT_EQ(expectedLayersAdded, mActualLayersAdded);
+ mActualLayersAdded.clear();
+ }
+ void expectLayersDestroyed(const std::unordered_set<uint32_t>& expectedLayersDestroyed) {
+ EXPECT_EQ(expectedLayersDestroyed, mActualLayersDestroyed);
+ mActualLayersDestroyed.clear();
+ }
+
+ std::unordered_set<uint32_t> mActualLayersAdded;
+ std::unordered_set<uint32_t> mActualLayersDestroyed;
+};
+
+class LayerLifecycleManagerTest : public testing::Test {
+protected:
+ std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
+ return std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr));
+ }
+
+ std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
+ mHandles[parentId] = sp<LayerHandle>::make(parentId);
+ return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
+ /*parent=*/mHandles[parentId],
+ /*mirror=*/nullptr));
+ }
+
+ TransactionState reparentLayer(uint32_t id, uint32_t newParentId) {
+ TransactionState transaction;
+ transaction.states.push_back({});
+
+ if (newParentId == UNASSIGNED_LAYER_ID) {
+ transaction.states.front().state.parentSurfaceControlForChild = nullptr;
+ } else {
+ transaction.states.front().state.parentSurfaceControlForChild =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
+ sp<LayerHandle>::make(newParentId),
+ static_cast<int32_t>(newParentId), "Test");
+ }
+ transaction.states.front().state.what = layer_state_t::eReparent;
+ transaction.states.front().state.surface = sp<LayerHandle>::make(id);
+ return transaction;
+ }
+
+ TransactionState setLayer(uint32_t id, int32_t z) {
+ TransactionState transaction;
+ transaction.states.push_back({});
+ transaction.states.front().state.z = z;
+ transaction.states.front().state.what = layer_state_t::eLayerChanged;
+ transaction.states.front().state.surface = sp<LayerHandle>::make(id);
+ return transaction;
+ }
+
+ TransactionState makeRelative(uint32_t id, uint32_t relativeParentId) {
+ TransactionState transaction;
+ transaction.states.push_back({});
+
+ if (relativeParentId == UNASSIGNED_LAYER_ID) {
+ transaction.states.front().state.relativeLayerSurfaceControl = nullptr;
+ } else {
+ transaction.states.front().state.relativeLayerSurfaceControl =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(),
+ sp<LayerHandle>::make(relativeParentId),
+ static_cast<int32_t>(relativeParentId), "Test");
+ }
+ transaction.states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ transaction.states.front().state.surface = sp<LayerHandle>::make(id);
+ return transaction;
+ }
+
+ RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
+ uint32_t layerId) {
+ return lifecycleManager.getLayerFromId(layerId);
+ }
+
+ std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+TEST_F(LayerLifecycleManagerTest, addLayers) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(rootLayer(3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({1, 2, 3});
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ listener->expectLayersAdded({1, 2, 3});
+ listener->expectLayersDestroyed({1, 2, 3});
+}
+
+TEST_F(LayerLifecycleManagerTest, updateLayerStates) {
+ LayerLifecycleManager lifecycleManager;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.z = 2;
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ lifecycleManager.applyTransactions(transactions);
+ transactions.clear();
+
+ auto& managedLayers = lifecycleManager.getLayers();
+ ASSERT_EQ(managedLayers.size(), 1u);
+
+ EXPECT_EQ(managedLayers.front()->z, 2);
+ EXPECT_TRUE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ ASSERT_EQ(managedLayers.size(), 1u);
+ EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
+
+ // apply transactions that do not affect the hierarchy
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.backgroundBlurRadius = 22;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+ transactions.back().states.front().state.surface = handle;
+ lifecycleManager.applyTransactions(transactions);
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ EXPECT_EQ(managedLayers.front()->backgroundBlurRadius, 22u);
+}
+
+TEST_F(LayerLifecycleManagerTest, layerWithoutHandleIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.commitChanges();
+
+ SCOPED_TRACE("layerWithoutHandleIsDestroyed");
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({1});
+}
+
+TEST_F(LayerLifecycleManagerTest, rootLayerWithoutHandleIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({1});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenLayerIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.onHandlesDestroyed({3});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({3});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithHandleIsNotDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.onHandlesDestroyed({3});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({3});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithoutHandleIsDestroyed) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)});
+ lifecycleManager.onHandlesDestroyed({3, 4});
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({3, 4});
+}
+
+TEST_F(LayerLifecycleManagerTest, reparentingDoesNotAffectRelativeZ) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+ lifecycleManager.applyTransactions({reparentLayer(4, 2)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, reparentingToNullRemovesRelativeZ) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+ lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)});
+ EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, setZRemovesRelativeZ) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ layers.emplace_back(rootLayer(2));
+ layers.emplace_back(childLayer(3, /*parent*/ 2));
+ layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+ lifecycleManager.addLayers(std::move(layers));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2, 3, 4});
+ listener->expectLayersDestroyed({});
+
+ lifecycleManager.applyTransactions({makeRelative(4, 1)});
+ EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+ lifecycleManager.applyTransactions({setLayer(4, 1)});
+ EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({});
+ listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ lifecycleManager.applyTransactions(transactions);
+
+ auto& managedLayers = lifecycleManager.getLayers();
+ ASSERT_EQ(managedLayers.size(), 2u);
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({});
+ EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf);
+}
+
+TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.surface = handle;
+
+ lifecycleManager.applyTransactions(transactions);
+
+ ASSERT_EQ(lifecycleManager.getLayers().size(), 1u);
+ ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 1u);
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({2});
+}
+
+TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) {
+ LayerLifecycleManager lifecycleManager;
+ auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+ lifecycleManager.addLifecycleListener(listener);
+
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(rootLayer(1));
+ lifecycleManager.addLayers(std::move(layers));
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.bgColorAlpha = 0.5;
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ sp<LayerHandle> handle = sp<LayerHandle>::make(1u);
+ transactions.back().states.front().state.surface = handle;
+ transactions.emplace_back();
+ lifecycleManager.applyTransactions(transactions);
+ lifecycleManager.onHandlesDestroyed({1});
+
+ ASSERT_EQ(lifecycleManager.getLayers().size(), 0u);
+ ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 2u);
+
+ EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ lifecycleManager.commitChanges();
+ listener->expectLayersAdded({1, 2});
+ listener->expectLayersDestroyed({1, 2});
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index cedb7eb..a3b3c4c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -26,10 +26,14 @@
#include <log/log.h>
#include <ui/Size.h>
+#include <scheduler/FrameRateMode.h>
#include "DisplayHardware/HWC2.h"
#include "FpsOps.h"
#include "Scheduler/RefreshRateSelector.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include "libsurfaceflinger_unittest_main.h"
using namespace std::chrono_literals;
@@ -45,71 +49,77 @@
using mock::createDisplayMode;
struct TestableRefreshRateSelector : RefreshRateSelector {
+ using RefreshRateSelector::FrameRateRanking;
using RefreshRateSelector::RefreshRateOrder;
- using RefreshRateSelector::RefreshRateRanking;
using RefreshRateSelector::RefreshRateSelector;
- void setActiveModeId(DisplayModeId modeId) {
+ void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
ftl::FakeGuard guard(kMainThreadContext);
- return RefreshRateSelector::setActiveModeId(modeId);
+ return RefreshRateSelector::setActiveMode(modeId, renderFrameRate);
}
const DisplayMode& getActiveMode() const {
- ftl::FakeGuard guard(kMainThreadContext);
- return RefreshRateSelector::getActiveMode();
+ std::lock_guard lock(mLock);
+ return *RefreshRateSelector::getActiveModeLocked().modePtr;
}
- DisplayModePtr getMinSupportedRefreshRate() const {
+ ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const {
std::lock_guard lock(mLock);
- return mMinRefreshRateModeIt->second;
+ return ftl::as_non_null(mMinRefreshRateModeIt->second);
}
- DisplayModePtr getMaxSupportedRefreshRate() const {
+ ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const {
std::lock_guard lock(mLock);
- return mMaxRefreshRateModeIt->second;
+ return ftl::as_non_null(mMaxRefreshRateModeIt->second);
}
- DisplayModePtr getMinRefreshRateByPolicy() const {
+ ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
- return getMinRefreshRateByPolicyLocked();
+ return ftl::as_non_null(getMinRefreshRateByPolicyLocked());
}
- DisplayModePtr getMaxRefreshRateByPolicy() const {
+ ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
- return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+ return ftl::as_non_null(
+ getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()));
}
- RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
- RefreshRateOrder refreshRateOrder) const {
+ FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder) const {
std::lock_guard lock(mLock);
- return RefreshRateSelector::rankRefreshRates(anchorGroupOpt, refreshRateOrder);
+ return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder);
}
const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
- using RefreshRateSelector::GetRankedRefreshRatesCache;
- auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
+ using RefreshRateSelector::GetRankedFrameRatesCache;
+ auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
- auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const {
- const auto result = RefreshRateSelector::getRankedRefreshRates(layers, signals);
+ auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
- ScoredRefreshRate::DescendingScore{}));
+ ScoredFrameRate::DescendingScore{}));
return result;
}
auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) const {
- const auto [ranking, consideredSignals] = getRankedRefreshRates(layers, signals);
+ const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
return std::make_pair(ranking, consideredSignals);
}
- DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
- GlobalSignals signals = {}) const {
- return getRankedRefreshRates(layers, signals).ranking.front().modePtr;
+ ftl::NonNull<DisplayModePtr> getBestFrameRateMode(
+ const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
+ }
+
+ ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {},
+ GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front();
}
SetPolicyResult setPolicy(const PolicyVariant& policy) {
@@ -120,9 +130,11 @@
SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
return setPolicy(policy);
}
+
+ const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
};
-class RefreshRateSelectorTest : public testing::Test {
+class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
protected:
using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder;
@@ -140,28 +152,48 @@
static constexpr DisplayModeId kModeId24Frac{8};
static constexpr DisplayModeId kModeId30Frac{9};
static constexpr DisplayModeId kModeId60Frac{10};
+ static constexpr DisplayModeId kModeId35{11};
- static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
- static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
- static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
- static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1);
- static inline const DisplayModePtr kMode90_4K =
- createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160});
- static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz);
- static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1);
- static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
- static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1);
- static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz);
- static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1);
- static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
- static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
- static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
- static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
- static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
- static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
+ static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+ ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode60Frac =
+ ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90_4K =
+ ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}));
+ static inline const ftl::NonNull<DisplayModePtr> kMode72 =
+ ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+ ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode30 =
+ ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode30Frac =
+ ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode25 =
+ ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 =
+ ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1));
+ static inline const ftl::NonNull<DisplayModePtr> kMode35 =
+ ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode50 =
+ ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode24 =
+ ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode24Frac =
+ ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz));
// Test configurations.
static inline const DisplayModes kModes_60 = makeModes(kMode60);
+ static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90);
static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
@@ -185,6 +217,13 @@
static inline const DisplayModes kModes_24_25_30_50_60_Frac =
makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
kMode60Frac);
+
+ static TestableRefreshRateSelector createSelector(DisplayModes modes,
+ DisplayModeId activeModeId,
+ Config config = {}) {
+ config.enableFrameRateOverride = GetParam();
+ return TestableRefreshRateSelector(modes, activeModeId, config);
+ }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -201,13 +240,23 @@
namespace {
-TEST_F(RefreshRateSelectorTest, oneMode_canSwitch) {
- RefreshRateSelector selector(kModes_60, kModeId60);
- EXPECT_FALSE(selector.canSwitch());
+INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest,
+ testing::Values(Config::FrameRateOverride::Disabled,
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+ Config::FrameRateOverride::AppOverride,
+ Config::FrameRateOverride::Enabled));
+
+TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) {
+ auto selector = createSelector(kModes_60, kModeId60);
+ if (GetParam() == Config::FrameRateOverride::Enabled) {
+ EXPECT_TRUE(selector.canSwitch());
+ } else {
+ EXPECT_FALSE(selector.canSwitch());
+ }
}
-TEST_F(RefreshRateSelectorTest, invalidPolicy) {
- TestableRefreshRateSelector selector(kModes_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, invalidPolicy) {
+ auto selector = createSelector(kModes_60, kModeId60);
EXPECT_EQ(SetPolicyResult::Invalid,
selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
@@ -215,8 +264,8 @@
selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
}
-TEST_F(RefreshRateSelectorTest, unchangedPolicy) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, unchangedPolicy) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
@@ -236,8 +285,8 @@
selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
}
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
const auto minRate = selector.getMinSupportedRefreshRate();
const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -252,8 +301,8 @@
EXPECT_EQ(performanceRateByPolicy, performanceRate);
}
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
const auto minRate = selector.getMinRefreshRateByPolicy();
const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -266,7 +315,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
const auto minRate90 = selector.getMinRefreshRateByPolicy();
const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
@@ -276,8 +325,8 @@
EXPECT_EQ(kMode90_G1, performanceRate90);
}
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
- TestableRefreshRateSelector selector(kModes_60_90_4K, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+ auto selector = createSelector(kModes_60_90_4K, kModeId60);
const auto minRate = selector.getMinRefreshRateByPolicy();
const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -290,7 +339,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
const auto minRate90 = selector.getMinRefreshRateByPolicy();
const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
@@ -300,8 +349,8 @@
EXPECT_EQ(kMode90_4K, performanceRate90);
}
-TEST_F(RefreshRateSelectorTest, twoModes_policyChange) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_policyChange) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
const auto minRate = selector.getMinRefreshRateByPolicy();
const auto performanceRate = selector.getMaxRefreshRateByPolicy();
@@ -319,14 +368,14 @@
EXPECT_EQ(kMode60, performanceRate60);
}
-TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
{
const auto& mode = selector.getActiveMode();
EXPECT_EQ(mode.getId(), kModeId60);
}
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
{
const auto& mode = selector.getActiveMode();
EXPECT_EQ(mode.getId(), kModeId90);
@@ -340,31 +389,31 @@
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_noLayers) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) {
{
- TestableRefreshRateSelector selector(kModes_60_72_90, kModeId72);
+ auto selector = createSelector(kModes_60_72_90, kModeId72);
// If there are no layers we select the default frame rate, which is the max of the primary
// range.
- EXPECT_EQ(kMode90, selector.getBestRefreshRate());
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode());
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
- EXPECT_EQ(kMode60, selector.getBestRefreshRate());
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode());
}
{
// We select max even when this will cause a non-seamless switch.
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
constexpr bool kAllowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy(
{kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
- EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate());
+ EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId72);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId72);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].vote = LayerVoteType::ExplicitExact;
@@ -372,188 +421,187 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_90) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.name = "";
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_multipleThreshold_60_90) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60,
- {.frameRateMultipleThreshold = 90});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_72_90) {
- TestableRefreshRateSelector selector(kModes_60_72_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) {
+ auto selector = createSelector(kModes_60_72_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -563,23 +611,23 @@
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -591,7 +639,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -599,7 +647,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -607,7 +655,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -615,7 +663,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -623,7 +671,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -631,7 +679,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
@@ -639,7 +687,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -647,7 +695,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -655,12 +703,13 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
- {.frameRateMultipleThreshold = 120});
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) {
+ auto selector =
+ createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120});
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -673,7 +722,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -681,7 +730,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -689,7 +738,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -697,7 +746,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -705,7 +754,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -713,7 +762,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
@@ -721,7 +770,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -729,7 +778,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -737,14 +786,14 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -752,7 +801,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "120Hz ExplicitDefault";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -760,7 +809,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::ExplicitExact;
lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 10_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -768,7 +817,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 30_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -779,86 +828,86 @@
lr3.vote = LayerVoteType::Heuristic;
lr3.desiredRefreshRate = 120_Hz;
lr3.name = "120Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60) {
- TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) {
+ auto selector = createSelector(kModes_30_60, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) {
+ auto selector = createSelector(kModes_30_60_72_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 24_Hz;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) {
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) {
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -866,43 +915,43 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 15_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -910,15 +959,14 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestRefreshRate(layers);
+ const auto mode = selector.getBestFrameRateMode(layers);
EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getFps());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
- TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
- {.frameRateMultipleThreshold = 120});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120});
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -926,14 +974,14 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestRefreshRate(layers);
+ const auto mode = selector.getBestFrameRateMode(layers);
EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getFps());
}
}
-TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -943,23 +991,23 @@
lr1.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -967,14 +1015,14 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestRefreshRate(layers, {});
+ const auto mode = selector.getBestFrameRateMode(layers, {});
EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getFps());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -986,7 +1034,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
@@ -994,14 +1042,14 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30_Hz;
@@ -1009,18 +1057,18 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30_Hz;
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -1031,28 +1079,28 @@
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1061,49 +1109,70 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
-
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
RefreshRateOrder::Descending);
- const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
RefreshRateOrder::Ascending);
- const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
+ auto selector = createSelector(kModes_30_60_90, kModeId72);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
@@ -1111,20 +1180,31 @@
const auto refreshRates =
selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
- const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
+ auto selector = createSelector(kModes_30_60_90, kModeId72);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
@@ -1132,29 +1212,52 @@
const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
RefreshRateOrder::Descending);
- const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
- auto [refreshRates, signals] = selector.getRankedRefreshRates({}, {});
+ auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.powerOnImminent);
- std::array expectedRefreshRates = {kMode90, kMode60};
+ auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::tie(refreshRates, signals) =
@@ -1164,9 +1267,11 @@
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1182,29 +1287,43 @@
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::tie(refreshRates, signals) =
selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
EXPECT_FALSE(signals.powerOnImminent);
- expectedRefreshRates = {kMode60, kMode90};
+ expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, touchConsidered) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, touchConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
- auto [_, signals] = selector.getRankedRefreshRates({}, {});
+ auto [_, signals] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.touch);
std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
@@ -1251,8 +1370,8 @@
EXPECT_FALSE(signals.touch);
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) {
- TestableRefreshRateSelector selector(kModes_60_90_72_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
+ auto selector = createSelector(kModes_60_90_72_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -1284,57 +1403,57 @@
ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- EXPECT_EQ(expected, selector.getBestRefreshRate(layers)->getFps());
+ EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
}
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
// Test that 23.976 will choose 24 if 23.976 is not supported
{
- TestableRefreshRateSelector selector(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
+ auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+ kMode60Frac),
+ kModeId60);
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 23.976_Hz;
lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId24, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 24 will choose 23.976 if 24 is not supported
{
- TestableRefreshRateSelector selector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
+ auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+ kMode60, kMode60Frac),
+ kModeId60);
lr.desiredRefreshRate = 24_Hz;
lr.name = "ExplicitExactOrMultiple 24 Hz";
- EXPECT_EQ(kModeId24Frac, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 29.97 will prefer 59.94 over 60 and 30
{
- TestableRefreshRateSelector selector(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
- kMode60, kMode60Frac),
- kModeId60);
+ auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60,
+ kMode60Frac),
+ kModeId60);
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId60Frac, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
// Test that voting for supported refresh rate will select this refresh rate
{
- TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60);
+ auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
lr.vote = LayerVoteType::ExplicitExact;
@@ -1343,14 +1462,14 @@
ss << "ExplicitExact " << desired;
lr.name = ss.str();
- EXPECT_EQ(lr.desiredRefreshRate, selector.getBestRefreshRate(layers)->getFps());
+ EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps());
}
}
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
constexpr FpsRange k90 = {90_Hz, 90_Hz};
constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1365,16 +1484,16 @@
lr.name = "60Hz ExplicitDefault";
lr.focused = true;
- const auto [mode, signals] =
- selector.getRankedRefreshRates(layers, {.touch = true, .idle = true});
+ const auto [rankedFrameRate, signals] =
+ selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
- EXPECT_EQ(mode.begin()->modePtr, kMode60);
+ EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
EXPECT_FALSE(signals.touch);
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
constexpr FpsRange k60 = {60_Hz, 60_Hz};
constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1389,11 +1508,11 @@
lr.desiredRefreshRate = 90_Hz;
lr.name = "90Hz ExplicitDefault";
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.idle = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}));
}
-TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f},
{.weight = 1.f},
@@ -1426,15 +1545,31 @@
lr5.name = "30Hz";
lr5.focused = true;
- std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
- auto actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ auto expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.vote = LayerVoteType::Max;
@@ -1452,15 +1587,31 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz";
- expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
- actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.vote = LayerVoteType::Heuristic;
@@ -1476,15 +1627,31 @@
lr5.desiredRefreshRate = 72_Hz;
lr5.name = "72Hz";
- expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72};
- actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30},
+ {60_Hz, kMode60},
+ {90_Hz, kMode90},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}, {120_Hz, kMode120},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.desiredRefreshRate = 120_Hz;
@@ -1503,21 +1670,37 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz-2";
- expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30};
- actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90},
+ {60_Hz, kMode60},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {120_Hz, kMode120}, {72_Hz, kMode72},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
constexpr FpsRange k90 = {90_Hz, 90_Hz};
constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1525,8 +1708,8 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
- const auto [ranking, signals] = selector.getRankedRefreshRates({}, {});
- EXPECT_EQ(ranking.front().modePtr, kMode90);
+ const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+ EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
EXPECT_FALSE(signals.touch);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1536,50 +1719,50 @@
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitExactOrMultiple";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::ExplicitDefault;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitDefault";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Heuristic;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Max";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Min;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Min";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
// The default policy doesn't allow group switching. Verify that no
// group switches are performed.
@@ -1591,11 +1774,11 @@
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1609,11 +1792,11 @@
layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1628,18 +1811,18 @@
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
// Verify that we won't do a seamless switch if we request the same mode as the default
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1649,18 +1832,18 @@
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
// Verify that if the active mode is in another group and there are no layers with
// Seamlessness::SeamedAndSeamless, we should switch back to the default group.
@@ -1673,18 +1856,18 @@
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
// If there's a layer with Seamlessness::SeamedAndSeamless, another layer with
// Seamlessness::OnlySeamless can't change the mode group.
@@ -1702,18 +1885,18 @@
layers[1].name = "90Hz ExplicitDefault";
layers[1].focused = false;
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
// If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with
// Seamlessness::Default can't change the mode group back to the group of the default
@@ -1735,18 +1918,18 @@
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
// Layer with Seamlessness::Default can change the mode group if there's an
// unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens
@@ -1765,11 +1948,11 @@
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
- TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
+ auto selector = createSelector(kModes_30_60, kModeId60);
// Allow group switching.
RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1785,14 +1968,14 @@
layer.name = "60Hz ExplicitExactOrMultiple";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
- selector.setActiveModeId(kModeId120);
- EXPECT_EQ(kModeId120, selector.getBestRefreshRate(layers)->getId());
+ selector.setActiveMode(kModeId120, 120_Hz);
+ EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
- TestableRefreshRateSelector selector(kModes_25_30_50_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+ auto selector = createSelector(kModes_25_30_50_60, kModeId60);
// Allow group switching.
RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1813,18 +1996,18 @@
.weight = 1.f,
.focused = true}};
- EXPECT_EQ(kModeId50, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId());
auto& seamedLayer = layers[0];
seamedLayer.desiredRefreshRate = 30_Hz;
seamedLayer.name = "30Hz ExplicitDefault";
- selector.setActiveModeId(kModeId30);
+ selector.setActiveMode(kModeId30, 30_Hz);
- EXPECT_EQ(kModeId25, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId90);
+TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId90);
// Allow group switching.
RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1835,11 +2018,11 @@
std::vector<LayerRequirement> layers = {
{.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
@@ -1849,13 +2032,14 @@
bool focused = true;
};
- // Returns the mode selected by getBestRefreshRate for a single layer with the given arguments.
+ // Returns the mode selected by getBestFrameRateMode for a single layer with the given
+ // arguments.
const auto getFrameRate = [&](LayerVoteType voteType, Fps fps,
Args args = {}) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
layers[0].focused = args.focused;
- return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId();
+ return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId();
};
constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
@@ -1864,7 +2048,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId());
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
@@ -1897,22 +2081,23 @@
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
}
-TEST_F(RefreshRateSelectorTest, idle) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, idle) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
- const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
+ const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+ bool touchActive) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
const auto [ranking, signals] =
- selector.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true});
+ selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
EXPECT_EQ(!touchActive, signals.idle);
- return ranking.front().modePtr->getId();
+ return ranking.front().frameRateMode.modePtr->getId();
};
EXPECT_EQ(SetPolicyResult::Changed,
@@ -1921,38 +2106,38 @@
// Idle should be lower priority than touch boost.
{
constexpr bool kTouchActive = true;
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
EXPECT_EQ(kModeId90,
- getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
}
// With no layers, idle should still be lower priority than touch boost.
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
// Idle should be higher precedence than other layer frame rate considerations.
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
{
constexpr bool kTouchActive = false;
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
EXPECT_EQ(kModeId60,
- getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
}
// Idle should be applied rather than the active mode when there are no layers.
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate({}, {.idle = true})->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId());
}
-TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps));
@@ -1969,12 +2154,12 @@
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
struct Expectation {
Fps fps;
- DisplayModePtr mode;
+ ftl::NonNull<DisplayModePtr> mode;
};
const std::initializer_list<Expectation> knownFrameRatesExpectations = {
@@ -1997,101 +2182,86 @@
for (const auto& [fps, mode] : knownFrameRatesExpectations) {
layer.desiredRefreshRate = fps;
- EXPECT_EQ(mode, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(mode, selector.getBestFrameRateMode(layers));
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
auto& explicitExactOrMultipleLayer = layers[1];
- explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
- explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
explicitExactLayer.vote = LayerVoteType::ExplicitExact;
explicitExactLayer.name = "ExplicitExact";
explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers, {.touch = true}));
-
- explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
- explicitExactLayer.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 72_Hz;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 120_Hz;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- auto& explicitExactLayer = layers[0];
- auto& explicitExactOrMultipleLayer = layers[1];
-
explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
- explicitExactLayer.vote = LayerVoteType::ExplicitExact;
- explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = 30_Hz;
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ selector.getBestScoredFrameRate(layers, {.touch = true})
+ .frameRateMode);
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers, {.touch = true})
+ .frameRateMode);
+ }
explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
explicitExactLayer.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ }
explicitExactLayer.desiredRefreshRate = 72_Hz;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
explicitExactLayer.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
explicitExactLayer.desiredRefreshRate = 120_Hz;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ReadsCache) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
using GlobalSignals = RefreshRateSelector::GlobalSignals;
const auto args = std::make_pair(std::vector<LayerRequirement>{},
GlobalSignals{.touch = true, .idle = true});
- const RefreshRateSelector::RankedRefreshRates result = {{RefreshRateSelector::ScoredRefreshRate{
- kMode90}},
- {.touch = true}};
+ const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
+ {90_Hz, kMode90}}},
+ GlobalSignals{.touch = true}};
selector.mutableGetRankedRefreshRatesCache() = {args, result};
- EXPECT_EQ(result, selector.getRankedRefreshRates(args.first, args.second));
+ EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
- const auto result = selector.getRankedRefreshRates(layers, globalSignals);
+ const auto result = selector.getRankedFrameRates(layers, globalSignals);
const auto& cache = selector.mutableGetRankedRefreshRatesCache();
ASSERT_TRUE(cache);
@@ -2100,10 +2270,8 @@
EXPECT_EQ(cache->result, result);
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) {
- TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) {
+ auto selector = createSelector(kModes_60_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
@@ -2117,19 +2285,21 @@
explicitExactLayer.name = "ExplicitExact";
explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+ } else {
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}));
+ }
explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
- TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) {
+ auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
auto& explicitDefaultLayer = layers[0];
@@ -2143,12 +2313,16 @@
explicitDefaultLayer.name = "ExplicitDefault";
explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
// b/190578904
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) {
- constexpr int kMinRefreshRate = 10;
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+ if (g_noSlowTests) {
+ GTEST_SKIP();
+ }
+
+ const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
constexpr int kMaxRefreshRate = 240;
DisplayModes displayModes;
@@ -2159,14 +2333,13 @@
Fps::fromValue(static_cast<float>(fps))));
}
- const TestableRefreshRateSelector selector(std::move(displayModes),
- DisplayModeId(kMinRefreshRate));
+ const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
layers[0].desiredRefreshRate = fps;
layers[0].vote = vote;
- EXPECT_EQ(fps.getIntValue(), selector.getBestRefreshRate(layers)->getFps().getIntValue())
+ EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
<< "Failed for " << ftl::enum_string(vote);
};
@@ -2180,7 +2353,7 @@
}
// b/190578904
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) {
constexpr DisplayModeId kActiveModeId{0};
DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
createDisplayMode(DisplayModeId(1), 53_Hz),
@@ -2188,7 +2361,7 @@
createDisplayMode(DisplayModeId(3), 60_Hz));
const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
- const TestableRefreshRateSelector selector(std::move(displayModes), kActiveModeId);
+ const auto selector = createSelector(std::move(displayModes), kActiveModeId);
const std::vector<LayerRequirement> layers = {
{
@@ -2205,19 +2378,19 @@
},
};
- EXPECT_EQ(53_Hz, selector.getBestRefreshRate(layers, globalSignals)->getFps());
+ EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps());
}
-TEST_F(RefreshRateSelectorTest, modeComparison) {
+TEST_P(RefreshRateSelectorTest, modeComparison) {
EXPECT_LT(kMode60->getFps(), kMode90->getFps());
EXPECT_GE(kMode60->getFps(), kMode60->getFps());
EXPECT_GE(kMode90->getFps(), kMode90->getFps());
}
-TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) {
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) {
using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
- TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+ auto selector = createSelector(kModes_60_90, kModeId90);
EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
@@ -2234,10 +2407,10 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
}
-TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
- TestableRefreshRateSelector selector(kModes_60_120, kModeId120);
+ auto selector = createSelector(kModes_60_120, kModeId120);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
@@ -2256,30 +2429,30 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId30);
+TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId30);
const auto frameRate = 30_Hz;
Fps displayRefreshRate = selector.getActiveMode().getFps();
EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
- selector.setActiveModeId(kModeId60);
+ selector.setActiveMode(kModeId60, 60_Hz);
displayRefreshRate = selector.getActiveMode().getFps();
EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
- selector.setActiveModeId(kModeId72);
+ selector.setActiveMode(kModeId72, 72_Hz);
displayRefreshRate = selector.getActiveMode().getFps();
EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
displayRefreshRate = selector.getActiveMode().getFps();
EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
- selector.setActiveModeId(kModeId120);
+ selector.setActiveMode(kModeId120, 120_Hz);
displayRefreshRate = selector.getActiveMode().getFps();
EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
- selector.setActiveModeId(kModeId90);
+ selector.setActiveMode(kModeId90, 90_Hz);
displayRefreshRate = selector.getActiveMode().getFps();
EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
@@ -2289,7 +2462,7 @@
EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz));
}
-TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
+TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
@@ -2315,22 +2488,72 @@
EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120);
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
layers[0].ownerUid = 1234;
layers[0].desiredRefreshRate = 60_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].vote = LayerVoteType::NoVote;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Min;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Max;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Heuristic;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) {
+ if (GetParam() != Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::ExplicitDefault;
auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
@@ -2342,26 +2565,23 @@
ASSERT_EQ(1u, frameRateOverrides.count(1234));
EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
- layers[0].vote = LayerVoteType::NoVote;
+ layers[0].vote = LayerVoteType::ExplicitExact;
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
{.ownerUid = 5678, .weight = 1.f}};
@@ -2392,9 +2612,16 @@
EXPECT_TRUE(frameRateOverrides.empty());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
layers[0].name = "Test layer";
@@ -2432,88 +2659,87 @@
EXPECT_TRUE(frameRateOverrides.empty());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_Enabled) {
- RefreshRateSelector selector(kModes_60_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_60_120, kModeId120);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
layers[0].ownerUid = 1234;
layers[0].desiredRefreshRate = 30_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
+ const auto expetedFps =
+ GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
- layers[0].vote = LayerVoteType::NoVote;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateSelectorTest,
- getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_EnabledForNativeRefreshRates) {
- RefreshRateSelector selector(kModes_60_120, kModeId120,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::EnabledForNativeRefreshRates});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].name = "Test layer";
- layers[0].ownerUid = 1234;
- layers[0].desiredRefreshRate = 30_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
-
- auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].vote = LayerVoteType::ExplicitExact;
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::NoVote;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ // The render frame rate cannot be greater than the physical refresh rate
+ {
+ const FpsRange physical = {60_Hz, 60_Hz};
+ const FpsRange render = {60_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Invalid,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ {
+ const FpsRange physical = {0_Hz, 120_Hz};
+ const FpsRange render = {0_Hz, 60_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ const auto expectedMaxMode =
+ GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60;
+ EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+ }
+
+ {
+ const FpsRange physical = {0_Hz, 120_Hz};
+ const FpsRange render = {120_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
{
@@ -2566,5 +2792,169 @@
EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
}
+TEST_P(RefreshRateSelectorTest, renderFrameRates) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ // [renderRate, refreshRate]
+ const auto expected = []() -> std::vector<std::pair<Fps, Fps>> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, 30_Hz},
+ {60_Hz, 60_Hz},
+ {72_Hz, 72_Hz},
+ {90_Hz, 90_Hz},
+ {120_Hz, 120_Hz}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz},
+ {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}};
+ }
+ }();
+
+ const auto& primaryRefreshRates = selector.getPrimaryFrameRates();
+ ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+
+ for (size_t i = 0; i < expected.size(); i++) {
+ const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
+ EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
+ EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_60_120, kModeId60);
+
+ constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+ constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz};
+
+ constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
+ /*render*/ k0_120Hz};
+
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+ /*render*/ k0_120Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
+
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
+ /*render*/ k0_60Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
+
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+ /*render*/ k0_60Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+
+ const auto expectedRenderRate =
+ GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz;
+
+ layer.name = "30Hz ExplicitDefault";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::ExplicitDefault;
+ EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ layer.name = "30Hz Heuristic";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::Heuristic;
+ EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+
+ layer.name = "30Hz ExplicitExactOrMultiple";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
+ auto selector = createSelector(kModes_35_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+
+ const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+ bool touchActive) -> DisplayModeId {
+ layers[0].vote = voteType;
+ layers[0].desiredRefreshRate = 90_Hz;
+
+ const auto [ranking, signals] =
+ selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+ // Refresh rate will be chosen by either touch state or idle state.
+ EXPECT_EQ(!touchActive, signals.idle);
+ return ranking.front().frameRateMode.modePtr->getId();
+ };
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}}));
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+ // Idle should be higher precedence than other layer frame rate considerations.
+ selector.setActiveMode(kModeId90, 90_Hz);
+ {
+ constexpr bool kTouchActive = false;
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId35,
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ }
+
+ // Idle should be applied rather than the active mode when there are no layers.
+ EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ constexpr Fps inf = Fps::fromValue(std::numeric_limits<float>::infinity());
+
+ using namespace fps_approx_ops;
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, inf}});
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 8e333a3..3ee53c9 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -58,22 +58,22 @@
SchedulerTest();
static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
- static inline const DisplayModePtr kDisplay1Mode60 =
- createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz);
- static inline const DisplayModePtr kDisplay1Mode120 =
- createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz);
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
+ ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 =
+ ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
- static inline const DisplayModePtr kDisplay2Mode60 =
- createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz);
- static inline const DisplayModePtr kDisplay2Mode120 =
- createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz);
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
+ ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 =
+ ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz));
static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
- static inline const DisplayModePtr kDisplay3Mode60 =
- createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz);
+ static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 =
+ ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz));
static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
std::shared_ptr<RefreshRateSelector> mSelector =
@@ -217,7 +217,9 @@
// If the handle is incorrect, the function should return before
// onModeChange is called.
ConnectionHandle invalidHandle = {.id = 123};
- EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
+ EXPECT_NO_FATAL_FAILURE(
+ mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+ {90_Hz, ftl::as_non_null(mode)}));
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
}
@@ -232,7 +234,7 @@
}
MATCHER(Is120Hz, "") {
- return isApproxEqual(arg.front().modePtr->getFps(), 120_Hz);
+ return isApproxEqual(arg.front().mode.fps, 120_Hz);
}
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
@@ -277,7 +279,7 @@
auto choice = modeChoices.get(kDisplayId1);
ASSERT_TRUE(choice);
- EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode60, globalSignals));
+ EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals));
globalSignals = {.idle = false};
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -287,7 +289,7 @@
choice = modeChoices.get(kDisplayId1);
ASSERT_TRUE(choice);
- EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+ EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
globalSignals = {.touch = true};
mScheduler->replaceTouchTimer(10);
@@ -298,7 +300,7 @@
choice = modeChoices.get(kDisplayId1);
ASSERT_TRUE(choice);
- EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+ EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
mScheduler->unregisterDisplay(kDisplayId1);
EXPECT_FALSE(mScheduler->hasRefreshRateSelectors());
@@ -319,8 +321,11 @@
const GlobalSignals globalSignals = {.idle = true};
expectedChoices =
ftl::init::map<const PhysicalDisplayId&,
- DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
- globalSignals)(kDisplayId2, kDisplay2Mode60,
+ DisplayModeChoice>(kDisplayId1,
+ FrameRateMode{60_Hz, kDisplay1Mode60},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{60_Hz,
+ kDisplay2Mode60},
globalSignals);
std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
@@ -335,8 +340,11 @@
const GlobalSignals globalSignals = {.idle = false};
expectedChoices =
ftl::init::map<const PhysicalDisplayId&,
- DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
- globalSignals)(kDisplayId2, kDisplay2Mode120,
+ DisplayModeChoice>(kDisplayId1,
+ FrameRateMode{120_Hz, kDisplay1Mode120},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{120_Hz,
+ kDisplay2Mode120},
globalSignals);
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -351,8 +359,11 @@
expectedChoices =
ftl::init::map<const PhysicalDisplayId&,
- DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
- globalSignals)(kDisplayId2, kDisplay2Mode120,
+ DisplayModeChoice>(kDisplayId1,
+ FrameRateMode{120_Hz, kDisplay1Mode120},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{120_Hz,
+ kDisplay2Mode120},
globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
@@ -369,13 +380,15 @@
mScheduler->replaceTouchTimer(10);
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
- expectedChoices =
- ftl::init::map<const PhysicalDisplayId&,
- DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
- globalSignals)(kDisplayId2, kDisplay2Mode60,
- globalSignals)(kDisplayId3,
- kDisplay3Mode60,
- globalSignals);
+ expectedChoices = ftl::init::map<
+ const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{60_Hz, kDisplay2Mode60},
+ globalSignals)(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 05d0ebf..ad3bd35 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "mock/MockDisplayModeSpecs.h"
-#include "mock/MockEventThread.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include "DisplayTransactionTestHelpers.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockDisplayModeSpecs.h"
#include <ftl/fake_guard.h>
#include <scheduler/Fps.h>
@@ -42,8 +42,7 @@
PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
- DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
- auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
+ auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(kModes, kModeId60);
setupScheduler(selectorPtr);
@@ -51,7 +50,7 @@
mFlinger.configureAndCommit();
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr))
+ .setDisplayModes(kModes, kModeId60, std::move(selectorPtr))
.inject();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
@@ -78,6 +77,8 @@
static constexpr ui::Size kResolution4K{3840, 2160};
static inline const DisplayModePtr kMode90_4K =
createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+
+ static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
};
void DisplayModeSwitchingTest::setupScheduler(
@@ -116,7 +117,7 @@
ftl::FakeGuard guard(kMainThreadContext);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(mDisplay);
@@ -125,8 +126,8 @@
120));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -139,17 +140,18 @@
Mock::VerifyAndClearExpectations(mComposer);
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
@@ -164,8 +166,8 @@
120));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -175,12 +177,13 @@
hal::HWConfigId(kModeId90.value()), _, _))
.WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -190,7 +193,7 @@
// is still being processed the later call will be respected.
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(mDisplay);
@@ -211,7 +214,7 @@
180));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
EXPECT_CALL(*mComposer,
setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
@@ -221,19 +224,19 @@
mFlinger.commit();
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
mFlinger.commit();
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
}
TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(mDisplay);
@@ -242,8 +245,8 @@
120));
ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -278,7 +281,116 @@
mDisplay = mFlinger.getDisplay(displayToken);
ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
- ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K);
+ ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
+}
+
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+ constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+ constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+ constexpr bool kIsPrimary = false;
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+ kIsPrimary)
+ .setHwcDisplayId(kOuterDisplayHwcId)
+ .inject(&mFlinger, mComposer);
+
+ const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+ kModeId120);
+ },
+ {.displayId = kOuterDisplayId,
+ .hwcDisplayId = kOuterDisplayHwcId,
+ .isPrimary = kIsPrimary});
+
+ const auto& innerDisplay = mDisplay;
+
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(innerDisplay);
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(outerDisplay);
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+
+ // Transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId60);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
new file mode 100644
index 0000000..0e149d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class ExcludeDolbyVisionTest : public DisplayTransactionTest {
+public:
+ void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) {
+ DisplayModes modes;
+ for (DisplayModePtr displayMode : displayModePtrs) {
+ modes.try_emplace(displayMode->getId(), displayMode);
+ }
+
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setDisplayModes(std::move(modes), displayModePtrs[0]->getId())
+ .inject();
+ mDisplay->overrideHdrTypes(types);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+
+ static constexpr DisplayModeId modeId1080p60{0};
+ static constexpr DisplayModeId modeId4k30{1};
+ static constexpr DisplayModeId modeId4k60{2};
+
+ static inline const DisplayModePtr mode1080p60 =
+ createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080));
+ static inline const DisplayModePtr mode4k30 =
+ createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k30NonStandard =
+ createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k60 =
+ createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160));
+
+ const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30,
+ ui::Hdr::HDR10_PLUS};
+};
+
+TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(1, displayModes.size());
+ ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(),
+ displayModes[0].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1);
+}
+
+TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) {
+ injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(2, displayModes.size());
+ for (size_t i = 0; i < displayModes.size(); i++) {
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+ ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2);
+ }
+}
+
+TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes();
+
+ ASSERT_EQ(2, displayHdrTypes.size());
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index bc66961..622717f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,6 @@
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
- mFlinger.mutableActiveDisplayId() = mDisplay->getPhysicalId();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 25857ec..6e50059 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -259,12 +259,6 @@
auto injector = Display::makeFakeExistingDisplayInjector(test);
const auto display = injector.inject();
display->setPowerMode(mode);
- if (injector.physicalDisplay()
- .transform(&display::PhysicalDisplay::isInternal)
- .value_or(false)) {
- test->mFlinger.mutableActiveDisplayId() = display->getPhysicalId();
- }
-
return display;
}
@@ -490,5 +484,38 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
+// TODO(b/262417075)
+TEST_F(SetPowerModeInternalTest, DISABLED_designatesLeaderDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Inject a primary display with uninitialized power mode.
+ constexpr bool kInitPowerMode = false;
+ Case::Display::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ const auto display = injector.inject();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
+ // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
+ // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
+ constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
+ ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
+ mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
+
+ mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should be designated as the leader.
+ EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 0384568..c0796df 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -288,7 +288,7 @@
if constexpr (Case::Display::CONNECTION_TYPE::value) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId());
+ EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
}
}
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f8fe0d..b8a6063 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,8 +44,7 @@
: Scheduler(*this, callback, Feature::kContentDetection) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
- const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
- selectorPtr->getActiveMode().getPhysicalDisplayId());
+ const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr));
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
@@ -88,6 +87,10 @@
Scheduler::unregisterDisplay(displayId);
}
+ std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+ return mLeaderDisplayId;
+ }
+
void setLeaderDisplay(PhysicalDisplayId displayId) {
ftl::FakeGuard guard(kMainThreadContext);
Scheduler::setLeaderDisplay(displayId);
@@ -147,7 +150,7 @@
mPolicy.cachedModeChangedParams.reset();
}
- void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+ void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c15b3c8..2117084 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,6 +30,7 @@
#include <ftl/fake_guard.h>
#include <gui/ScreenCaptureResults.h>
+#include <ui/DynamicDisplayInfo.h>
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
@@ -221,7 +222,7 @@
selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
}
- const auto fps = FTL_FAKE_GUARD(kMainThreadContext, selectorPtr->getActiveMode().getFps());
+ const auto fps = selectorPtr->getActiveMode().fps;
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -470,7 +471,7 @@
void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
- mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+ mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
}
auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
@@ -486,6 +487,11 @@
void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+ void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* dynamicDisplayInfo) {
+ mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -621,7 +627,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mPowerMode = mode;
return *this;
}
@@ -643,16 +649,18 @@
// is much longer lived.
auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
mHwcDisplayType);
-
display->mutableIsConnected() = true;
- display->setPowerMode(mPowerMode);
+
+ if (mPowerMode) {
+ display->setPowerMode(*mPowerMode);
+ }
+
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
.WillRepeatedly(
DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
Return(hal::Error::NONE)));
-
EXPECT_CALL(*composer,
getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
@@ -711,7 +719,7 @@
int32_t mDpiY = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
- hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+ std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
mCapabilities = nullptr;
};
@@ -788,7 +796,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mCreationArgs.initialPowerMode = mode;
return *this;
}
@@ -853,27 +861,32 @@
LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
const auto physicalId = *physicalIdOpt;
+ if (mCreationArgs.isPrimary) {
+ mFlinger.mutableActiveDisplayId() = physicalId;
+ }
+
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
const auto activeMode = modes.get(activeModeId);
LOG_ALWAYS_FATAL_IF(!activeMode);
+ const auto fps = activeMode->get()->getFps();
state.physical = {.id = physicalId,
.hwcDisplayId = *mHwcDisplayId,
.activeMode = activeMode->get()};
- const auto it = mFlinger.mutablePhysicalDisplays()
- .emplace_or_replace(physicalId, mDisplayToken, physicalId,
- *mConnectionType, std::move(modes),
- ui::ColorModes(), std::nullopt)
- .first;
+ mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
+ physicalId, *mConnectionType,
+ std::move(modes),
+ ui::ColorModes(),
+ std::nullopt);
if (mFlinger.scheduler()) {
mFlinger.scheduler()->registerDisplay(physicalId,
display->holdRefreshRateSelector());
}
- display->setActiveMode(activeModeId, it->second.snapshot());
+ display->setActiveMode(activeModeId, fps, fps);
}
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 6ffc039..a9ae1d3 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -44,11 +44,14 @@
namespace {
using testing::_;
+using testing::AllOf;
using testing::AnyNumber;
using testing::Contains;
+using testing::ElementsAre;
using testing::HasSubstr;
using testing::InSequence;
using testing::Not;
+using testing::Property;
using testing::SizeIs;
using testing::StrEq;
using testing::UnorderedElementsAre;
@@ -645,7 +648,7 @@
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
ASSERT_EQ(1, globalProto.stats_size());
- const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats(0);
ASSERT_TRUE(layerProto.has_layer_name());
EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
ASSERT_TRUE(layerProto.has_total_frames());
@@ -653,7 +656,7 @@
ASSERT_EQ(6, layerProto.deltas_size());
for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
ASSERT_EQ(1, deltaProto.histograms_size());
- const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0);
EXPECT_EQ(1, histogramProto.frame_count());
if ("post2acquire" == deltaProto.delta_name()) {
EXPECT_EQ(1, histogramProto.time_millis());
@@ -673,6 +676,46 @@
}
}
+using LayerProto = SFTimeStatsLayerProto;
+using DeltaProto = SFTimeStatsDeltaProto;
+using BucketProto = SFTimeStatsHistogramBucketProto;
+
+TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta
+ // Slightly unstable frames
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_THAT(globalProto.stats(),
+ ElementsAre(AllOf(
+ Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)),
+ Property(&LayerProto::total_frames, 4),
+ Property(&LayerProto::deltas,
+ Contains(AllOf(Property(&DeltaProto::delta_name,
+ "present2presentDelta"),
+ Property(&DeltaProto::histograms,
+ UnorderedElementsAre(
+ AllOf(Property(&BucketProto::
+ time_millis,
+ 0),
+ Property(&BucketProto::
+ frame_count,
+ 1)),
+ AllOf(Property(&BucketProto::
+ time_millis,
+ 1),
+ Property(&BucketProto::
+ frame_count,
+ 2))))))))));
+}
+
TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -1099,8 +1142,10 @@
kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1234,8 +1279,10 @@
GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1320,16 +1367,19 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
// The first time record is never uploaded to stats.
- ASSERT_EQ(atomList.atom_size(), 3);
+ ASSERT_EQ(atomList.atom_size(), 4);
// Layers are ordered based on the hash in LayerStatsKey. For this test, the order happens to
- // be: 0 - Battery 1 - Performance 2 - Standard
+ // be: 0 - Battery 1 - Custom 2 - Performance 3 - Standard
const SurfaceflingerStatsLayerInfo& atom0 = atomList.atom(0);
EXPECT_EQ(atom0.layer_name(), genLayerName(LAYER_ID_0));
@@ -1364,7 +1414,7 @@
EXPECT_EQ(atom1.uid(), UID_0);
EXPECT_EQ(atom1.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
EXPECT_EQ(atom1.render_rate_bucket(), RENDER_RATE_BUCKET_0);
- EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+ EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM);
const SurfaceflingerStatsLayerInfo& atom2 = atomList.atom(2);
@@ -1377,12 +1427,30 @@
EXPECT_THAT(atom2.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
EXPECT_THAT(atom2.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
EXPECT_THAT(atom2.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
- EXPECT_EQ(atom2.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
- EXPECT_EQ(atom2.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom2.late_acquire_frames(), 0);
+ EXPECT_EQ(atom2.bad_desired_present_frames(), 0);
EXPECT_EQ(atom2.uid(), UID_0);
EXPECT_EQ(atom2.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
EXPECT_EQ(atom2.render_rate_bucket(), RENDER_RATE_BUCKET_0);
- EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
+ EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+
+ const SurfaceflingerStatsLayerInfo& atom3 = atomList.atom(3);
+
+ EXPECT_EQ(atom3.layer_name(), genLayerName(LAYER_ID_0));
+ EXPECT_EQ(atom3.total_frames(), 1);
+ EXPECT_EQ(atom3.dropped_frames(), 0);
+ EXPECT_THAT(atom3.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom3.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+ EXPECT_THAT(atom3.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+ EXPECT_THAT(atom3.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+ EXPECT_THAT(atom3.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_THAT(atom3.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+ EXPECT_EQ(atom3.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+ EXPECT_EQ(atom3.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+ EXPECT_EQ(atom3.uid(), UID_0);
+ EXPECT_EQ(atom3.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+ EXPECT_EQ(atom3.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+ EXPECT_EQ(atom3.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
}
TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
@@ -1393,8 +1461,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1418,8 +1488,10 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1437,8 +1509,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1457,8 +1531,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 488d4a9..d84698f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -42,6 +42,8 @@
using testing::Return;
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using frontend::TransactionHandler;
+
constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
class TransactionApplicationTest : public testing::Test {
public:
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 09d002f..1173d1c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -126,7 +126,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- dequeueTime, FrameTimelineInfo{}, 0);
+ dequeueTime, FrameTimelineInfo{});
commitTransaction(layer.get());
nsecs_t latchTime = 25;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 7dfbcc0..ae03db4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -131,7 +131,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -166,7 +166,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -183,7 +183,7 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -229,7 +229,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -264,7 +264,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -307,7 +307,7 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -352,7 +352,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -367,7 +367,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -404,7 +404,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -424,7 +424,7 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -445,7 +445,7 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2, 0);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -494,7 +494,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 2da266b..47c2dee 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,6 +54,7 @@
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ void setDivisor(unsigned) final {}
void dump(std::string&) const final {}
private:
@@ -91,6 +92,7 @@
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ void setDivisor(unsigned) final {}
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f660753..2b86e94 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -55,6 +55,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(void, setDivisor, (unsigned), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 74d2b7d..3095e8a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -532,6 +532,26 @@
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
}
+TEST_F(VSyncPredictorTest, setDivisorIsRespected) {
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+
+ tracker.setDivisor(3);
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index a35ff96..8bd689a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,6 +50,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(void, setDivisor, (unsigned), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
index c2c3d77..5654691 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
@@ -36,7 +36,7 @@
MockAidlPowerHalWrapper();
~MockAidlPowerHalWrapper() override;
MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
- MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(bool, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
MOCK_METHOD(void, restartPowerHintSession, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5ee38ec..2f16b7b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -32,7 +32,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::composer::V2_1::Config;
@@ -99,6 +98,8 @@
Error(Display, nsecs_t, uint32_t*, uint32_t*, int*, uint32_t*));
MOCK_METHOD4(setCursorPosition, Error(Display, Layer, int32_t, int32_t));
MOCK_METHOD5(setLayerBuffer, Error(Display, Layer, uint32_t, const sp<GraphicBuffer>&, int));
+ MOCK_METHOD4(setLayerBufferSlotsToClear,
+ Error(Display, Layer, const std::vector<uint32_t>&, uint32_t));
MOCK_METHOD3(setLayerSurfaceDamage,
Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
MOCK_METHOD3(setLayerBlendMode, Error(Display, Layer, IComposerClient::BlendMode));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index c78b6bd..3b36361 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -38,4 +38,24 @@
return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
}
+inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
+ return DisplayMode::Builder(modePtr->getHwcId())
+ .setId(modePtr->getId())
+ .setPhysicalDisplayId(displayId)
+ .setVsyncPeriod(modePtr->getVsyncPeriod())
+ .setGroup(modePtr->getGroup())
+ .setResolution(modePtr->getResolution())
+ .build();
+}
+
+inline DisplayModes cloneForDisplay(PhysicalDisplayId displayId, const DisplayModes& modes) {
+ DisplayModes clones;
+
+ for (const auto& [id, modePtr] : modes) {
+ clones.try_emplace(id, cloneForDisplay(displayId, modePtr));
+ }
+
+ return clones;
+}
+
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fb1b394..7fc625c 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -32,7 +32,7 @@
MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
(override));
MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
- MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ded6806..f8567bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,7 +34,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
+ MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
MOCK_METHOD2(onFrameRateOverridesChanged,
void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));
diff --git a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
similarity index 71%
copy from libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
copy to services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index a8bc994..ef9cd9b 100644
--- a/libs/gui/aidl/android/gui/SupportedBufferCombinations.aidl
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.gui;
+#pragma once
-/** @hide */
-parcelable SupportedBufferCombinations {
- int[] pixelFormats;
- int[] dataspaces;
-}
+#include <scheduler/FrameRateMode.h>
+
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
+ EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 0dee800..86fbadc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
TimeStats();
~TimeStats() override;
- MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
+ MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*));
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 5b0c1f3..6893154 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,6 +34,7 @@
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+ MOCK_METHOD(void, setDivisor, (unsigned), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
deleted file mode 100644
index 80e9a3c..0000000
--- a/services/vr/hardware_composer/Android.bp
+++ /dev/null
@@ -1,134 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_shared {
- name: "libvr_hwc-hal",
-
- system_ext_specific: true,
-
- srcs: [
- "impl/vr_hwc.cpp",
- "impl/vr_composer_client.cpp",
- ],
-
- static_libs: [
- "libbroadcastring",
- "libdisplay",
- ],
-
- shared_libs: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- "android.hardware.graphics.composer@2.1-resources",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
- "libbase",
- "libbufferhubqueue",
- "libbinder",
- "libcutils",
- "libfmq",
- "libhardware",
- "libhidlbase",
- "liblog",
- "libsync",
- "libui",
- "libutils",
- "libpdx_default_transport",
- ],
-
- header_libs: [
- "android.hardware.graphics.composer@2.1-command-buffer",
- "android.hardware.graphics.composer@2.3-hal",
- ],
-
- export_header_lib_headers: [
- "android.hardware.graphics.composer@2.3-hal",
- ],
-
- export_static_lib_headers: [
- "libdisplay",
- ],
-
- export_shared_lib_headers: [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- ],
-
- export_include_dirs: ["."],
-
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-Wall",
- "-Werror",
- "-Wno-error=unused-private-field",
- // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
- "-Wno-sign-compare",
- "-Wno-unused-parameter",
- ],
-
-}
-
-cc_library_static {
- name: "libvr_hwc-impl",
- srcs: [
- "vr_composer.cpp",
- ],
- static_libs: [
- "libvr_hwc-binder",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
- export_shared_lib_headers: [
- "libvr_hwc-hal",
- ],
- cflags: [
- "-DLOG_TAG=\"vr_hwc\"",
- "-Wall",
- "-Werror",
- ],
-}
-
-cc_test {
- name: "vr_hwc_test",
- gtest: true,
- srcs: ["tests/vr_composer_test.cpp"],
- static_libs: [
- "libgtest",
- "libvr_hwc-impl",
- // NOTE: This needs to be included after the *-impl lib otherwise the
- // symbols in the *-binder library get optimized out.
- "libvr_hwc-binder",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- // warnings in vr_composer_test.cpp to be fixed after merge of goog/master
- "-Wno-sign-compare",
- "-Wno-unused-parameter",
- ],
- shared_libs: [
- "libbase",
- "libbinder",
- "liblog",
- "libui",
- "libutils",
- ],
-}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
deleted file mode 100644
index fa71ed7..0000000
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_library_static {
- name: "libvr_hwc-binder",
- srcs: [
- "android/dvr/IVrComposer.aidl",
- "android/dvr/IVrComposerCallback.aidl",
- "android/dvr/parcelable_composer_frame.cpp",
- "android/dvr/parcelable_composer_layer.cpp",
- "android/dvr/parcelable_unique_fd.cpp",
- ],
- aidl: {
- local_include_dirs: ["."],
- export_aidl_headers: true,
- },
- export_include_dirs: ["."],
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
-
- shared_libs: [
- "libbinder",
- "libui",
- "libutils",
- "libvr_hwc-hal",
- ],
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
deleted file mode 100644
index be1ec5b..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposer.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-package android.dvr;
-
-import android.dvr.IVrComposerCallback;
-
-/**
- * Service interface exposed by VR HWC exposed to system apps which allows one
- * system app to connect to get SurfaceFlinger's outputs (all displays). This
- * is active when SurfaceFlinger is in VR mode, where all 2D output is
- * redirected to VR HWC.
- *
- * @hide */
-interface IVrComposer
-{
- const String SERVICE_NAME = "vr_hwc";
-
- /**
- * Registers a callback used to receive frame notifications.
- */
- void registerObserver(in IVrComposerCallback callback);
-
- /**
- * Clears a previously registered frame notification callback.
- */
- void clearObserver();
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl b/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
deleted file mode 100644
index aa70de1..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/IVrComposerCallback.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-package android.dvr;
-
-import android.dvr.ParcelableComposerFrame;
-import android.dvr.ParcelableUniqueFd;
-
-/**
- * A system app will implement and register this callback with VRComposer
- * to receive the layers SurfaceFlinger presented when in VR mode.
- *
- * @hide */
-interface IVrComposerCallback {
- /**
- * Called by the VR HWC service when a new frame is ready to be presented.
- *
- * @param frame The new frame VR HWC wants to present.
- * @return A fence FD used to signal when the previous frame is no longer
- * used by the client. This may be an invalid fence (-1) if the client is not
- * using the previous frame, in which case the previous frame may be re-used
- * at any point in time.
- */
- ParcelableUniqueFd onNewFrame(in ParcelableComposerFrame frame);
-}
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
deleted file mode 100644
index 84abc19..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerFrame.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerFrame cpp_header "android/dvr/parcelable_composer_frame.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
deleted file mode 100644
index a200345..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableComposerLayer.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableComposerLayer cpp_header "android/dvr/parcelable_composer_layer.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl b/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
deleted file mode 100644
index eee9d13..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/ParcelableUniqueFd.aidl
+++ /dev/null
@@ -1,3 +0,0 @@
-package android.dvr;
-
-parcelable ParcelableUniqueFd cpp_header "android/dvr/parcelable_unique_fd.h";
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
deleted file mode 100644
index db7d5dc..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_frame.h"
-
-#include <binder/Parcel.h>
-
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerFrame::ParcelableComposerFrame() {}
-
-ParcelableComposerFrame::ParcelableComposerFrame(
- const ComposerView::Frame& frame)
- : frame_(frame) {}
-
-ParcelableComposerFrame::~ParcelableComposerFrame() {}
-
-status_t ParcelableComposerFrame::writeToParcel(Parcel* parcel) const {
- status_t ret = parcel->writeUint64(frame_.display_id);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(frame_.display_width);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(frame_.display_height);
- if (ret != OK) return ret;
-
- ret = parcel->writeBool(frame_.removed);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.active_config));
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.color_mode));
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.power_mode));
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(frame_.vsync_enabled));
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(frame_.color_transform_hint);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < 16; i++) {
- ret = parcel->writeFloat(frame_.color_transform[i]);
- if (ret != OK) return ret;
- }
-
- std::vector<ParcelableComposerLayer> layers;
- for (size_t i = 0; i < frame_.layers.size(); ++i)
- layers.push_back(ParcelableComposerLayer(frame_.layers[i]));
-
- ret = parcel->writeParcelableVector(layers);
-
- return ret;
-}
-
-status_t ParcelableComposerFrame::readFromParcel(const Parcel* parcel) {
- status_t ret = parcel->readUint64(&frame_.display_id);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&frame_.display_width);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&frame_.display_height);
- if (ret != OK) return ret;
-
- ret = parcel->readBool(&frame_.removed);
- if (ret != OK) return ret;
-
- uint32_t value;
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.active_config = static_cast<Config>(value);
-
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.color_mode = static_cast<ColorMode>(value);
-
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.power_mode = static_cast<IComposerClient::PowerMode>(value);
-
- ret = parcel->readUint32(&value);
- if (ret != OK) return ret;
- frame_.vsync_enabled = static_cast<IComposerClient::Vsync>(value);
-
- ret = parcel->readInt32(&frame_.color_transform_hint);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < 16; i++) {
- ret = parcel->readFloat(&frame_.color_transform[i]);
- if (ret != OK) return ret;
- }
-
- std::vector<ParcelableComposerLayer> layers;
- ret = parcel->readParcelableVector(&layers);
- if (ret != OK) return ret;
-
- frame_.layers.clear();
- for (size_t i = 0; i < layers.size(); ++i)
- frame_.layers.push_back(layers[i].layer());
-
- return ret;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
deleted file mode 100644
index a82df7f..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_frame.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerFrame : public Parcelable {
- public:
- ParcelableComposerFrame();
- explicit ParcelableComposerFrame(const ComposerView::Frame& frame);
- ~ParcelableComposerFrame() override;
-
- ComposerView::Frame frame() const { return frame_; }
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- ComposerView::Frame frame_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_FRAME_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
deleted file mode 100644
index c3621eb..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "aidl/android/dvr/parcelable_composer_layer.h"
-
-#include <binder/Parcel.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicBufferMapper.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableComposerLayer::ParcelableComposerLayer() {}
-
-ParcelableComposerLayer::ParcelableComposerLayer(
- const ComposerView::ComposerLayer& layer) : layer_(layer) {}
-
-ParcelableComposerLayer::~ParcelableComposerLayer() {}
-
-status_t ParcelableComposerLayer::writeToParcel(Parcel* parcel) const {
- status_t ret = parcel->writeUint64(layer_.id);
- if (ret != OK) return ret;
-
- ret = parcel->write(*layer_.buffer);
- if (ret != OK) return ret;
-
- ret = parcel->writeBool(layer_.fence->isValid());
- if (ret != OK) return ret;
-
- if (layer_.fence->isValid()) {
- ret = parcel->writeFileDescriptor(layer_.fence->dup(), true);
- if (ret != OK) return ret;
- }
-
- ret = parcel->writeInt32(layer_.display_frame.left);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.display_frame.top);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.display_frame.right);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.display_frame.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.left);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.top);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.right);
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.crop.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(static_cast<int32_t>(layer_.blend_mode));
- if (ret != OK) return ret;
-
- ret = parcel->writeFloat(layer_.alpha);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(layer_.type);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(layer_.app_id);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(layer_.z_order);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.cursor_x);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.cursor_y);
- if (ret != OK) return ret;
-
- uint32_t color = layer_.color.r |
- (static_cast<uint32_t>(layer_.color.g) << 8) |
- (static_cast<uint32_t>(layer_.color.b) << 16) |
- (static_cast<uint32_t>(layer_.color.a) << 24);
- ret = parcel->writeUint32(color);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.dataspace);
- if (ret != OK) return ret;
-
- ret = parcel->writeInt32(layer_.transform);
- if (ret != OK) return ret;
-
- ret = parcel->writeUint32(static_cast<uint32_t>(layer_.visible_regions.size()));
- if (ret != OK) return ret;
-
- for (auto& rect: layer_.visible_regions) {
- ret = parcel->writeInt32(rect.left);
- ret = parcel->writeInt32(rect.top);
- ret = parcel->writeInt32(rect.right);
- ret = parcel->writeInt32(rect.bottom);
- if (ret != OK) return ret;
- }
-
- ret = parcel->writeUint32(static_cast<uint32_t>(layer_.damaged_regions.size()));
- if (ret != OK) return ret;
-
- for (auto& rect: layer_.damaged_regions) {
- ret = parcel->writeInt32(rect.left);
- ret = parcel->writeInt32(rect.top);
- ret = parcel->writeInt32(rect.right);
- ret = parcel->writeInt32(rect.bottom);
- if (ret != OK) return ret;
- }
-
- return OK;
-}
-
-status_t ParcelableComposerLayer::readFromParcel(const Parcel* parcel) {
- status_t ret = parcel->readUint64(&layer_.id);
- if (ret != OK) return ret;
-
- layer_.buffer = new GraphicBuffer();
- ret = parcel->read(*layer_.buffer);
- if (ret != OK) {
- layer_.buffer.clear();
- return ret;
- }
-
- bool has_fence = 0;
- ret = parcel->readBool(&has_fence);
- if (ret != OK) return ret;
-
- if (has_fence)
- layer_.fence = new Fence(dup(parcel->readFileDescriptor()));
- else
- layer_.fence = new Fence();
-
- ret = parcel->readInt32(&layer_.display_frame.left);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.display_frame.top);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.display_frame.right);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.display_frame.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.left);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.top);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.right);
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.crop.bottom);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(reinterpret_cast<int32_t*>(&layer_.blend_mode));
- if (ret != OK) return ret;
-
- ret = parcel->readFloat(&layer_.alpha);
- if (ret != OK) return ret;
-
- ret = parcel->readUint32(&layer_.type);
- if (ret != OK) return ret;
-
- ret = parcel->readUint32(&layer_.app_id);
- if (ret != OK) return ret;
-
- ret = parcel->readUint32(&layer_.z_order);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.cursor_x);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.cursor_y);
- if (ret != OK) return ret;
-
- uint32_t color;
- ret = parcel->readUint32(&color);
- if (ret != OK) return ret;
- layer_.color.r = color & 0xFF;
- layer_.color.g = (color >> 8) & 0xFF;
- layer_.color.b = (color >> 16) & 0xFF;
- layer_.color.a = (color >> 24) & 0xFF;
-
- ret = parcel->readInt32(&layer_.dataspace);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&layer_.transform);
- if (ret != OK) return ret;
-
- uint32_t size;
- ret = parcel->readUint32(&size);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < size; i++) {
- hwc_rect_t rect;
- ret = parcel->readInt32(&rect.left);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.top);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.right);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.bottom);
- if (ret != OK) return ret;
-
- layer_.visible_regions.push_back(rect);
- }
-
- ret = parcel->readUint32(&size);
- if (ret != OK) return ret;
-
- for(size_t i = 0; i < size; i++) {
- hwc_rect_t rect;
- ret = parcel->readInt32(&rect.left);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.top);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.right);
- if (ret != OK) return ret;
-
- ret = parcel->readInt32(&rect.bottom);
- if (ret != OK) return ret;
-
- layer_.damaged_regions.push_back(rect);
- }
-
- return OK;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
deleted file mode 100644
index 6d2ac09..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_composer_layer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
-
-#include <binder/Parcelable.h>
-#include <impl/vr_hwc.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-class ParcelableComposerLayer : public Parcelable {
- public:
- ParcelableComposerLayer();
- explicit ParcelableComposerLayer(const ComposerView::ComposerLayer& layer);
- ~ParcelableComposerLayer() override;
-
- ComposerView::ComposerLayer layer() const { return layer_; }
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- ComposerView::ComposerLayer layer_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_COMPOSER_LAYER_H
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
deleted file mode 100644
index 9486f3c..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "android/dvr/parcelable_unique_fd.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace dvr {
-
-ParcelableUniqueFd::ParcelableUniqueFd() {}
-
-ParcelableUniqueFd::ParcelableUniqueFd(const base::unique_fd& fence)
- : fence_(dup(fence.get())) {}
-
-ParcelableUniqueFd::~ParcelableUniqueFd() {}
-
-status_t ParcelableUniqueFd::writeToParcel(Parcel* parcel) const {
- status_t ret = parcel->writeBool(fence_.get() >= 0);
- if (ret != OK) return ret;
-
- if (fence_.get() >= 0)
- ret = parcel->writeUniqueFileDescriptor(fence_);
-
- return ret;
-}
-
-status_t ParcelableUniqueFd::readFromParcel(const Parcel* parcel) {
- bool has_fence = 0;
- status_t ret = parcel->readBool(&has_fence);
- if (ret != OK) return ret;
-
- if (has_fence)
- ret = parcel->readUniqueFileDescriptor(&fence_);
-
- return ret;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h b/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
deleted file mode 100644
index c4216f6..0000000
--- a/services/vr/hardware_composer/aidl/android/dvr/parcelable_unique_fd.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
-
-#include <android-base/unique_fd.h>
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace dvr {
-
-// Provide a wrapper to serialized base::unique_fd. The wrapper also handles the
-// case where the FD is invalid (-1), unlike FileDescriptor which expects a
-// valid FD.
-class ParcelableUniqueFd : public Parcelable {
- public:
- ParcelableUniqueFd();
- explicit ParcelableUniqueFd(const base::unique_fd& fence);
- ~ParcelableUniqueFd() override;
-
- void set_fence(const base::unique_fd& fence) {
- fence_.reset(dup(fence.get()));
- }
- base::unique_fd fence() const { return base::unique_fd(dup(fence_.get())); }
-
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- private:
- base::unique_fd fence_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_AIDL_ANDROID_DVR_PARCELABLE_UNIQUE_FD_H
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
deleted file mode 100644
index dd1603d..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <hardware/gralloc.h>
-#include <hardware/gralloc1.h>
-#include <log/log.h>
-
-#include <memory>
-
-#include "impl/vr_hwc.h"
-#include "impl/vr_composer_client.h"
-
-namespace android {
-namespace dvr {
-
-using android::frameworks::vr::composer::V2_0::IVrComposerClient;
-
-VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
- : ComposerClient(&hal), mVrHal(hal) {
- if (!init()) {
- LOG_ALWAYS_FATAL("failed to initialize VrComposerClient");
- }
-}
-
-VrComposerClient::~VrComposerClient() {}
-
-std::unique_ptr<ComposerCommandEngine>
-VrComposerClient::createCommandEngine() {
- return std::make_unique<VrCommandEngine>(*this);
-}
-
-VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
- : ComposerCommandEngine(client.mHal, client.mResources.get()),
- mVrHal(client.mVrHal) {}
-
-VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
-
-bool VrComposerClient::VrCommandEngine::executeCommand(
- hardware::graphics::composer::V2_1::IComposerClient::Command command,
- uint16_t length) {
- IVrComposerClient::VrCommand vrCommand =
- static_cast<IVrComposerClient::VrCommand>(command);
- switch (vrCommand) {
- case IVrComposerClient::VrCommand::SET_LAYER_INFO:
- return executeSetLayerInfo(length);
- case IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA:
- return executeSetClientTargetMetadata(length);
- case IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA:
- return executeSetLayerBufferMetadata(length);
- default:
- return ComposerCommandEngine::executeCommand(command, length);
- }
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerInfo(uint16_t length) {
- if (length != 2) {
- return false;
- }
-
- auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
- if (err != Error::NONE) {
- mWriter->setError(getCommandLoc(), err);
- }
-
- return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetClientTargetMetadata(
- uint16_t length) {
- if (length != 7)
- return false;
-
- auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
- if (err != Error::NONE)
- mWriter->setError(getCommandLoc(), err);
-
- return true;
-}
-
-bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata(
- uint16_t length) {
- if (length != 7)
- return false;
-
- auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
- readBufferMetadata());
- if (err != Error::NONE)
- mWriter->setError(getCommandLoc(), err);
-
- return true;
-}
-
-IVrComposerClient::BufferMetadata
-VrComposerClient::VrCommandEngine::readBufferMetadata() {
- IVrComposerClient::BufferMetadata metadata = {
- .width = read(),
- .height = read(),
- .stride = read(),
- .layerCount = read(),
- .format =
- static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
- readSigned()),
- .usage = read64(),
- };
- return metadata;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
deleted file mode 100644
index 1b2b5f4..0000000
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
-
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.1/ComposerCommandEngine.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-
-namespace android {
-namespace dvr {
-
-class VrHwc;
-
-using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine;
-using hardware::graphics::composer::V2_3::hal::ComposerHal;
-using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl;
-
-using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>;
-
-class VrComposerClient : public ComposerClient {
- public:
- explicit VrComposerClient(android::dvr::VrHwc& hal);
- virtual ~VrComposerClient();
-
- private:
- class VrCommandEngine : public ComposerCommandEngine {
- public:
- explicit VrCommandEngine(VrComposerClient& client);
- ~VrCommandEngine() override;
-
- bool executeCommand(
- hardware::graphics::composer::V2_1::IComposerClient::Command command,
- uint16_t length) override;
-
- private:
- bool executeSetLayerInfo(uint16_t length);
- bool executeSetClientTargetMetadata(uint16_t length);
- bool executeSetLayerBufferMetadata(uint16_t length);
-
- IVrComposerClient::BufferMetadata readBufferMetadata();
-
- android::dvr::VrHwc& mVrHal;
-
- VrCommandEngine(const VrCommandEngine&) = delete;
- void operator=(const VrCommandEngine&) = delete;
- };
-
- VrComposerClient(const VrComposerClient&) = delete;
- void operator=(const VrComposerClient&) = delete;
-
- std::unique_ptr<ComposerCommandEngine> createCommandEngine() override;
- dvr::VrHwc& mVrHal;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
deleted file mode 100644
index e530b16..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ /dev/null
@@ -1,1178 +0,0 @@
-/*
- * Copyright 2016 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 "impl/vr_hwc.h"
-
-#include "android-base/stringprintf.h"
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <private/dvr/display_client.h>
-#include <ui/Fence.h>
-#include <utils/Trace.h>
-
-#include <mutex>
-
-#include "vr_composer_client.h"
-
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::base::StringPrintf;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-
-namespace types = android::hardware::graphics::common;
-
-namespace android {
-namespace dvr {
-namespace {
-
-const Display kDefaultDisplayId = 1;
-const Config kDefaultConfigId = 1;
-
-sp<GraphicBuffer> CreateGraphicBuffer(
- const native_handle_t* handle,
- const IVrComposerClient::BufferMetadata& metadata) {
- sp<GraphicBuffer> buffer = new GraphicBuffer(
- handle, GraphicBuffer::CLONE_HANDLE, metadata.width, metadata.height,
- static_cast<int32_t>(metadata.format), metadata.layerCount,
- metadata.usage, metadata.stride);
- if (buffer->initCheck() != OK) {
- ALOGE("Failed to create graphic buffer");
- return nullptr;
- }
-
- return buffer;
-}
-
-void GetPrimaryDisplaySize(int32_t* width, int32_t* height) {
- *width = 1080;
- *height = 1920;
-
- int error = 0;
- auto display_client = display::DisplayClient::Create(&error);
- if (!display_client) {
- ALOGE("Could not connect to display service : %s(%d)", strerror(error),
- error);
- return;
- }
-
- auto status = display_client->GetDisplayMetrics();
- if (!status) {
- ALOGE("Could not get display metrics from display service : %s(%d)",
- status.GetErrorMessage().c_str(), status.error());
- return;
- }
-
- *width = status.get().display_width;
- *height = status.get().display_height;
-}
-
-} // namespace
-
-HwcDisplay::HwcDisplay(int32_t width, int32_t height)
- : width_(width), height_(height) {}
-
-HwcDisplay::~HwcDisplay() {}
-
-bool HwcDisplay::SetClientTarget(const native_handle_t* handle,
- base::unique_fd fence) {
- if (handle)
- buffer_ = CreateGraphicBuffer(handle, buffer_metadata_);
-
- fence_ = new Fence(fence.release());
- return true;
-}
-
-void HwcDisplay::SetClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata) {
- buffer_metadata_ = metadata;
-}
-
-HwcLayer* HwcDisplay::CreateLayer() {
- uint64_t layer_id = layer_ids_++;
- layers_.push_back(HwcLayer(layer_id));
- return &layers_.back();
-}
-
-HwcLayer* HwcDisplay::GetLayer(Layer id) {
- for (size_t i = 0; i < layers_.size(); ++i)
- if (layers_[i].info.id == id)
- return &layers_[i];
-
- return nullptr;
-}
-
-bool HwcDisplay::DestroyLayer(Layer id) {
- for (auto it = layers_.begin(); it != layers_.end(); ++it) {
- if (it->info.id == id) {
- layers_.erase(it);
- return true;
- }
- }
-
- return false;
-}
-
-void HwcDisplay::GetChangedCompositionTypes(
- std::vector<Layer>* layer_ids,
- std::vector<IComposerClient::Composition>* types) {
- std::sort(layers_.begin(), layers_.end(),
- [](const auto& lhs, const auto& rhs) {
- return lhs.info.z_order < rhs.info.z_order;
- });
-
- const size_t no_layer = std::numeric_limits<size_t>::max();
- size_t first_client_layer = no_layer, last_client_layer = no_layer;
- for (size_t i = 0; i < layers_.size(); ++i) {
- switch (layers_[i].composition_type) {
- case IComposerClient::Composition::SOLID_COLOR:
- case IComposerClient::Composition::CURSOR:
- case IComposerClient::Composition::SIDEBAND:
- if (first_client_layer == no_layer)
- first_client_layer = i;
-
- last_client_layer = i;
- break;
- default:
- break;
- }
- }
-
- for (size_t i = 0; i < layers_.size(); ++i) {
- if (i >= first_client_layer && i <= last_client_layer) {
- if (layers_[i].composition_type != IComposerClient::Composition::CLIENT) {
- layer_ids->push_back(layers_[i].info.id);
- types->push_back(IComposerClient::Composition::CLIENT);
- layers_[i].composition_type = IComposerClient::Composition::CLIENT;
- }
-
- continue;
- }
-
- if (layers_[i].composition_type != IComposerClient::Composition::DEVICE) {
- layer_ids->push_back(layers_[i].info.id);
- types->push_back(IComposerClient::Composition::DEVICE);
- layers_[i].composition_type = IComposerClient::Composition::DEVICE;
- }
- }
-}
-
-Error HwcDisplay::GetFrame(
- std::vector<ComposerView::ComposerLayer>* out_frames) {
- bool queued_client_target = false;
- std::vector<ComposerView::ComposerLayer> frame;
- for (const auto& layer : layers_) {
- if (layer.composition_type == IComposerClient::Composition::CLIENT) {
- if (queued_client_target)
- continue;
-
- if (!buffer_.get()) {
- ALOGE("Client composition requested but no client target buffer");
- return Error::BAD_LAYER;
- }
-
- ComposerView::ComposerLayer client_target_layer = {
- .buffer = buffer_,
- .fence = fence_.get() ? fence_ : new Fence(-1),
- .display_frame = {0, 0, static_cast<int32_t>(buffer_->getWidth()),
- static_cast<int32_t>(buffer_->getHeight())},
- .crop = {0.0f, 0.0f, static_cast<float>(buffer_->getWidth()),
- static_cast<float>(buffer_->getHeight())},
- .blend_mode = IComposerClient::BlendMode::NONE,
- };
-
- frame.push_back(client_target_layer);
- queued_client_target = true;
- } else {
- if (!layer.info.buffer.get() || !layer.info.fence.get()) {
- ALOGV("Layer requested without valid buffer");
- continue;
- }
-
- frame.push_back(layer.info);
- }
- }
-
- out_frames->swap(frame);
- return Error::NONE;
-}
-
-std::vector<Layer> HwcDisplay::UpdateLastFrameAndGetLastFrameLayers() {
- std::vector<Layer> last_frame_layers;
- last_frame_layers.swap(last_frame_layers_ids_);
-
- for (const auto& layer : layers_)
- last_frame_layers_ids_.push_back(layer.info.id);
-
- return last_frame_layers;
-}
-
-void HwcDisplay::SetColorTransform(const float* matrix, int32_t hint) {
- color_transform_hint_ = hint;
- if (matrix)
- memcpy(color_transform_, matrix, sizeof(color_transform_));
-}
-
-void HwcDisplay::dumpDebugInfo(std::string* result) const {
- if (!result) {
- return;
- }
- *result += StringPrintf("HwcDisplay: width: %d, height: %d, layers size: %zu, colormode: %d\
- , config: %d\n", width_, height_, layers_.size(), color_mode_, active_config_);
- *result += StringPrintf("HwcDisplay buffer metadata: width: %d, height: %d, stride: %d,\
- layerCount: %d, pixelFormat: %d\n", buffer_metadata_.width, buffer_metadata_.height,
- buffer_metadata_.stride, buffer_metadata_.layerCount, buffer_metadata_.format);
- for (const auto& layer : layers_) {
- layer.dumpDebugInfo(result);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// VrHwcClient
-
-VrHwc::VrHwc() {
- vsync_callback_ = new VsyncCallback;
-}
-
-VrHwc::~VrHwc() {
- vsync_callback_->SetEventCallback(nullptr);
-}
-
-bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
-
-void VrHwc::registerEventCallback(EventCallback* callback) {
- std::unique_lock<std::mutex> lock(mutex_);
- event_callback_ = callback;
- int32_t width, height;
- GetPrimaryDisplaySize(&width, &height);
- // Create the primary display late to avoid initialization issues between
- // VR HWC and SurfaceFlinger.
- displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-
- // Surface flinger will make calls back into vr_hwc when it receives the
- // onHotplug() call, so it's important to release mutex_ here.
- lock.unlock();
- event_callback_->onHotplug(kDefaultDisplayId,
- hardware::graphics::composer::V2_1::
- IComposerCallback::Connection::CONNECTED);
- lock.lock();
- UpdateVsyncCallbackEnabledLocked();
-}
-
-void VrHwc::unregisterEventCallback() {
- std::lock_guard<std::mutex> guard(mutex_);
- event_callback_ = nullptr;
- UpdateVsyncCallbackEnabledLocked();
-}
-
-uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
-
-Error VrHwc::destroyVirtualDisplay(Display display) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (display == kDefaultDisplayId || displays_.erase(display) == 0)
- return Error::BAD_DISPLAY;
- ComposerView::Frame frame;
- frame.display_id = display;
- frame.removed = true;
- if (observer_)
- observer_->OnNewFrame(frame);
- return Error::NONE;
-}
-
-Error VrHwc::createLayer(Display display, Layer* outLayer) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* layer = display_ptr->CreateLayer();
- *outLayer = layer->info.id;
- return Error::NONE;
-}
-
-Error VrHwc::destroyLayer(Display display, Layer layer) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr) {
- return Error::BAD_DISPLAY;
- }
-
- return display_ptr->DestroyLayer(layer) ? Error::NONE : Error::BAD_LAYER;
-}
-
-Error VrHwc::getActiveConfig(Display display, Config* outConfig) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- *outConfig = kDefaultConfigId;
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayAttribute(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr) {
- return Error::BAD_DISPLAY;
- }
- if (config != kDefaultConfigId) {
- return Error::BAD_CONFIG;
- }
-
- switch (attribute) {
- case IComposerClient::Attribute::WIDTH:
- *outValue = display_ptr->width();
- break;
- case IComposerClient::Attribute::HEIGHT:
- *outValue = display_ptr->height();
- break;
- case IComposerClient::Attribute::VSYNC_PERIOD:
- {
- int error = 0;
- auto display_client = display::DisplayClient::Create(&error);
- if (!display_client) {
- ALOGE("Could not connect to display service : %s(%d)",
- strerror(error), error);
- // Return a default value of 30 fps
- *outValue = 1000 * 1000 * 1000 / 30;
- } else {
- auto metrics = display_client->GetDisplayMetrics();
- *outValue = metrics.get().vsync_period_ns;
- }
- }
- break;
- case IComposerClient::Attribute::DPI_X:
- case IComposerClient::Attribute::DPI_Y:
- {
- constexpr int32_t kDefaultDPI = 300;
- int32_t dpi = property_get_int32("ro.vr.hwc.dpi", kDefaultDPI);
- if (dpi <= 0) {
- dpi = kDefaultDPI;
- }
- *outValue = 1000 * dpi;
- }
- break;
- default:
- return Error::BAD_PARAMETER;
- }
-
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- std::vector<Config> configs(1, kDefaultConfigId);
- *outConfigs = hidl_vec<Config>(configs);
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
- *outName = hidl_string();
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayType(Display display,
- IComposerClient::DisplayType* outType) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr) {
- *outType = IComposerClient::DisplayType::INVALID;
- return Error::BAD_DISPLAY;
- }
-
- if (display == kDefaultDisplayId)
- *outType = IComposerClient::DisplayType::PHYSICAL;
- else
- *outType = IComposerClient::DisplayType::VIRTUAL;
-
- return Error::NONE;
-}
-
-Error VrHwc::getDozeSupport(Display display, bool* outSupport) {
- *outSupport = false;
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- return Error::NONE;
-}
-
-Error VrHwc::setActiveConfig(Display display, Config config) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
- if (config != kDefaultConfigId)
- return Error::BAD_CONFIG;
-
- display_ptr->set_active_config(config);
- return Error::NONE;
-}
-
-Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (enabled != IComposerClient::Vsync::ENABLE &&
- enabled != IComposerClient::Vsync::DISABLE) {
- return Error::BAD_PARAMETER;
- }
-
- Error set_vsync_result = Error::NONE;
- if (display == kDefaultDisplayId) {
- sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
- defaultServiceManager()->getService(
- String16(IVsyncService::GetServiceName())));
- if (vsync_service == nullptr) {
- ALOGE("Failed to get vsync service");
- return Error::NO_RESOURCES;
- }
-
- if (enabled == IComposerClient::Vsync::ENABLE) {
- ALOGI("Enable vsync");
- display_ptr->set_vsync_enabled(true);
- status_t result = vsync_service->registerCallback(vsync_callback_);
- if (result != OK) {
- ALOGE("%s service registerCallback() failed: %s (%d)",
- IVsyncService::GetServiceName(), strerror(-result), result);
- set_vsync_result = Error::NO_RESOURCES;
- }
- } else if (enabled == IComposerClient::Vsync::DISABLE) {
- ALOGI("Disable vsync");
- display_ptr->set_vsync_enabled(false);
- status_t result = vsync_service->unregisterCallback(vsync_callback_);
- if (result != OK) {
- ALOGE("%s service unregisterCallback() failed: %s (%d)",
- IVsyncService::GetServiceName(), strerror(-result), result);
- set_vsync_result = Error::NO_RESOURCES;
- }
- }
-
- UpdateVsyncCallbackEnabledLocked();
- }
-
- return set_vsync_result;
-}
-
-Error VrHwc::setColorTransform(Display display, const float* matrix,
- int32_t hint) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- display_ptr->SetColorTransform(matrix, hint);
- return Error::NONE;
-}
-
-Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
- int32_t acquireFence, int32_t /* dataspace */,
- const std::vector<hwc_rect_t>& /* damage */) {
- base::unique_fd fence(acquireFence);
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (target == nullptr)
- return Error::NONE;
-
- if (!display_ptr->SetClientTarget(target, std::move(fence)))
- return Error::BAD_PARAMETER;
-
- return Error::NONE;
-}
-
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
- int32_t releaseFence) {
- base::unique_fd fence(releaseFence);
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- // TODO(dnicoara): Is it necessary to do anything here?
- return Error::NONE;
-}
-
-Error VrHwc::validateDisplay(
- Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* /* outDisplayRequestMask */,
- std::vector<Layer>* /* outRequestedLayers */,
- std::vector<uint32_t>* /* outRequestMasks */) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- display_ptr->GetChangedCompositionTypes(outChangedLayers,
- outCompositionTypes);
- return Error::NONE;
-}
-
-Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
-
-Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
- std::vector<Layer>* outLayers,
- std::vector<int32_t>* outReleaseFences) {
- *outPresentFence = -1;
- outLayers->clear();
- outReleaseFences->clear();
-
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
-
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- ComposerView::Frame frame;
- std::vector<Layer> last_frame_layers;
- Error status = display_ptr->GetFrame(&frame.layers);
- frame.display_id = display;
- frame.display_width = display_ptr->width();
- frame.display_height = display_ptr->height();
- frame.active_config = display_ptr->active_config();
- frame.power_mode = display_ptr->power_mode();
- frame.vsync_enabled = display_ptr->vsync_enabled() ?
- IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
- frame.color_transform_hint = display_ptr->color_transform_hint();
- frame.color_mode = display_ptr->color_mode();
- memcpy(frame.color_transform, display_ptr->color_transform(),
- sizeof(frame.color_transform));
- if (status != Error::NONE)
- return status;
-
- last_frame_layers = display_ptr->UpdateLastFrameAndGetLastFrameLayers();
-
- base::unique_fd fence;
- if (observer_)
- fence = observer_->OnNewFrame(frame);
-
- if (fence.get() < 0)
- return Error::NONE;
-
- *outPresentFence = dup(fence.get());
- outLayers->swap(last_frame_layers);
- for (size_t i = 0; i < outLayers->size(); ++i)
- outReleaseFences->push_back(dup(fence.get()));
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerCursorPosition(Display display, Layer layer, int32_t x,
- int32_t y) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.cursor_x = x;
- hwc_layer->info.cursor_y = y;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerBuffer(Display display, Layer layer,
- buffer_handle_t buffer, int32_t acquireFence) {
- base::unique_fd fence(acquireFence);
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.buffer = CreateGraphicBuffer(
- buffer, hwc_layer->buffer_metadata);
- hwc_layer->info.fence = new Fence(fence.release());
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<hwc_rect_t>& damage) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.damaged_regions = damage;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerBlendMode(Display display, Layer layer, int32_t mode) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.blend_mode =
- static_cast<ComposerView::ComposerLayer::BlendMode>(mode);
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerColor(Display display, Layer layer,
- IComposerClient::Color color) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.color = color;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerCompositionType(Display display, Layer layer,
- int32_t type) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->composition_type = static_cast<HwcLayer::Composition>(type);
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerDataspace(Display display, Layer layer,
- int32_t dataspace) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.dataspace = dataspace;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerDisplayFrame(Display display, Layer layer,
- const hwc_rect_t& frame) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.display_frame =
- {frame.left, frame.top, frame.right, frame.bottom};
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.alpha = alpha;
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
- buffer_handle_t /* stream */) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (!FindDisplay(display))
- return Error::BAD_DISPLAY;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerSourceCrop(Display display, Layer layer,
- const hwc_frect_t& crop) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.crop = {crop.left, crop.top, crop.right, crop.bottom};
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerTransform(Display display, Layer layer,
- int32_t transform) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.transform = transform;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<hwc_rect_t>& visible) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.visible_regions = visible;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerZOrder(Display display, Layer layer, uint32_t z) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.z_order = z;
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerInfo(Display display, Layer layer, uint32_t type,
- uint32_t appId) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->info.type = type;
- hwc_layer->info.app_id = appId;
-
- return Error::NONE;
-}
-
-Error VrHwc::setClientTargetMetadata(
- Display display, const IVrComposerClient::BufferMetadata& metadata) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- display_ptr->SetClientTargetMetadata(metadata);
-
- return Error::NONE;
-}
-
-Error VrHwc::setLayerBufferMetadata(
- Display display, Layer layer,
- const IVrComposerClient::BufferMetadata& metadata) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- HwcLayer* hwc_layer = display_ptr->GetLayer(layer);
- if (!hwc_layer)
- return Error::BAD_LAYER;
-
- hwc_layer->buffer_metadata = metadata;
-
- return Error::NONE;
-}
-
-Return<void> VrHwc::getCapabilities(getCapabilities_cb hidl_cb) {
- hidl_cb(hidl_vec<Capability>());
- return Void();
-}
-
-Return<void> VrHwc::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
- std::string result;
-
- {
- std::lock_guard<std::mutex> guard(mutex_);
- result = "\nVrHwc states:\n";
- for (const auto& pair : displays_) {
- result += StringPrintf("Display id: %lu\n", (unsigned long)pair.first);
- pair.second->dumpDebugInfo(&result);
- }
- result += "\n";
- }
-
- hidl_cb(hidl_string(result));
- return Void();
-}
-
-Return<void> VrHwc::createClient(createClient_cb hidl_cb) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- Error status = Error::NONE;
- sp<VrComposerClient> client;
- if (!client_.promote().get()) {
- client = new VrComposerClient(*this);
- } else {
- ALOGE("Already have a client");
- status = Error::NO_RESOURCES;
- }
-
- client_ = client;
- hidl_cb(status, client);
- return Void();
-}
-
-Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- Error status = Error::NONE;
- sp<VrComposerClient> client;
- if (!client_.promote().get()) {
- client = new VrComposerClient(*this);
- } else {
- ALOGE("Already have a client");
- status = Error::NO_RESOURCES;
- }
-
- client_ = client;
- hidl_cb(status, client);
- return Void();
-}
-
-void VrHwc::ForceDisplaysRefresh() {
- std::lock_guard<std::mutex> guard(mutex_);
- if (event_callback_ != nullptr) {
- for (const auto& pair : displays_)
- event_callback_->onRefresh(pair.first);
- }
-}
-
-void VrHwc::RegisterObserver(Observer* observer) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (observer_)
- ALOGE("Overwriting observer");
- else
- observer_ = observer;
-}
-
-void VrHwc::UnregisterObserver(Observer* observer) {
- std::lock_guard<std::mutex> guard(mutex_);
- if (observer != observer_)
- ALOGE("Trying to unregister unknown observer");
- else
- observer_ = nullptr;
-}
-
-HwcDisplay* VrHwc::FindDisplay(Display display) {
- auto iter = displays_.find(display);
- return iter == displays_.end() ? nullptr : iter->second.get();
-}
-
-void VrHwc::UpdateVsyncCallbackEnabledLocked() {
- auto primary_display = FindDisplay(kDefaultDisplayId);
- LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
- "Should have created the primary display by now");
- bool send_vsync =
- event_callback_ != nullptr && primary_display->vsync_enabled();
- vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
-}
-
-Return<void> VrHwc::debug(const hidl_handle& fd,
- const hidl_vec<hidl_string>& args) {
- std::string result;
-
- {
- std::lock_guard<std::mutex> guard(mutex_);
- for (const auto& pair : displays_) {
- result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first));
- pair.second->dumpDebugInfo(&result);
- }
- result += "\n";
- }
-
- FILE* out = fdopen(dup(fd->data[0]), "w");
- fprintf(out, "%s", result.c_str());
- fclose(out);
-
- return Void();
-}
-
-void HwcLayer::dumpDebugInfo(std::string* result) const {
- if (!result) {
- return;
- }
- *result += StringPrintf("Layer: composition_type: %d, type: %d, app_id: %d, z_order: %d,\
- cursor_x: %d, cursor_y: %d, color(rgba): (%d,%d,%d,%d), dataspace: %d, transform: %d,\
- display_frame(LTRB): (%d,%d,%d,%d), crop(LTRB): (%.1f,%.1f,%.1f,%.1f), blend_mode: %d\n",
- composition_type, info.type, info.app_id, info.z_order, info.cursor_x, info.cursor_y,
- info.color.r, info.color.g, info.color.b, info.color.a, info.dataspace, info.transform,
- info.display_frame.left, info.display_frame.top, info.display_frame.right,
- info.display_frame.bottom, info.crop.left, info.crop.top, info.crop.right,
- info.crop.bottom, info.blend_mode);
- *result += StringPrintf("Layer buffer metadata: width: %d, height: %d, stride: %d, layerCount: %d\
- , pixelFormat: %d\n", buffer_metadata.width, buffer_metadata.height, buffer_metadata.stride,
- buffer_metadata.layerCount, buffer_metadata.format);
-}
-
-status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
- ATRACE_NAME("vr_hwc onVsync");
- std::lock_guard<std::mutex> guard(mutex_);
- if (callback_ != nullptr)
- callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
- return OK;
-}
-
-void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
- std::lock_guard<std::mutex> guard(mutex_);
- callback_ = callback;
-}
-
-// composer::V2_2::ComposerHal
-Error VrHwc::setReadbackBuffer(Display display,
- const native_handle_t* bufferHandle,
- android::base::unique_fd fenceFd) {
- return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferFence(Display display,
- android::base::unique_fd* outFenceFd) {
- return Error::NONE;
-}
-
-Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
- types::V1_1::PixelFormat* format,
- Display* outDisplay) {
- *format = types::V1_1::PixelFormat::RGBA_8888;
- *outDisplay = display_count_;
- displays_[display_count_].reset(new HwcDisplay(width, height));
- display_count_++;
- return Error::NONE;
-}
-
-Error VrHwc::setPowerMode_2_2(Display display,
- IComposerClient::PowerMode mode) {
- bool dozeSupported = false;
-
- Error dozeSupportError = getDozeSupport(display, &dozeSupported);
-
- if (dozeSupportError != Error::NONE)
- return dozeSupportError;
-
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (mode < IComposerClient::PowerMode::OFF ||
- mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
- return Error::BAD_PARAMETER;
- }
-
- if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE ||
- mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
- return Error::UNSUPPORTED;
- }
-
- display_ptr->set_power_mode(mode);
- return Error::NONE;
-}
-
-Error VrHwc::setLayerFloatColor(Display display, Layer layer,
- IComposerClient::FloatColor color) {
- return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode,
- std::vector<RenderIntent>* outIntents) {
- return Error::NONE;
-}
-
-std::array<float, 16> VrHwc::getDataspaceSaturationMatrix(
- types::V1_1::Dataspace dataspace) {
- return {};
-}
-
-// composer::V2_3::ComposerHal
-Error VrHwc::getHdrCapabilities_2_3(Display /*display*/,
- hidl_vec<Hdr>* /*outTypes*/,
- float* outMaxLuminance,
- float* outMaxAverageLuminance,
- float* outMinLuminance) {
- *outMaxLuminance = 0;
- *outMaxAverageLuminance = 0;
- *outMinLuminance = 0;
- return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadata_2_3(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadata>& metadata) {
- return Error::NONE;
-}
-
-Error VrHwc::getPerFrameMetadataKeys_2_3(
- Display display,
- std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
- return Error::NONE;
-}
-
-Error VrHwc::setColorMode_2_3(Display display, ColorMode mode,
- RenderIntent intent) {
- std::lock_guard<std::mutex> guard(mutex_);
- auto display_ptr = FindDisplay(display);
- if (!display_ptr)
- return Error::BAD_DISPLAY;
-
- if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
- return Error::BAD_PARAMETER;
-
- display_ptr->set_color_mode(mode);
- return Error::NONE;
-}
-
-Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode,
- std::vector<RenderIntent>* outIntents) {
- return Error::NONE;
-}
-
-Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) {
- return Error::NONE;
-}
-
-Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width,
- uint32_t height, PixelFormat format,
- Dataspace dataspace) {
- return Error::NONE;
-}
-
-Error VrHwc::getReadbackBufferAttributes_2_3(Display display,
- PixelFormat* outFormat,
- Dataspace* outDataspace) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) {
- int error = 0;
- auto display_client = display::DisplayClient::Create(&error);
- if (!display_client) {
- ALOGE("Could not connect to display service : %s(%d)", strerror(error),
- error);
- return Error::BAD_CONFIG;
- }
- auto edid_data = display_client->GetConfigurationData(
- display::ConfigFileType::kDeviceEdid);
- auto display_identification_port =
- display_client->GetDisplayIdentificationPort();
- *outPort = display_identification_port.get();
-
- std::copy(edid_data.get().begin(), edid_data.get().end(),
- std::back_inserter(*outData));
- return Error::NONE;
-}
-
-Error VrHwc::setLayerColorTransform(Display display, Layer layer,
- const float* matrix) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSamplingAttributes(
- Display display, PixelFormat& format, Dataspace& dataspace,
- hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
- return Error::NONE;
-}
-
-Error VrHwc::setDisplayedContentSamplingEnabled(
- Display display, IComposerClient::DisplayedContentSampling enable,
- hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
- uint64_t maxFrames) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames,
- uint64_t timestamp, uint64_t& frameCount,
- hidl_vec<uint64_t>& sampleComponent0,
- hidl_vec<uint64_t>& sampleComponent1,
- hidl_vec<uint64_t>& sampleComponent2,
- hidl_vec<uint64_t>& sampleComponent3) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayCapabilities(
- Display display,
- std::vector<IComposerClient::DisplayCapability>* outCapabilities) {
- return Error::NONE;
-}
-
-Error VrHwc::setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) {
- return Error::NONE;
-}
-
-Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) {
- return Error::NONE;
-}
-
-Error VrHwc::setDisplayBrightness(Display display, float brightness) {
- return Error::NONE;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
deleted file mode 100644
index 3e3a630..0000000
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
-
-#include <android-base/unique_fd.h>
-#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
-#include <android/hardware/graphics/composer/2.3/IComposer.h>
-#include <composer-hal/2.3/ComposerHal.h>
-#include <private/dvr/vsync_service.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/StrongPointer.h>
-
-#include <mutex>
-#include <unordered_map>
-
-using namespace android::frameworks::vr::composer::V2_0;
-using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_3;
-
-using android::hardware::hidl_bitfield;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::graphics::composer::V2_1::Config;
-using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
-using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_3::IComposerClient;
-
-namespace android {
-
-class Fence;
-
-namespace dvr {
-
-class VrComposerClient;
-
-using android::hardware::graphics::composer::V2_3::hal::ComposerHal;
-
-namespace types = android::hardware::graphics::common;
-
-using types::V1_1::RenderIntent;
-using types::V1_2::ColorMode;
-using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
-using types::V1_2::PixelFormat;
-
-class ComposerView {
- public:
- struct ComposerLayer {
- using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect;
- using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect;
- using BlendMode =
- hardware::graphics::composer::V2_3::IComposerClient::BlendMode;
-
- Layer id;
- sp<GraphicBuffer> buffer;
- sp<Fence> fence;
- Recti display_frame;
- Rectf crop;
- BlendMode blend_mode;
- float alpha;
- uint32_t type;
- uint32_t app_id;
- uint32_t z_order;
- int32_t cursor_x;
- int32_t cursor_y;
- IComposerClient::Color color;
- int32_t dataspace;
- int32_t transform;
- std::vector<hwc_rect_t> visible_regions;
- std::vector<hwc_rect_t> damaged_regions;
- };
-
- struct Frame {
- Display display_id;
- // This is set to true to notify the upper layer that the display is
- // being removed, or left false in the case of a normal frame. The upper
- // layer tracks display IDs and will handle new ones showing up.
- bool removed = false;
- int32_t display_width;
- int32_t display_height;
- Config active_config;
- ColorMode color_mode;
- IComposerClient::PowerMode power_mode;
- IComposerClient::Vsync vsync_enabled;
- float color_transform[16];
- int32_t color_transform_hint;
- std::vector<ComposerLayer> layers;
- };
-
- class Observer {
- public:
- virtual ~Observer() {}
-
- // Returns a list of layers that need to be shown together. Layers are
- // returned in z-order, with the lowest layer first.
- virtual base::unique_fd OnNewFrame(const Frame& frame) = 0;
- };
-
- virtual ~ComposerView() {}
-
- virtual void ForceDisplaysRefresh() = 0;
- virtual void RegisterObserver(Observer* observer) = 0;
- virtual void UnregisterObserver(Observer* observer) = 0;
-};
-
-struct HwcLayer {
- using Composition =
- hardware::graphics::composer::V2_3::IComposerClient::Composition;
-
- explicit HwcLayer(Layer new_id) { info.id = new_id; }
-
- void dumpDebugInfo(std::string* result) const;
-
- Composition composition_type;
- ComposerView::ComposerLayer info;
- IVrComposerClient::BufferMetadata buffer_metadata;
-};
-
-class HwcDisplay {
- public:
- HwcDisplay(int32_t width, int32_t height);
- ~HwcDisplay();
-
- int32_t width() const { return width_; }
- int32_t height() const { return height_; }
-
- HwcLayer* CreateLayer();
- bool DestroyLayer(Layer id);
- HwcLayer* GetLayer(Layer id);
-
- bool SetClientTarget(const native_handle_t* handle, base::unique_fd fence);
- void SetClientTargetMetadata(
- const IVrComposerClient::BufferMetadata& metadata);
-
- void GetChangedCompositionTypes(
- std::vector<Layer>* layer_ids,
- std::vector<IComposerClient::Composition>* composition);
-
- Error GetFrame(std::vector<ComposerView::ComposerLayer>* out_frame);
-
- std::vector<Layer> UpdateLastFrameAndGetLastFrameLayers();
-
- Config active_config() const { return active_config_; }
- void set_active_config(Config config) { active_config_ = config; }
-
- ColorMode color_mode() const { return color_mode_; }
- void set_color_mode(ColorMode mode) { color_mode_ = mode; }
-
- IComposerClient::PowerMode power_mode() const { return power_mode_; }
- void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
-
- bool vsync_enabled() const { return vsync_enabled_; }
- void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
-
- const float* color_transform() const { return color_transform_; }
- int32_t color_transform_hint() const { return color_transform_hint_; }
- void SetColorTransform(const float* matrix, int32_t hint);
-
- void dumpDebugInfo(std::string* result) const;
-
- private:
- // The client target buffer and the associated fence.
- sp<GraphicBuffer> buffer_;
- IVrComposerClient::BufferMetadata buffer_metadata_;
- sp<Fence> fence_;
-
- // List of currently active layers.
- std::vector<HwcLayer> layers_;
-
- std::vector<Layer> last_frame_layers_ids_;
-
- // Layer ID generator.
- uint64_t layer_ids_ = 1;
-
- int32_t width_;
- int32_t height_;
-
- Config active_config_;
- ColorMode color_mode_;
- IComposerClient::PowerMode power_mode_;
- bool vsync_enabled_ = false;
- float color_transform_[16];
- int32_t color_transform_hint_;
-
- HwcDisplay(const HwcDisplay&) = delete;
- void operator=(const HwcDisplay&) = delete;
-};
-
-class VrHwc : public IComposer, public ComposerHal, public ComposerView {
- public:
- VrHwc();
- ~VrHwc() override;
-
- Error setLayerInfo(Display display, Layer layer, uint32_t type,
- uint32_t appId);
- Error setClientTargetMetadata(
- Display display, const IVrComposerClient::BufferMetadata& metadata);
- Error setLayerBufferMetadata(
- Display display, Layer layer,
- const IVrComposerClient::BufferMetadata& metadata);
-
- // composer::V2_1::ComposerHal
- bool hasCapability(hwc2_capability_t capability) override;
-
- std::string dumpDebugInfo() override { return {}; }
-
- void registerEventCallback(ComposerHal::EventCallback* callback) override;
- void unregisterEventCallback() override;
-
- uint32_t getMaxVirtualDisplayCount() override;
- Error destroyVirtualDisplay(Display display) override;
-
- Error createLayer(Display display, Layer* outLayer) override;
- Error destroyLayer(Display display, Layer layer) override;
-
- Error getActiveConfig(Display display, Config* outConfig) override;
- Error getDisplayAttribute(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) override;
- Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
- Error getDisplayName(Display display, hidl_string* outName) override;
- Error getDisplayType(Display display,
- IComposerClient::DisplayType* outType) override;
- Error getDozeSupport(Display display, bool* outSupport) override;
-
- Error setActiveConfig(Display display, Config config) override;
- Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
- Error setColorTransform(Display display, const float* matrix,
- int32_t hint) override;
- Error setClientTarget(Display display, buffer_handle_t target,
- int32_t acquireFence, int32_t dataspace,
- const std::vector<hwc_rect_t>& damage) override;
- Error setOutputBuffer(Display display, buffer_handle_t buffer,
- int32_t releaseFence) override;
- Error validateDisplay(
- Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks) override;
- Error acceptDisplayChanges(Display display) override;
- Error presentDisplay(Display display, int32_t* outPresentFence,
- std::vector<Layer>* outLayers,
- std::vector<int32_t>* outReleaseFences) override;
-
- Error setLayerCursorPosition(Display display, Layer layer, int32_t x,
- int32_t y) override;
- Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
- int32_t acquireFence) override;
- Error setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<hwc_rect_t>& damage) override;
- Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
- Error setLayerColor(Display display, Layer layer,
- IComposerClient::Color color) override;
- Error setLayerCompositionType(Display display, Layer layer,
- int32_t type) override;
- Error setLayerDataspace(Display display, Layer layer,
- int32_t dataspace) override;
- Error setLayerDisplayFrame(Display display, Layer layer,
- const hwc_rect_t& frame) override;
- Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
- Error setLayerSidebandStream(Display display, Layer layer,
- buffer_handle_t stream) override;
- Error setLayerSourceCrop(Display display, Layer layer,
- const hwc_frect_t& crop) override;
- Error setLayerTransform(Display display, Layer layer,
- int32_t transform) override;
- Error setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<hwc_rect_t>& visible) override;
- Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
- // composer::V2_2::ComposerHal
- Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
- android::base::unique_fd fenceFd) override;
- Error getReadbackBufferFence(Display display,
- android::base::unique_fd* outFenceFd) override;
- Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
- types::V1_1::PixelFormat* format,
- Display* outDisplay) override;
- Error setPowerMode_2_2(Display display,
- IComposerClient::PowerMode mode) override;
- Error setLayerFloatColor(Display display, Layer layer,
- IComposerClient::FloatColor color) override;
- Error getRenderIntents(Display display, types::V1_1::ColorMode mode,
- std::vector<RenderIntent>* outIntents) override;
- std::array<float, 16> getDataspaceSaturationMatrix(
- types::V1_1::Dataspace dataspace) override;
-
- // composer::V2_3::ComposerHal
- Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes,
- float* outMaxLuminance,
- float* outMaxAverageLuminance,
- float* outMinLuminance) override;
- Error setLayerPerFrameMetadata_2_3(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadata>& metadata) override;
- Error getPerFrameMetadataKeys_2_3(
- Display display,
- std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
- Error setColorMode_2_3(Display display, ColorMode mode,
- RenderIntent intent) override;
- Error getRenderIntents_2_3(Display display, ColorMode mode,
- std::vector<RenderIntent>* outIntents) override;
- Error getColorModes_2_3(Display display,
- hidl_vec<ColorMode>* outModes) override;
- Error getClientTargetSupport_2_3(Display display, uint32_t width,
- uint32_t height, PixelFormat format,
- Dataspace dataspace) override;
- Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat,
- Dataspace* outDataspace) override;
- Error getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) override;
- Error setLayerColorTransform(Display display, Layer layer,
- const float* matrix) override;
- Error getDisplayedContentSamplingAttributes(
- Display display, PixelFormat& format, Dataspace& dataspace,
- hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask)
- override;
- Error setDisplayedContentSamplingEnabled(
- Display display, IComposerClient::DisplayedContentSampling enable,
- hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
- uint64_t maxFrames) override;
- Error getDisplayedContentSample(
- Display display, uint64_t maxFrames, uint64_t timestamp,
- uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
- hidl_vec<uint64_t>& sampleComponent1,
- hidl_vec<uint64_t>& sampleComponent2,
- hidl_vec<uint64_t>& sampleComponent3) override;
- Error getDisplayCapabilities(Display display,
- std::vector<IComposerClient::DisplayCapability>*
- outCapabilities) override;
- Error setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override;
- Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
- Error setDisplayBrightness(Display display, float brightness) override;
-
- // IComposer:
- Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
- Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
- Return<void> createClient(createClient_cb hidl_cb) override;
- Return<void> createClient_2_3(
- IComposer::createClient_2_3_cb hidl_cb) override;
-
- // ComposerView:
- void ForceDisplaysRefresh() override;
- void RegisterObserver(Observer* observer) override;
- void UnregisterObserver(Observer* observer) override;
-
- Return<void> debug(const hidl_handle& fd,
- const hidl_vec<hidl_string>& args) override;
-
- private:
- class VsyncCallback : public BnVsyncCallback {
- public:
- status_t onVsync(int64_t vsync_timestamp) override;
- void SetEventCallback(EventCallback* callback);
- private:
- std::mutex mutex_;
- EventCallback* callback_;
- };
-
- HwcDisplay* FindDisplay(Display display);
-
- // Re-evaluate whether or not we should start making onVsync() callbacks to
- // the client. We need enableCallback(true) to have been called, and
- // setVsyncEnabled() to have been called for the primary display. The caller
- // must have mutex_ locked already.
- void UpdateVsyncCallbackEnabledLocked();
-
- wp<VrComposerClient> client_;
-
- // Guard access to internal state from binder threads.
- std::mutex mutex_;
-
- std::unordered_map<Display, std::unique_ptr<HwcDisplay>> displays_;
- Display display_count_ = 2;
-
- EventCallback* event_callback_ = nullptr;
- Observer* observer_ = nullptr;
-
- sp<VsyncCallback> vsync_callback_;
-
- VrHwc(const VrHwc&) = delete;
- void operator=(const VrHwc&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
diff --git a/services/vr/hardware_composer/tests/vr_composer_test.cpp b/services/vr/hardware_composer/tests/vr_composer_test.cpp
deleted file mode 100644
index 2e70928..0000000
--- a/services/vr/hardware_composer/tests/vr_composer_test.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#include <android/dvr/BnVrComposerCallback.h>
-#include <binder/IServiceManager.h>
-#include <gtest/gtest.h>
-#include <sys/eventfd.h>
-#include <vr_composer.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-const char kVrDisplayName[] = "VrDisplay_Test";
-
-class TestComposerView : public ComposerView {
- public:
- TestComposerView() {}
- ~TestComposerView() override = default;
-
- size_t display_refresh_count() const { return display_refresh_count_; }
-
- void ForceDisplaysRefresh() override { display_refresh_count_++; }
- void RegisterObserver(Observer* observer) override {}
- void UnregisterObserver(Observer* observer) override {}
-
- TestComposerView(const TestComposerView&) = delete;
- void operator=(const TestComposerView&) = delete;
-
- private:
- size_t display_refresh_count_ = 0;
-};
-
-class TestComposerCallback : public BnVrComposerCallback {
- public:
- TestComposerCallback() {}
- ~TestComposerCallback() override = default;
-
- ComposerView::Frame last_frame() const { return last_frame_; }
-
- binder::Status onNewFrame(
- const ParcelableComposerFrame& frame,
- ParcelableUniqueFd* /* fence */) override {
- last_frame_ = frame.frame();
- return binder::Status::ok();
- }
-
- private:
- ComposerView::Frame last_frame_;
-
- TestComposerCallback(const TestComposerCallback&) = delete;
- void operator=(const TestComposerCallback&) = delete;
-};
-
-class TestComposerCallbackWithFence : public TestComposerCallback {
- public:
- ~TestComposerCallbackWithFence() override = default;
-
- binder::Status onNewFrame(
- const ParcelableComposerFrame& frame,
- ParcelableUniqueFd* fence) override {
- binder::Status status = TestComposerCallback::onNewFrame(frame, fence);
-
- base::unique_fd fd(eventfd(0, 0));
- EXPECT_LE(0, fd.get());
- fence->set_fence(fd);
-
- return status;
- }
-};
-
-sp<GraphicBuffer> CreateBuffer() {
- return new GraphicBuffer(600, 400, PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_HW_TEXTURE);
-}
-
-} // namespace
-
-class VrComposerTest : public testing::Test {
- public:
- VrComposerTest() : composer_(new VrComposer(&composer_view_)) {}
- ~VrComposerTest() override = default;
-
- sp<IVrComposer> GetComposerProxy() const {
- sp<IServiceManager> sm(defaultServiceManager());
- return interface_cast<IVrComposer>(sm->getService(String16(kVrDisplayName)));
- }
-
- void SetUp() override {
- sp<IServiceManager> sm(defaultServiceManager());
- EXPECT_EQ(OK,
- sm->addService(String16(kVrDisplayName), composer_, false));
- }
-
- protected:
- TestComposerView composer_view_;
- sp<VrComposer> composer_;
-
- VrComposerTest(const VrComposerTest&) = delete;
- void operator=(const VrComposerTest&) = delete;
-};
-
-TEST_F(VrComposerTest, TestWithoutObserver) {
- sp<IVrComposer> composer = GetComposerProxy();
- ComposerView::Frame frame;
-
- base::unique_fd fence = composer_->OnNewFrame(frame);
- ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithObserver) {
- sp<IVrComposer> composer = GetComposerProxy();
- sp<TestComposerCallback> callback = new TestComposerCallback();
- ASSERT_EQ(0, composer_view_.display_refresh_count());
- ASSERT_TRUE(composer->registerObserver(callback).isOk());
- ASSERT_EQ(1, composer_view_.display_refresh_count());
-
- ComposerView::Frame frame;
- base::unique_fd fence = composer_->OnNewFrame(frame);
- ASSERT_EQ(-1, fence.get());
-}
-
-TEST_F(VrComposerTest, TestWithOneLayer) {
- sp<IVrComposer> composer = GetComposerProxy();
- sp<TestComposerCallback> callback = new TestComposerCallbackWithFence();
- ASSERT_TRUE(composer->registerObserver(callback).isOk());
-
- ComposerView::Frame frame;
- frame.display_id = 1;
- frame.removed = false;
- frame.display_width = 600;
- frame.display_height = 400;
- frame.layers.push_back(ComposerView::ComposerLayer{
- .id = 1,
- .buffer = CreateBuffer(),
- .fence = new Fence(eventfd(0, 0)),
- .display_frame = {0, 0, 600, 400},
- .crop = {0.0f, 0.0f, 600.0f, 400.0f},
- .blend_mode = IComposerClient::BlendMode::NONE,
- .alpha = 1.0f,
- .type = 1,
- .app_id = 1,
- });
- base::unique_fd fence = composer_->OnNewFrame(frame);
- ASSERT_LE(0, fence.get());
-
- ComposerView::Frame received_frame = callback->last_frame();
- ASSERT_EQ(frame.display_id, received_frame.display_id);
- ASSERT_EQ(frame.display_width, received_frame.display_width);
- ASSERT_EQ(frame.display_height, received_frame.display_height);
- ASSERT_EQ(frame.removed, received_frame.removed);
- ASSERT_EQ(1u, received_frame.layers.size());
- ASSERT_EQ(frame.layers[0].id, received_frame.layers[0].id);
- ASSERT_NE(nullptr, received_frame.layers[0].buffer.get());
- ASSERT_TRUE(received_frame.layers[0].fence->isValid());
- ASSERT_EQ(frame.layers[0].display_frame.left,
- received_frame.layers[0].display_frame.left);
- ASSERT_EQ(frame.layers[0].display_frame.top,
- received_frame.layers[0].display_frame.top);
- ASSERT_EQ(frame.layers[0].display_frame.right,
- received_frame.layers[0].display_frame.right);
- ASSERT_EQ(frame.layers[0].display_frame.bottom,
- received_frame.layers[0].display_frame.bottom);
- ASSERT_EQ(frame.layers[0].crop.left, received_frame.layers[0].crop.left);
- ASSERT_EQ(frame.layers[0].crop.top, received_frame.layers[0].crop.top);
- ASSERT_EQ(frame.layers[0].crop.right, received_frame.layers[0].crop.right);
- ASSERT_EQ(frame.layers[0].crop.bottom, received_frame.layers[0].crop.bottom);
- ASSERT_EQ(frame.layers[0].blend_mode, received_frame.layers[0].blend_mode);
- ASSERT_EQ(frame.layers[0].alpha, received_frame.layers[0].alpha);
- ASSERT_EQ(frame.layers[0].type, received_frame.layers[0].type);
- ASSERT_EQ(frame.layers[0].app_id, received_frame.layers[0].app_id);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.cpp b/services/vr/hardware_composer/vr_composer.cpp
deleted file mode 100644
index d93f370..0000000
--- a/services/vr/hardware_composer/vr_composer.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "vr_composer.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionCache.h>
-
-namespace android {
-namespace dvr {
-namespace {
-
-bool CheckPermission() {
- const android::IPCThreadState* ipc = android::IPCThreadState::self();
- const pid_t pid = ipc->getCallingPid();
- const uid_t uid = ipc->getCallingUid();
- const bool permission = PermissionCache::checkPermission(
- String16("android.permission.RESTRICTED_VR_ACCESS"), pid, uid);
- if (!permission)
- ALOGE("permission denied to pid=%d uid=%u", pid, uid);
-
- return permission;
-}
-
-} // namespace
-
-VrComposer::VrComposer(ComposerView* composer_view)
- : composer_view_(composer_view) {
- composer_view_->RegisterObserver(this);
-}
-
-VrComposer::~VrComposer() {
- composer_view_->UnregisterObserver(this);
-}
-
-binder::Status VrComposer::registerObserver(
- const sp<IVrComposerCallback>& callback) {
- {
- std::lock_guard<std::mutex> guard(mutex_);
-
- if (!CheckPermission())
- return binder::Status::fromStatusT(PERMISSION_DENIED);
-
- if (callback_.get()) {
- ALOGE("Failed to register callback, already registered");
- return binder::Status::fromStatusT(ALREADY_EXISTS);
- }
-
- callback_ = callback;
- IInterface::asBinder(callback_)->linkToDeath(this);
- }
-
- // Don't take the lock to force display refresh otherwise it could end in a
- // deadlock since HWC calls this with new frames and it has a lock of its own
- // to serialize access to the display information.
- composer_view_->ForceDisplaysRefresh();
- return binder::Status::ok();
-}
-
-binder::Status VrComposer::clearObserver() {
- std::lock_guard<std::mutex> guard(mutex_);
- callback_ = nullptr;
- return binder::Status::ok();
-}
-
-base::unique_fd VrComposer::OnNewFrame(const ComposerView::Frame& frame) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- if (!callback_.get())
- return base::unique_fd();
-
- ParcelableComposerFrame parcelable_frame(frame);
- ParcelableUniqueFd fence;
- binder::Status ret = callback_->onNewFrame(parcelable_frame, &fence);
- if (!ret.isOk())
- ALOGE("Failed to send new frame: %s", ret.toString8().string());
-
- return fence.fence();
-}
-
-void VrComposer::binderDied(const wp<IBinder>& /* who */) {
- std::lock_guard<std::mutex> guard(mutex_);
-
- callback_ = nullptr;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/services/vr/hardware_composer/vr_composer.h b/services/vr/hardware_composer/vr_composer.h
deleted file mode 100644
index 1273352..0000000
--- a/services/vr/hardware_composer/vr_composer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-#define ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
-
-#include <android/dvr/BnVrComposer.h>
-#include <impl/vr_hwc.h>
-
-namespace android {
-namespace dvr {
-
-class VrComposerCallback;
-
-// Implementation of the IVrComposer service used to notify VR Window Manager
-// when SurfaceFlinger presents 2D UI changes.
-//
-// VR HWC updates the presented frame via the ComposerView::Observer interface.
-// On notification |callback_| is called to update VR Window Manager.
-// NOTE: If VR Window Manager isn't connected, the notification is a no-op.
-class VrComposer
- : public BnVrComposer,
- public ComposerView::Observer,
- public IBinder::DeathRecipient {
- public:
- explicit VrComposer(ComposerView* composer_view);
- ~VrComposer() override;
-
- // BnVrComposer:
- binder::Status registerObserver(
- const sp<IVrComposerCallback>& callback) override;
-
- binder::Status clearObserver() override;
-
- // ComposerView::Observer:
- base::unique_fd OnNewFrame(const ComposerView::Frame& frame) override;
-
- private:
- // IBinder::DeathRecipient:
- void binderDied(const wp<IBinder>& who) override;
-
- std::mutex mutex_;
-
- sp<IVrComposerCallback> callback_;
-
- ComposerView* composer_view_; // Not owned.
-
- VrComposer(const VrComposer&) = delete;
- void operator=(const VrComposer&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_HARDWARE_COMPOSER_VR_COMPOSER_H
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
"vkGetPhysicalDeviceMemoryProperties2",
"vkGetPhysicalDeviceMemoryProperties2KHR",
"vkGetPhysicalDeviceMultisamplePropertiesEXT",
+ "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
"vkGetPhysicalDevicePresentRectanglesKHR",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 4927150..9ed992b 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,8 +1027,11 @@
}
}
-bool GetAndroidNativeBufferSpecVersion9Support(
- VkPhysicalDevice physicalDevice) {
+VkResult GetAndroidNativeBufferSpecVersion9Support(
+ VkPhysicalDevice physicalDevice,
+ bool& support) {
+ support = false;
+
const InstanceData& data = GetData(physicalDevice);
// Call to get propertyCount
@@ -1038,6 +1041,10 @@
physicalDevice, nullptr, &propertyCount, nullptr);
ATRACE_END();
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
// Call to enumerate properties
std::vector<VkExtensionProperties> properties(propertyCount);
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
@@ -1045,6 +1052,10 @@
physicalDevice, nullptr, &propertyCount, properties.data());
ATRACE_END();
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
for (uint32_t i = 0; i < propertyCount; i++) {
auto& prop = properties[i];
@@ -1053,11 +1064,12 @@
continue;
if (prop.specVersion >= 9) {
- return true;
+ support = true;
+ return result;
}
}
- return false;
+ return result;
}
VkResult EnumerateDeviceExtensionProperties(
@@ -1101,18 +1113,30 @@
swapchainCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
swapchainCompFeats.pNext = nullptr;
+ swapchainCompFeats.imageCompressionControlSwapchain = false;
VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
imageCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
imageCompFeats.pNext = &swapchainCompFeats;
+ imageCompFeats.imageCompressionControl = false;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
feats2.pNext = &imageCompFeats;
- GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ const auto& driver = GetData(physicalDevice).driver;
+ if (driver.GetPhysicalDeviceFeatures2 ||
+ driver.GetPhysicalDeviceFeatures2KHR) {
+ GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ }
- bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+ bool anb9 = false;
+ VkResult result =
+ GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9);
+
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
if (anb9 && imageCompFeats.imageCompressionControl) {
loader_extensions.push_back(
@@ -1142,7 +1166,7 @@
}
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
- VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
ATRACE_END();
@@ -1242,6 +1266,27 @@
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
+ // TODO(b/259516419) avoid getting stats from hwui
+ // const bool reportStats = (pCreateInfo->pApplicationInfo == nullptr )
+ // || (strcmp("android framework",
+ // pCreateInfo->pApplicationInfo->pEngineName) != 0);
+ const bool reportStats = true;
+ if (reportStats) {
+ // Set stats for Vulkan api version requested with application info
+ if (pCreateInfo->pApplicationInfo) {
+ const uint32_t vulkanApiVersion =
+ pCreateInfo->pApplicationInfo->apiVersion;
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ vulkanApiVersion);
+ }
+
+ // Update stats for the extensions requested
+ android::GraphicsEnv::getInstance().setVulkanInstanceExtensions(
+ pCreateInfo->enabledExtensionCount,
+ pCreateInfo->ppEnabledExtensionNames);
+ }
+
*pInstance = instance;
return VK_SUCCESS;
@@ -1347,6 +1392,65 @@
*pDevice = dev;
+ // TODO(b/259516419) avoid getting stats from hwui
+ const bool reportStats = true;
+ if (reportStats) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE);
+
+ // Set stats for creating a Vulkan device and report features in use
+ const VkPhysicalDeviceFeatures* pEnabledFeatures =
+ pCreateInfo->pEnabledFeatures;
+ if (!pEnabledFeatures) {
+ // Use features from the chained VkPhysicalDeviceFeatures2
+ // structure, if given
+ const VkPhysicalDeviceFeatures2* features2 =
+ reinterpret_cast<const VkPhysicalDeviceFeatures2*>(
+ pCreateInfo->pNext);
+ while (features2 &&
+ features2->sType !=
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) {
+ features2 = reinterpret_cast<const VkPhysicalDeviceFeatures2*>(
+ features2->pNext);
+ }
+ if (features2) {
+ pEnabledFeatures = &features2->features;
+ }
+ }
+ const VkBool32* pFeatures =
+ reinterpret_cast<const VkBool32*>(pEnabledFeatures);
+ if (pFeatures) {
+ // VkPhysicalDeviceFeatures consists of VkBool32 values, go over all
+ // of them using pointer arithmetic here and save the features in a
+ // 64-bit bitfield
+ static_assert(
+ (sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32)) <= 64,
+ "VkPhysicalDeviceFeatures has too many elements for bitfield "
+ "packing");
+ static_assert(
+ (sizeof(VkPhysicalDeviceFeatures) % sizeof(VkBool32)) == 0,
+ "VkPhysicalDeviceFeatures has invalid size for bitfield "
+ "packing");
+ const int numFeatures =
+ sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
+
+ uint64_t enableFeatureBits = 0;
+ for (int i = 0; i < numFeatures; i++) {
+ if (pFeatures[i] != VK_FALSE) {
+ enableFeatureBits |= (uint64_t(1) << i);
+ }
+ }
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ enableFeatureBits);
+ }
+
+ // Update stats for the extensions requested
+ android::GraphicsEnv::getInstance().setVulkanDeviceExtensions(
+ pCreateInfo->enabledExtensionCount,
+ pCreateInfo->ppEnabledExtensionNames);
+ }
+
return VK_SUCCESS;
}
@@ -1532,6 +1636,11 @@
} break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+ pFeats);
+ compressionFeat->imageCompressionControlSwapchain = false;
imageCompressionControlSwapchainInChain = true;
} break;
@@ -1551,6 +1660,7 @@
imageCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
imageCompFeats.pNext = nullptr;
+ imageCompFeats.imageCompressionControl = false;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 4f7b923..b03200e 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -243,6 +243,11 @@
// syncronous requests to Surface Flinger):
enum { MIN_NUM_FRAMES_AGO = 5 };
+bool IsSharedPresentMode(VkPresentModeKHR mode) {
+ return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+ mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
+}
+
struct Swapchain {
Swapchain(Surface& surface_,
uint32_t num_images_,
@@ -254,9 +259,7 @@
pre_transform(pre_transform_),
frame_timestamps_enabled(false),
acquire_next_image_timeout(-1),
- shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
- present_mode ==
- VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+ shared(IsSharedPresentMode(present_mode)) {
ANativeWindow* window = surface.window.get();
native_window_get_refresh_cycle_duration(
window,
@@ -508,6 +511,10 @@
case VK_FORMAT_R8_UNORM:
native_format = android::PIXEL_FORMAT_R_8;
break;
+ // TODO: Do we need to query for VK_EXT_rgba10x6_formats here?
+ case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+ native_format = android::PIXEL_FORMAT_RGBA_10101010;
+ break;
default:
ALOGV("unsupported swapchain format %d", format);
break;
@@ -715,6 +722,17 @@
capabilities->minImageExtent = VkExtent2D{1, 1};
capabilities->maxImageExtent = VkExtent2D{4096, 4096};
+ if (capabilities->maxImageExtent.height <
+ capabilities->currentExtent.height) {
+ capabilities->maxImageExtent.height =
+ capabilities->currentExtent.height;
+ }
+
+ if (capabilities->maxImageExtent.width <
+ capabilities->currentExtent.width) {
+ capabilities->maxImageExtent.width = capabilities->currentExtent.width;
+ }
+
capabilities->maxImageArrayLayers = 1;
capabilities->supportedTransforms = kSupportedTransforms;
@@ -743,7 +761,6 @@
const InstanceData& instance_data = GetData(pdev);
- bool wide_color_support = false;
uint64_t consumer_usage = 0;
bool colorspace_ext =
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
@@ -754,27 +771,15 @@
if (!surfaceless_enabled) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- // Support for VK_GOOGLE_surfaceless_query. The EGL loader
- // unconditionally supports wide color formats, even if they will cause
- // a SurfaceFlinger fallback. Based on that, wide_color_support will be
- // set to true in this case.
- wide_color_support = true;
+ // Support for VK_GOOGLE_surfaceless_query.
// TODO(b/203826952): research proper value; temporarily use the
// values seen on Pixel
consumer_usage = AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
} else {
Surface& surface = *SurfaceFromHandle(surface_handle);
- int err = native_window_get_wide_color_support(surface.window.get(),
- &wide_color_support);
- if (err) {
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- ALOGV("wide_color_support is: %d", wide_color_support);
-
consumer_usage = surface.consumer_usage;
}
- wide_color_support = wide_color_support && colorspace_ext;
AHardwareBuffer_Desc desc = {};
desc.width = 1;
@@ -796,9 +801,6 @@
VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT});
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
- }
-
- if (wide_color_support) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -828,8 +830,6 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_PASS_THROUGH_EXT});
- }
- if (wide_color_support) {
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
@@ -848,8 +848,6 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_PASS_THROUGH_EXT});
- }
- if (wide_color_support) {
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
@@ -864,6 +862,22 @@
}
}
+ // TODO query VK_EXT_rgba10x6_formats support
+ desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ if (colorspace_ext) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ }
+ }
+
// NOTE: Any new formats that are added must be coordinated across different
// Android users. This includes the ANGLE team (a layered implementation of
// OpenGL-ES).
@@ -939,27 +953,36 @@
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
pSurfaceFormatCount, nullptr);
- } else {
- // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
- // after the call.
- std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
- VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
- physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
- surface_formats.data());
+ }
- if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
- const auto& driver = GetData(physicalDevice).driver;
+ // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+ // after the call.
+ std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
+ VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+ physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+ surface_formats.data());
- // marshal results individually due to stride difference.
- uint32_t formats_to_marshal = *pSurfaceFormatCount;
- for (uint32_t i = 0u; i < formats_to_marshal; i++) {
- pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
- // Query the compression properties for the surface format
- if (pSurfaceFormats[i].pNext) {
+ const auto& driver = GetData(physicalDevice).driver;
+
+ // marshal results individually due to stride difference.
+ uint32_t formats_to_marshal = *pSurfaceFormatCount;
+ for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+ pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+ // Query the compression properties for the surface format
+ VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i];
+ while (pSurfaceFormat->pNext) {
+ pSurfaceFormat =
+ reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext);
+ switch (pSurfaceFormat->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: {
VkImageCompressionPropertiesEXT* surfaceCompressionProps =
reinterpret_cast<VkImageCompressionPropertiesEXT*>(
- pSurfaceFormats[i].pNext);
+ pSurfaceFormat);
if (surfaceCompressionProps &&
driver.GetPhysicalDeviceImageFormatProperties2KHR) {
@@ -1001,12 +1024,16 @@
return compressionRes;
}
}
- }
+ } break;
+
+ default:
+ // Ignore all other extension structs
+ break;
}
}
-
- return result;
}
+
+ return result;
}
VKAPI_ATTR
@@ -1623,6 +1650,10 @@
android::GpuStatsInfo::Stats::FALSE_PREROTATION);
}
+ // Set stats for creating a Vulkan swapchain
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN);
+
surface.swapchain_handle = HandleFromSwapchain(swapchain);
*swapchain_handle = surface.swapchain_handle;
return VK_SUCCESS;
@@ -1792,6 +1823,173 @@
return a != VK_SUCCESS ? a : b;
}
+// KHR_incremental_present aspect of QueuePresentKHR
+static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) {
+ std::vector<android_native_rect_t> rects(pRegion->rectangleCount);
+ for (auto i = 0u; i < pRegion->rectangleCount; i++) {
+ auto const& rect = pRegion->pRectangles[i];
+ if (rect.layer > 0) {
+ ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead",
+ rect.layer);
+ }
+
+ rects[i].left = rect.offset.x;
+ rects[i].bottom = rect.offset.y;
+ rects[i].right = rect.offset.x + rect.extent.width;
+ rects[i].top = rect.offset.y + rect.extent.height;
+ }
+ native_window_set_surface_damage(window, rects.data(), rects.size());
+}
+
+// GOOGLE_display_timing aspect of QueuePresentKHR
+static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) {
+ ANativeWindow *window = swapchain.surface.window.get();
+
+ // We don't know whether the app will actually use GOOGLE_display_timing
+ // with a particular swapchain until QueuePresent; enable it on the BQ
+ // now if needed
+ if (!swapchain.frame_timestamps_enabled) {
+ ALOGV("Calling native_window_enable_frame_timestamps(true)");
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+
+ // Record the nativeFrameId so it can be later correlated to
+ // this present.
+ uint64_t nativeFrameId = 0;
+ int err = native_window_get_next_frame_id(
+ window, &nativeFrameId);
+ if (err != android::OK) {
+ ALOGE("Failed to get next native frame ID.");
+ }
+
+ // Add a new timing record with the user's presentID and
+ // the nativeFrameId.
+ swapchain.timing.emplace_back(pTime, nativeFrameId);
+ if (swapchain.timing.size() > MAX_TIMING_INFOS) {
+ swapchain.timing.erase(
+ swapchain.timing.begin(),
+ swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS);
+ }
+ if (pTime->desiredPresentTime) {
+ ALOGV(
+ "Calling native_window_set_buffers_timestamp(%" PRId64 ")",
+ pTime->desiredPresentTime);
+ native_window_set_buffers_timestamp(
+ window,
+ static_cast<int64_t>(pTime->desiredPresentTime));
+ }
+}
+
+static VkResult PresentOneSwapchain(
+ VkQueue queue,
+ Swapchain& swapchain,
+ uint32_t imageIndex,
+ const VkPresentRegionKHR *pRegion,
+ const VkPresentTimeGOOGLE *pTime,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore *pWaitSemaphores) {
+
+ VkDevice device = GetData(queue).driver_device;
+ const auto& dispatch = GetData(queue).driver;
+
+ Swapchain::Image& img = swapchain.images[imageIndex];
+ VkResult swapchain_result = VK_SUCCESS;
+ VkResult result;
+ int err;
+
+ // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the
+ // wait semaphores, so this doesn't actually work for the multiple swapchain
+ // case.
+ int fence = -1;
+ result = dispatch.QueueSignalReleaseImageANDROID(
+ queue, waitSemaphoreCount,
+ pWaitSemaphores, img.image, &fence);
+ if (result != VK_SUCCESS) {
+ ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
+ swapchain_result = result;
+ }
+ if (img.release_fence >= 0)
+ close(img.release_fence);
+ img.release_fence = fence < 0 ? -1 : dup(fence);
+
+ if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) {
+ ANativeWindow* window = swapchain.surface.window.get();
+ if (swapchain_result == VK_SUCCESS) {
+
+ if (pRegion) {
+ SetSwapchainSurfaceDamage(window, pRegion);
+ }
+ if (pTime) {
+ SetSwapchainFrameTimestamp(swapchain, pTime);
+ }
+
+ err = window->queueBuffer(window, img.buffer.get(), fence);
+ // queueBuffer always closes fence, even on error
+ if (err != android::OK) {
+ ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ if (img.dequeue_fence >= 0) {
+ close(img.dequeue_fence);
+ img.dequeue_fence = -1;
+ }
+ img.dequeued = false;
+ }
+
+ // If the swapchain is in shared mode, immediately dequeue the
+ // buffer so it can be presented again without an intervening
+ // call to AcquireNextImageKHR. We expect to get the same buffer
+ // back from every call to dequeueBuffer in this mode.
+ if (swapchain.shared && swapchain_result == VK_SUCCESS) {
+ ANativeWindowBuffer* buffer;
+ int fence_fd;
+ err = window->dequeueBuffer(window, &buffer, &fence_fd);
+ if (err != android::OK) {
+ ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else if (img.buffer != buffer) {
+ ALOGE("got wrong image back for shared swapchain");
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ img.dequeue_fence = fence_fd;
+ img.dequeued = true;
+ }
+ }
+ }
+ if (swapchain_result != VK_SUCCESS) {
+ OrphanSwapchain(device, &swapchain);
+ }
+ // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
+ // and only when the window's transform/rotation changes. Extent
+ // changes will not cause VK_SUBOPTIMAL_KHR because of the
+ // application issues that were caused when the following transform
+ // change was added.
+ int window_transform_hint;
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+ &window_transform_hint);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+ strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ }
+ if (swapchain.pre_transform != window_transform_hint) {
+ swapchain_result =
+ WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
+ }
+ } else {
+ ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
+ img, true);
+ swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ return swapchain_result;
+}
+
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
ATRACE_CALL();
@@ -1800,8 +1998,6 @@
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
- VkDevice device = GetData(queue).driver_device;
- const auto& dispatch = GetData(queue).driver;
VkResult final_result = VK_SUCCESS;
// Look at the pNext chain for supported extension structs:
@@ -1837,175 +2033,19 @@
(present_regions) ? present_regions->pRegions : nullptr;
const VkPresentTimeGOOGLE* times =
(present_times) ? present_times->pTimes : nullptr;
- const VkAllocationCallbacks* allocator = &GetData(device).allocator;
- android_native_rect_t* rects = nullptr;
- uint32_t nrects = 0;
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
Swapchain& swapchain =
*SwapchainFromHandle(present_info->pSwapchains[sc]);
- uint32_t image_idx = present_info->pImageIndices[sc];
- Swapchain::Image& img = swapchain.images[image_idx];
- const VkPresentRegionKHR* region =
- (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr;
- const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr;
- VkResult swapchain_result = VK_SUCCESS;
- VkResult result;
- int err;
- int fence = -1;
- result = dispatch.QueueSignalReleaseImageANDROID(
- queue, present_info->waitSemaphoreCount,
- present_info->pWaitSemaphores, img.image, &fence);
- if (result != VK_SUCCESS) {
- ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
- swapchain_result = result;
- }
- if (img.release_fence >= 0)
- close(img.release_fence);
- img.release_fence = fence < 0 ? -1 : dup(fence);
-
- if (swapchain.surface.swapchain_handle ==
- present_info->pSwapchains[sc]) {
- ANativeWindow* window = swapchain.surface.window.get();
- if (swapchain_result == VK_SUCCESS) {
- if (region) {
- // Process the incremental-present hint for this swapchain:
- uint32_t rcount = region->rectangleCount;
- if (rcount > nrects) {
- android_native_rect_t* new_rects =
- static_cast<android_native_rect_t*>(
- allocator->pfnReallocation(
- allocator->pUserData, rects,
- sizeof(android_native_rect_t) * rcount,
- alignof(android_native_rect_t),
- VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
- if (new_rects) {
- rects = new_rects;
- nrects = rcount;
- } else {
- rcount = 0; // Ignore the hint for this swapchain
- }
- }
- for (uint32_t r = 0; r < rcount; ++r) {
- if (region->pRectangles[r].layer > 0) {
- ALOGV(
- "vkQueuePresentKHR ignoring invalid layer "
- "(%u); using layer 0 instead",
- region->pRectangles[r].layer);
- }
- int x = region->pRectangles[r].offset.x;
- int y = region->pRectangles[r].offset.y;
- int width = static_cast<int>(
- region->pRectangles[r].extent.width);
- int height = static_cast<int>(
- region->pRectangles[r].extent.height);
- android_native_rect_t* cur_rect = &rects[r];
- cur_rect->left = x;
- cur_rect->top = y + height;
- cur_rect->right = x + width;
- cur_rect->bottom = y;
- }
- native_window_set_surface_damage(window, rects, rcount);
- }
- if (time) {
- if (!swapchain.frame_timestamps_enabled) {
- ALOGV(
- "Calling "
- "native_window_enable_frame_timestamps(true)");
- native_window_enable_frame_timestamps(window, true);
- swapchain.frame_timestamps_enabled = true;
- }
-
- // Record the nativeFrameId so it can be later correlated to
- // this present.
- uint64_t nativeFrameId = 0;
- err = native_window_get_next_frame_id(
- window, &nativeFrameId);
- if (err != android::OK) {
- ALOGE("Failed to get next native frame ID.");
- }
-
- // Add a new timing record with the user's presentID and
- // the nativeFrameId.
- swapchain.timing.emplace_back(time, nativeFrameId);
- while (swapchain.timing.size() > MAX_TIMING_INFOS) {
- swapchain.timing.erase(swapchain.timing.begin());
- }
- if (time->desiredPresentTime) {
- // Set the desiredPresentTime:
- ALOGV(
- "Calling "
- "native_window_set_buffers_timestamp(%" PRId64 ")",
- time->desiredPresentTime);
- native_window_set_buffers_timestamp(
- window,
- static_cast<int64_t>(time->desiredPresentTime));
- }
- }
-
- err = window->queueBuffer(window, img.buffer.get(), fence);
- // queueBuffer always closes fence, even on error
- if (err != android::OK) {
- ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- } else {
- if (img.dequeue_fence >= 0) {
- close(img.dequeue_fence);
- img.dequeue_fence = -1;
- }
- img.dequeued = false;
- }
-
- // If the swapchain is in shared mode, immediately dequeue the
- // buffer so it can be presented again without an intervening
- // call to AcquireNextImageKHR. We expect to get the same buffer
- // back from every call to dequeueBuffer in this mode.
- if (swapchain.shared && swapchain_result == VK_SUCCESS) {
- ANativeWindowBuffer* buffer;
- int fence_fd;
- err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != android::OK) {
- ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else if (img.buffer != buffer) {
- ALOGE("got wrong image back for shared swapchain");
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else {
- img.dequeue_fence = fence_fd;
- img.dequeued = true;
- }
- }
- }
- if (swapchain_result != VK_SUCCESS) {
- OrphanSwapchain(device, &swapchain);
- }
- // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
- // and only when the window's transform/rotation changes. Extent
- // changes will not cause VK_SUBOPTIMAL_KHR because of the
- // application issues that were caused when the following transform
- // change was added.
- int window_transform_hint;
- err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
- &window_transform_hint);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
- strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- }
- if (swapchain.pre_transform != window_transform_hint) {
- swapchain_result =
- WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
- }
- } else {
- ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
- img, true);
- swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
- }
+ VkResult swapchain_result = PresentOneSwapchain(
+ queue,
+ swapchain,
+ present_info->pImageIndices[sc],
+ (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr,
+ times ? ×[sc] : nullptr,
+ present_info->waitSemaphoreCount,
+ present_info->pWaitSemaphores);
if (present_info->pResults)
present_info->pResults[sc] = swapchain_result;
@@ -2013,9 +2053,6 @@
if (swapchain_result != final_result)
final_result = WorstPresentResult(final_result, swapchain_result);
}
- if (rects) {
- allocator->pfnFree(allocator->pUserData, rects);
- }
return final_result;
}
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index da6b00a..0284192 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -731,7 +731,7 @@
visitor->Visit("vulkanMemoryModelAvailabilityVisibilityChains", &features->vulkanMemoryModelAvailabilityVisibilityChains) &&
visitor->Visit("shaderOutputViewportIndex", &features->shaderOutputViewportIndex) &&
visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer) &&
- visitor->Visit("shaderOutputLayer", &features->shaderOutputLayer);
+ visitor->Visit("subgroupBroadcastDynamicId", &features->subgroupBroadcastDynamicId);
}
template <typename Visitor>