Merge "Add InputDispatcher unit tests for untrusted touches [6/n]" into sc-dev
diff --git a/Android.bp b/Android.bp
index 298c01a..dec6716 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,4 +85,10 @@
cc_library_headers{
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
-}
\ No newline at end of file
+}
+
+filegroup {
+ name: "deviceproductinfoconstants_aidl",
+ srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"],
+ path: "aidl",
+}
diff --git a/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl b/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl
similarity index 100%
rename from aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl
rename to aidl/android/hardware/display/IDeviceProductInfoConstants.aidl
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index c7c3a34..006e532 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -37,12 +37,18 @@
chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable
chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable
chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/enable
+ chmod 0666 /sys/kernel/tracing/events/sched/sched_wakeup_new/enable
chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable
chmod 0666 /sys/kernel/tracing/events/cgroup/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
chmod 0666 /sys/kernel/tracing/events/power/cpu_idle/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/power/clock_enable/enable
+ chmod 0666 /sys/kernel/tracing/events/power/clock_enable/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/power/clock_disable/enable
+ chmod 0666 /sys/kernel/tracing/events/power/clock_disable/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable
chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
@@ -79,6 +85,8 @@
chmod 0666 /sys/kernel/tracing/events/binder/binder_locked/enable
chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
chmod 0666 /sys/kernel/tracing/events/binder/binder_unlock/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_set_priority/enable
+ chmod 0666 /sys/kernel/tracing/events/binder/binder_set_priority/enable
chmod 0666 /sys/kernel/debug/tracing/events/i2c/enable
chmod 0666 /sys/kernel/tracing/events/i2c/enable
chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
@@ -125,6 +133,8 @@
chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable
chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable
chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/oom/mark_victim/enable
+ chmod 0666 /sys/kernel/tracing/events/oom/mark_victim/enable
chmod 0666 /sys/kernel/debug/tracing/events/task/task_rename/enable
chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable
chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable
@@ -159,6 +169,12 @@
chmod 0666 /sys/kernel/tracing/events/ipi/ipi_exit/enable
chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_raise/enable
chmod 0666 /sys/kernel/tracing/events/ipi/ipi_raise/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_enable/enable
+ chmod 0666 /sys/kernel/tracing/events/clk/clk_disable/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_disable/enable
+ chmod 0666 /sys/kernel/tracing/events/clk/clk_enable/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_set_rate/enable
+ chmod 0666 /sys/kernel/tracing/events/clk/clk_set_rate/enable
# disk
chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 6c86c21..9186514 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -18,8 +18,3 @@
chmod 0666 /sys/kernel/tracing/events/filemap/enable
chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable
- # irq
- chmod 0666 /sys/kernel/tracing/events/irq/enable
- chmod 0666 /sys/kernel/debug/tracing/events/irq/enable
- chmod 0666 /sys/kernel/tracing/events/ipi/enable
- chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 990aa53..3a2c769 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1208,10 +1208,6 @@
static void DumpPacketStats() {
DumpFile("NETWORK DEV INFO", "/proc/net/dev");
- DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
- DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
- DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
- DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
}
static void DumpIpAddrAndRules() {
@@ -1657,8 +1653,6 @@
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
- /* Dump Bluetooth HCI logs */
- ds.AddDir("/data/misc/bluetooth/logs", true);
/* Dump Nfc NCI logs */
ds.AddDir("/data/misc/nfc/logs", true);
@@ -1744,6 +1738,9 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
+ /* Dump Bluetooth HCI logs after getting bluetooth_manager dumpsys */
+ ds.AddDir("/data/misc/bluetooth/logs", true);
+
if (ds.dump_pool_) {
WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
} else {
@@ -2060,7 +2057,7 @@
}
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
- const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
+ const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
@@ -3067,6 +3064,9 @@
android::os::UnlinkAndLogOnError(tmp_path_);
android::os::UnlinkAndLogOnError(screenshot_path_);
android::os::UnlinkAndLogOnError(path_);
+ if (dump_traces_path != nullptr) {
+ android::os::UnlinkAndLogOnError(dump_traces_path);
+ }
}
void Dumpstate::EnableParallelRunIfNeeded() {
diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp
index a7a70c3..81bdbe2 100644
--- a/cmds/idlcli/CommandVibrator.cpp
+++ b/cmds/idlcli/CommandVibrator.cpp
@@ -22,7 +22,7 @@
class IdlCli;
class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
- std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }
+ std::string getDescription() const override { return "Invoke Vibrator IDL APIs."; }
std::string getUsageSummary() const override { return "<api> [arguments]"; }
diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h
index dd84304..24a40d9 100644
--- a/cmds/idlcli/IdlCli.h
+++ b/cmds/idlcli/IdlCli.h
@@ -25,14 +25,47 @@
class IdlCli : public CommandWithSubcommands<IdlCli> {
std::string getDescription() const override { return "Invoke IDL APIs."; }
- std::string getUsageSummary() const override { return "<idl> [arguments]"; }
+ std::string getUsageSummary() const override { return "<idl> [options] [arguments]"; }
UsageDetails getUsageDetails() const override {
UsageDetails details{
+ {"-n <name>", {"Get named service, rather than default."}},
{"<idl>", CommandRegistry<IdlCli>::List()},
};
return details;
}
+
+ Status doArgs(Args &args) override {
+ while (args.get<std::string>().value_or("").find("-") == 0) {
+ auto opt = *args.pop<std::string>();
+ if (opt == "--") {
+ break;
+ } else if (opt == "-n") {
+ if (auto name = args.pop<decltype(mName)>()) {
+ mName = *name;
+ } else {
+ std::cerr << "Missing Value for Name!" << std::endl;
+ return USAGE;
+ }
+ } else {
+ std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+ return USAGE;
+ }
+ }
+ return CommandWithSubcommands::doArgs(args);
+ }
+
+ IdlCli() {}
+
+ std::string mName;
+
+public:
+ static IdlCli &Get() {
+ static IdlCli instance;
+ return instance;
+ }
+
+ auto getName() { return mName; }
};
} // namespace idlcli
diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp
index 9ed9d82..308f294 100644
--- a/cmds/idlcli/main.cpp
+++ b/cmds/idlcli/main.cpp
@@ -19,5 +19,5 @@
int main(const int argc, const char* const argv[]) {
using namespace ::android::idlcli;
- return IdlCli{}.main(Args{argc, argv});
+ return IdlCli::Get().main(Args{argc, argv});
}
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index b874455..262f2e5 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -249,7 +249,7 @@
template <typename T>
class CommandWithSubcommands : public Command {
-private:
+protected:
Status doArgs(Args &args) override {
mCommand = CommandRegistry<T>::Create(*args.get());
if (!mCommand) {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index 6c30a9e..dfbb886 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,24 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
-#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#pragma once
#include <future>
#include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include "IdlCli.h"
#include "utils.h"
-#include "log/log.h"
-
namespace android {
using hardware::Return;
+using idlcli::IdlCli;
static constexpr int NUM_TRIES = 2;
@@ -47,20 +47,34 @@
}
template <typename I>
-inline auto getService() {
- return I::getService();
+inline auto getService(std::string name) {
+ const auto instance = std::string() + I::descriptor + "/" + name;
+ auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+ return I::fromBinder(vibBinder);
}
template <>
-inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
- const auto instance =
- std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
- auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
- return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
+inline auto getService<android::hardware::vibrator::V1_0::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_0::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_1::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_1::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_2::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_2::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_3::IVibrator>(std::string name) {
+ return android::hardware::vibrator::V1_3::IVibrator::getService(name);
}
template <typename I>
-using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+using shared_ptr = std::result_of_t<decltype(getService<I>)&(std::string)>;
template <typename I>
class HalWrapper {
@@ -68,7 +82,8 @@
static std::unique_ptr<HalWrapper> Create() {
// Assume that if getService returns a nullptr, HAL is not available on the
// device.
- auto hal = getService<I>();
+ const auto name = IdlCli::Get().getName();
+ auto hal = getService<I>(name.empty() ? "default" : name);
return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
}
@@ -121,5 +136,3 @@
} // namespace idlcli
} // namespace android
-
-#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 4dfd1d0..0cf50a3 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -2112,8 +2112,9 @@
{
struct stat s;
if (stat(b_path.c_str(), &s) != 0) {
- // Silently ignore for now. The service calling this isn't smart enough to understand
- // lack of artifacts at the moment.
+ // Ignore for now. The service calling this isn't smart enough to
+ // understand lack of artifacts at the moment.
+ LOG(VERBOSE) << "A/B artifact " << b_path << " does not exist!";
return false;
}
if (!S_ISREG(s.st_mode)) {
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 443821c..ef052bd 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -473,24 +473,29 @@
// Run dexopt with the parameters of parameters_.
// TODO(calin): embed the profile name in the parameters.
int Dexopt() {
- std::string dummy;
- return dexopt(parameters_.apk_path,
- parameters_.uid,
- parameters_.pkgName,
- parameters_.instruction_set,
- parameters_.dexopt_needed,
- parameters_.oat_dir,
- parameters_.dexopt_flags,
- parameters_.compiler_filter,
- parameters_.volume_uuid,
- parameters_.shared_libraries,
- parameters_.se_info,
- parameters_.downgrade,
- parameters_.target_sdk_version,
- parameters_.profile_name,
- parameters_.dex_metadata_path,
- parameters_.compilation_reason,
- &dummy);
+ std::string error;
+ int res = dexopt(parameters_.apk_path,
+ parameters_.uid,
+ parameters_.pkgName,
+ parameters_.instruction_set,
+ parameters_.dexopt_needed,
+ parameters_.oat_dir,
+ parameters_.dexopt_flags,
+ parameters_.compiler_filter,
+ parameters_.volume_uuid,
+ parameters_.shared_libraries,
+ parameters_.se_info,
+ parameters_.downgrade,
+ parameters_.target_sdk_version,
+ parameters_.profile_name,
+ parameters_.dex_metadata_path,
+ parameters_.compilation_reason,
+ &error);
+ if (res != 0) {
+ LOG(ERROR) << "During preopt of " << parameters_.apk_path << " got result " << res
+ << " error: " << error;
+ }
+ return res;
}
int RunPreopt() {
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
index dae976e..23b54ee 100644
--- a/cmds/surfacereplayer/proto/Android.bp
+++ b/cmds/surfacereplayer/proto/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 83b6aa0..9d88ca6 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -1,7 +1,16 @@
+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"],
+}
+
prebuilt_etc {
name: "android.hardware.biometrics.face.xml",
product_specific: true,
sub_dir: "permissions",
src: "android.hardware.biometrics.face.xml",
filename_from_src: true,
-}
\ No newline at end of file
+}
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index cac67d4..ee1eee2 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -367,6 +367,10 @@
* the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
* after calling {@link AImageDecoder_rewind}).
*
+ * It is strongly recommended to use setTargetSize only for downscaling, as it
+ * is often more efficient to scale-up when rendering than up-front due to
+ * reduced overall memory.
+ *
* Available since API level 30.
*
* @param width Width of the output (prior to cropping).
diff --git a/include/input/Flags.h b/include/input/Flags.h
index 072dd18..b12a9ed 100644
--- a/include/input/Flags.h
+++ b/include/input/Flags.h
@@ -84,7 +84,7 @@
template <typename F>
constexpr std::optional<std::string_view> flag_name(F flag) {
using U = std::underlying_type_t<F>;
- auto idx = __builtin_ctzl(static_cast<U>(flag));
+ auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
return details::flag_names<F>[idx];
}
@@ -280,4 +280,4 @@
} // namespace flag_operators
} // namespace android
-#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file
+#endif // __UI_INPUT_FLAGS_H
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index b85aecd..ea3c341 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -11,6 +11,15 @@
// 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"],
+}
+
cc_library_static {
name: "libattestation",
cflags: [
@@ -28,4 +37,4 @@
"liblog",
"libcrypto",
],
-}
\ No newline at end of file
+}
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
index 6ce5ea1..8dac0ce 100644
--- a/libs/attestation/tests/Android.bp
+++ b/libs/attestation/tests/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
cc_test {
name: "libattestation_tests",
test_suites: ["device-tests"],
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 2e90142..1fbaa13 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -22,10 +22,6 @@
"name": "binderParcelTest"
},
{
- "name": "binderParcelTest",
- "host": true
- },
- {
"name": "binderLibTest"
},
{
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index a7a6563..c20d9f6 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -113,5 +113,14 @@
* Returns the debug flag for the given package.
* Unknown packages will cause the call to fail.
*/
- boolean isPackageDebuggable(in String packageName);
+ boolean isPackageDebuggable(in String packageName);
+
+ /**
+ * Check whether the given feature name and version is one of the available
+ * features as returned by {@link PackageManager#getSystemAvailableFeatures()}. Since
+ * features are defined to always be backwards compatible, this returns true
+ * if the available feature version is greater than or equal to the
+ * requested version.
+ */
+ boolean hasSystemFeature(in String featureName, in int version);
}
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 5842925..ef71a81 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -136,8 +136,7 @@
template <typename _U>
static std::enable_if_t<
#ifdef HAS_NDK_INTERFACE
- std::is_base_of_v<::ndk::ICInterface, _U> || std::is_same_v<::ndk::SpAIBinder, _U> ||
- std::is_same_v<::ndk::ScopedFileDescriptor, _U> ||
+ std::is_base_of_v<::ndk::ICInterface, _U> ||
std::is_same_v<::ndk::AParcelableHolder, _U>
#else
std::is_base_of_v<IInterface, _U> || std::is_same_v<IBinder, _U> ||
@@ -168,13 +167,19 @@
return std::to_string(t);
} else if constexpr (std::is_same_v<std::string, _T>) {
return t;
+#ifdef HAS_NDK_INTERFACE
+ } else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
+ return (t.get() == nullptr) ? "(null)" : "";
+ } else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
+ return (t.get() == -1) ? "(null)" : "";
+#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
std::stringstream out;
out << t;
return out.str();
#endif
- } else if constexpr (details::IsPointerLike<_T>::value) {
+ } else if constexpr (details::IsPointerLike<_T>::value || std::is_pointer_v<_T>) {
if (!t) return "(null)";
std::stringstream out;
out << ToString(*t);
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 4580751..5df0012 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -96,6 +96,22 @@
bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31);
/**
+ * Returns all declared instances for a particular interface.
+ *
+ * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+ * passed here, then ["foo"] would be returned.
+ *
+ * See also AServiceManager_isDeclared.
+ *
+ * \param interface interface, e.g. 'android.foo.IFoo'
+ * \param context to pass to callback
+ * \param callback taking instance (e.g. 'foo') and context
+ */
+void AServiceManager_forEachDeclaredInstance(const char* interface, void* context,
+ void (*callback)(const char*, void*))
+ __INTRODUCED_IN(31);
+
+/**
* Prevent lazy services without client from shutting down their process
*
* \param persist 'true' if the process should not exit.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index cef0bf3..8d08275 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -118,6 +118,7 @@
AIBinder_getCallingSid; # apex
AIBinder_setRequestingSid; # apex
AServiceManager_isDeclared; # apex llndk
+ AServiceManager_forEachDeclaredInstance; # apex llndk
AServiceManager_registerLazyService; # llndk
AServiceManager_waitForService; # apex llndk
AServiceManager_forceLazyServicesPersist; # llndk
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index cb0987e..1ccd0d2 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -19,6 +19,7 @@
#include "ibinder_internal.h"
#include "status_internal.h"
+#include <android-base/logging.h>
#include <binder/IServiceManager.h>
#include <binder/LazyServiceRegistrar.h>
@@ -28,6 +29,7 @@
using ::android::sp;
using ::android::status_t;
using ::android::String16;
+using ::android::String8;
binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) {
if (binder == nullptr || instance == nullptr) {
@@ -92,6 +94,17 @@
sp<IServiceManager> sm = defaultServiceManager();
return sm->isDeclared(String16(instance));
}
+void AServiceManager_forEachDeclaredInstance(const char* interface, void* context,
+ void (*callback)(const char*, void*)) {
+ CHECK(interface != nullptr);
+ // context may be nullptr
+ CHECK(callback != nullptr);
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ for (const String16& instance : sm->getDeclaredInstances(String16(interface))) {
+ callback(String8(instance).c_str(), context);
+ }
+}
void AServiceManager_forceLazyServicesPersist(bool persist) {
auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
serviceRegistrar.forcePersist(persist);
@@ -110,4 +123,4 @@
void AServiceManager_reRegister() {
auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
serviceRegistrar.reRegister();
-}
\ No newline at end of file
+}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index de1a48d..6a88401 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -270,6 +270,25 @@
EXPECT_EQ(2, out);
}
+void defaultInstanceCounter(const char* instance, void* context) {
+ if (strcmp(instance, "default") == 0) {
+ ++*(size_t*)(context);
+ }
+}
+
+TEST(NdkBinder, GetDeclaredInstances) {
+ bool hasLight = AServiceManager_isDeclared("android.hardware.light.ILights/default");
+
+ size_t count;
+ AServiceManager_forEachDeclaredInstance("android.hardware.light.ILights", &count,
+ defaultInstanceCounter);
+
+ // At the time of writing this test, there is no good interface guaranteed
+ // to be on all devices. Cuttlefish has light, so this will generally test
+ // things.
+ EXPECT_EQ(count, hasLight ? 1 : 0);
+}
+
TEST(NdkBinder, GetLazyService) {
// Not declared in the vintf manifest
ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService));
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2bf3d03..f1b068e 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -40,6 +40,41 @@
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
+macro_rules! assert_eq {
+ ($left:expr, $right:expr $(,)?) => {
+ match (&$left, &$right) {
+ (left, right) => {
+ if *left != *right {
+ eprintln!(
+ "assertion failed: `{:?}` == `{:?}`, {}:{}:{}",
+ &*left,
+ &*right,
+ file!(),
+ line!(),
+ column!()
+ );
+ return Err(StatusCode::FAILED_TRANSACTION);
+ }
+ }
+ }
+ };
+}
+
+macro_rules! assert {
+ ($expr:expr) => {
+ if !$expr {
+ eprintln!(
+ "assertion failed: `{:?}`, {}:{}:{}",
+ $expr,
+ file!(),
+ line!(),
+ column!()
+ );
+ return Err(StatusCode::FAILED_TRANSACTION);
+ }
+ };
+}
+
static SERVICE_ONCE: Once = Once::new();
static mut SERVICE: Option<SpIBinder> = None;
@@ -282,7 +317,7 @@
))?;
}
bindings::Transaction_TEST_FAIL => {
- return Err(StatusCode::FAILED_TRANSACTION)
+ assert!(false);
}
_ => return Err(StatusCode::UNKNOWN_TRANSACTION),
}
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 5bccaca..97626be 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -1,3 +1,12 @@
+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_test {
name: "ftl_test",
test_suites: ["device-tests"],
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index debd664..ff059d7 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -143,7 +143,16 @@
aidl: {
export_aidl_headers: true,
- }
+ },
+
+ pgo: {
+ sampling: true,
+ profile_file: "libgui/libgui.profdata",
+ },
+
+ lto: {
+ thin: true,
+ },
}
// Used by media codec services exclusively as a static lib for
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2e4f858..05e1935 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,15 +17,10 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
-#include <stdint.h>
-#include <sys/types.h>
-
#include <android/gui/ITransactionTraceListener.h>
-
-#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-
+#include <binder/Parcel.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IRegionSamplingListener.h>
@@ -33,16 +28,15 @@
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <system/graphics.h>
-
#include <ui/DisplayMode.h>
#include <ui/DisplayStatInfo.h>
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/HdrCapabilities.h>
#include <ui/StaticDisplayInfo.h>
-
#include <utils/Log.h>
// ---------------------------------------------------------------------------
@@ -411,24 +405,6 @@
return static_cast<status_t>(reply.readInt32());
}
- status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
- bool* outSupport) const override {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- status_t result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
- &reply);
- if (result != NO_ERROR) {
- ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
- return result;
- }
- return reply.readBool(outSupport);
- }
-
void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -454,23 +430,6 @@
}
}
- status_t getGameContentTypeSupport(const sp<IBinder>& display,
- bool* outSupport) const override {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- status_t result = data.writeStrongBinder(display);
- if (result != NO_ERROR) {
- ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getGameContentTypeSupport failed to transact: %d", result);
- return result;
- }
- return reply.readBool(outSupport);
- }
-
void setGameContentType(const sp<IBinder>& display, bool on) override {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -796,6 +755,33 @@
return error;
}
+ virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, layerHandle);
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("addFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ Parcel data, reply;
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+ const status_t error =
+ remote()->transact(BnSurfaceComposer::REMOVE_FPS_LISTENER, data, &reply);
+ if (error != OK) {
+ ALOGE("removeFpsListener: Failed to transact");
+ }
+ return error;
+ }
+
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
ui::DisplayModeId defaultMode, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1423,23 +1409,6 @@
result = reply->writeInt32(result);
return result;
}
-
- case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
- return result;
- }
- bool supported = false;
- result = getAutoLowLatencyModeSupport(display, &supported);
- if (result == NO_ERROR) {
- result = reply->writeBool(supported);
- }
- return result;
- }
-
case SET_AUTO_LOW_LATENCY_MODE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -1457,23 +1426,6 @@
setAutoLowLatencyMode(display, setAllm);
return result;
}
-
- case GET_GAME_CONTENT_TYPE_SUPPORT: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> display = nullptr;
- status_t result = data.readStrongBinder(&display);
- if (result != NO_ERROR) {
- ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
- return result;
- }
- bool supported = false;
- result = getGameContentTypeSupport(display, &supported);
- if (result == NO_ERROR) {
- result = reply->writeBool(supported);
- }
- return result;
- }
-
case SET_GAME_CONTENT_TYPE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = nullptr;
@@ -1491,7 +1443,6 @@
setGameContentType(display, setGameContentTypeOn);
return result;
}
-
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
@@ -1716,6 +1667,32 @@
}
return removeRegionSamplingListener(listener);
}
+ case ADD_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> layerHandle;
+ status_t result = data.readNullableStrongBinder(&layerHandle);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read layer handle");
+ return result;
+ }
+ sp<gui::IFpsListener> listener;
+ result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("addFpsListener: Failed to read listener");
+ return result;
+ }
+ return addFpsListener(layerHandle, listener);
+ }
+ case REMOVE_FPS_LISTENER: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<gui::IFpsListener> listener;
+ status_t result = data.readNullableStrongBinder(&listener);
+ if (result != NO_ERROR) {
+ ALOGE("removeFpsListener: Failed to read listener");
+ return result;
+ }
+ return removeFpsListener(listener);
+ }
case SET_DESIRED_DISPLAY_MODE_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1a643c2..f5cde59 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1904,22 +1904,10 @@
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
-bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
- bool supported = false;
- ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
- return supported;
-}
-
void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
}
-bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
- bool supported = false;
- ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
- return supported;
-}
-
void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
ComposerService::getComposerService()->setGameContentType(display, on);
}
@@ -1994,6 +1982,15 @@
return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
}
+status_t SurfaceComposerClient::addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->addFpsListener(layerHandle, listener);
+}
+
+status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ return ComposerService::getComposerService()->removeFpsListener(listener);
+}
+
bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
bool support = false;
ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
diff --git a/libs/gui/aidl/android/gui/IFpsListener.aidl b/libs/gui/aidl/android/gui/IFpsListener.aidl
new file mode 100644
index 0000000..63d333c
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IFpsListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IFpsListener {
+
+ // Reports the most recent recorded fps for the tree rooted at this layer
+ void onFpsReported(float fps);
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d0ab480..8b64bcf 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,21 +16,17 @@
#pragma once
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-
+#include <android/gui/IFpsListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
#include <gui/FrameTimelineInfo.h>
#include <gui/ITransactionCompletedListener.h>
-
#include <input/Flags.h>
-
#include <math/vec4.h>
-
+#include <stdint.h>
+#include <sys/types.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -40,7 +36,6 @@
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rotation.h>
-
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -219,32 +214,17 @@
ui::ColorMode colorMode) = 0;
/**
- * Returns true if the connected display reports support for HDMI 2.1 Auto
- * Low Latency Mode.
- * For more information, see the HDMI 2.1 specification.
- */
- virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
- bool* outSupport) const = 0;
-
- /**
* Switches Auto Low Latency Mode on/off on the connected display, if it is
- * available. This should only be called if #getAutoLowLatencyMode returns
- * true.
+ * available. This should only be called if the display supports Auto Low
+ * Latency Mode as reported in #getDynamicDisplayInfo.
* For more information, see the HDMI 2.1 specification.
*/
virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
/**
- * Returns true if the connected display reports support for Game Content Type.
- * For more information, see the HDMI 1.4 specification.
- */
- virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
- bool* outSupport) const = 0;
-
- /**
* This will start sending infoframes to the connected display with
- * ContentType=Game (if on=true). This will switch the disply to Game mode.
- * This should only be called if #getGameContentTypeSupport returns true.
+ * ContentType=Game (if on=true). This should only be called if the display
+ * Game Content Type as reported in #getDynamicDisplayInfo.
* For more information, see the HDMI 1.4 specification.
*/
virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
@@ -369,6 +349,23 @@
*/
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
+ /* Registers a listener that streams fps updates from SurfaceFlinger.
+ *
+ * The listener will stream fps updates for the layer tree rooted at layerHandle. Usually, this
+ * should be tied to a task. Layers that are not descendants of that task are out of scope for
+ * FPS computations.
+ *
+ * Multiple listeners may be supported.
+ *
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) = 0;
+ /*
+ * Removes a listener that was streaming fps updates from SurfaceFlinger.
+ */
+ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0;
+
/* Sets the refresh rate boundaries for the display.
*
* The primary refresh rate range represents display manager's general guidance on the display
@@ -563,9 +560,9 @@
CAPTURE_DISPLAY_BY_ID,
NOTIFY_POWER_BOOST,
SET_GLOBAL_SHADOW_SETTINGS,
- GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+ GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_AUTO_LOW_LATENCY_MODE,
- GET_GAME_CONTENT_TYPE_SUPPORT,
+ GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
@@ -574,6 +571,8 @@
GET_GPU_CONTEXT_PRIORITY,
GET_EXTRA_BUFFER_COUNT,
GET_DYNAMIC_DISPLAY_INFO,
+ ADD_FPS_LISTENER,
+ REMOVE_FPS_LISTENER,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6f0f18c..ab0347c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -143,17 +143,11 @@
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
- // Reports whether the connected display supports Auto Low Latency Mode
- static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
-
// Switches on/off Auto Low Latency Mode on the connected display. This should only be
// called if the connected display supports Auto Low Latency Mode as reported by
// #getAutoLowLatencyModeSupport
static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
- // Reports whether the connected display supports Game content type
- static bool getGameContentTypeSupport(const sp<IBinder>& display);
-
// Turns Game mode on/off on the connected display. This should only be called
// if the display supports Game content type, as reported by #getGameContentTypeSupport
static void setGameContentType(const sp<IBinder>& display, bool on);
@@ -595,6 +589,9 @@
const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener);
static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+ static status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener);
+ static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
private:
virtual void onFirstRef();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3397198..91b2aff 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -757,15 +757,7 @@
const sp<IScreenCaptureListener>& /* captureListener */) override {
return NO_ERROR;
}
- status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
- bool* /*outSupport*/) const override {
- return NO_ERROR;
- }
void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
- bool* /*outSupport*/) const override {
- return NO_ERROR;
- }
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
const sp<IScreenCaptureListener>& /* captureListener */) override {
@@ -832,6 +824,11 @@
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
+ status_t addFpsListener(const sp<IBinder>& /*layerHandle*/,
+ const sp<gui::IFpsListener>& /*listener*/) {
+ return NO_ERROR;
+ }
+ status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
ui::DisplayModeId /*defaultMode*/,
bool /*allowGroupSwitching*/,
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 3cf9f3f..907eb67 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -39,6 +39,7 @@
"com.android.media.swcodec",
"com.android.neuralnetworks",
],
+
min_sdk_version: "29",
export_include_dirs: ["include"],
@@ -49,4 +50,23 @@
}
}
+cc_library_headers {
+ name: "libmath_headers",
+ export_include_dirs: ["include"],
+ host_supported: true,
+ vendor_available: true,
+
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ target: {
+ windows: {
+ enabled: true,
+ }
+ }
+}
+
subdirs = ["tests"]
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 6fb149a..72d33bc 100644
--- a/libs/math/OWNERS
+++ b/libs/math/OWNERS
@@ -1,6 +1,3 @@
-jaesoo@google.com
-jiyong@google.com
mathias@google.com
-pawin@google.com
randolphs@google.com
romainguy@google.com
diff --git a/libs/math/include/math/HashCombine.h b/libs/math/include/math/HashCombine.h
new file mode 100644
index 0000000..e91b52b
--- /dev/null
+++ b/libs/math/include/math/HashCombine.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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>
+
+namespace android {
+static inline void hashCombineSingleHashed(size_t& combinedHash, size_t hash) {
+ combinedHash = 31 * combinedHash + hash;
+}
+
+template<typename T>
+static inline void hashCombineSingle(size_t& combinedHash, const T& val) {
+ hashCombineSingleHashed(combinedHash, std::hash<T>{}(val));
+}
+
+template<typename... Types>
+static inline size_t hashCombine(const Types& ... args) {
+ size_t hash = 0;
+ ( hashCombineSingle(hash, args), ... );
+ return hash;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 20f852f..0dac662 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -19,9 +19,11 @@
#include <math.h>
#include <stdint.h>
+#include <math/HashCombine.h>
#include <sys/types.h>
#include <cmath>
+#include <functional>
#include <limits>
#include <iostream>
@@ -250,6 +252,17 @@
}
return r;
}
+
+ // This isn't strictly a unary operator, but it is a common place shared between both
+ // matrix and vector classes
+ size_t hash() const {
+ VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+ size_t hashed = 0;
+ for (size_t i = 0; i < rv.size(); i++) {
+ android::hashCombineSingle(hashed, rv[i]);
+ }
+ return hashed;
+ }
};
/*
@@ -606,3 +619,16 @@
// -------------------------------------------------------------------------------------
} // namespace details
} // namespace android
+
+namespace std {
+ template<template<typename T> class VECTOR, typename T>
+ struct hash<VECTOR<T>> {
+ static constexpr bool IS_VECTOR =
+ std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
+
+ typename std::enable_if<IS_VECTOR, size_t>::type
+ operator()(const VECTOR<T>& v) const {
+ return v.hash();
+ }
+ };
+}
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 617a0ab..5ec9bf7 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <functional>
#include <iosfwd>
#include <limits>
#include <type_traits>
@@ -202,6 +203,12 @@
inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
};
+template<> struct hash<android::half> {
+ size_t operator()(const android::half& half) {
+ return std::hash<float>{}(half);
+ }
+};
+
} // namespace std
#ifdef LIKELY_DEFINED_LOCAL
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
index 4a7c4dd..14fb72a 100644
--- a/libs/math/tests/Android.bp
+++ b/libs/math/tests/Android.bp
@@ -50,3 +50,10 @@
static_libs: ["libmath"],
cflags: ["-Wall", "-Werror"],
}
+
+cc_test {
+ name: "hashcombine_test",
+ srcs: ["hashcombine_test.cpp"],
+ static_libs: ["libmath"],
+ cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 604072e..a514d98 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -94,4 +94,13 @@
EXPECT_EQ(f4.xy, h2);
}
+
+TEST_F(HalfTest, Hash) {
+ float4 f4a(1,2,3,4);
+ float4 f4b(2,2,3,4);
+ half4 h4a(f4a), h4b(f4b);
+
+ EXPECT_NE(std::hash<half4>{}(h4a), std::hash<half4>{}(h4b));
+}
+
}; // namespace android
diff --git a/libs/math/tests/hashcombine_test.cpp b/libs/math/tests/hashcombine_test.cpp
new file mode 100644
index 0000000..96c6e81
--- /dev/null
+++ b/libs/math/tests/hashcombine_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 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_TAG "HashCombineTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HashCombineTest : public testing::Test {
+protected:
+};
+
+TEST_F(HashCombineTest, Basics) {
+ char a = 40;
+ int b = 32;
+ int c = 55;
+ float d = 42.f;
+ float d_ = 42.1f;
+
+ EXPECT_NE(hashCombine(a, b, c, d), hashCombine(a, b, c, d_));
+}
+
+}; // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index b2ad22d..79839c1 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -49,7 +49,8 @@
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
return renderengine::threaded::RenderEngineThreaded::create(
- [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
+ [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
+ renderEngineType);
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
@@ -66,12 +67,14 @@
.setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly)
.setSupportsBackgroundBlur(args.supportsBackgroundBlur)
.setContextPriority(args.contextPriority)
- .setRenderEngineType(RenderEngineType::SKIA_GL_THREADED)
+ .setRenderEngineType(renderEngineType)
.build();
ALOGD("Threaded RenderEngine with SkiaGL Backend");
- return renderengine::threaded::RenderEngineThreaded::create([skiaArgs]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
- });
+ return renderengine::threaded::RenderEngineThreaded::create(
+ [skiaArgs]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
+ },
+ renderEngineType);
}
case RenderEngineType::GLES:
default:
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 70ae0b2..397f038 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -379,7 +379,8 @@
GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLConfig config, EGLContext ctxt, EGLSurface stub,
EGLContext protectedContext, EGLSurface protectedStub)
- : mEGLDisplay(display),
+ : RenderEngine(args.renderEngineType),
+ mEGLDisplay(display),
mEGLConfig(config),
mEGLContext(ctxt),
mStubSurface(stub),
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 163a163..572d348 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -87,6 +87,10 @@
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
+ RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+
+ RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
+
virtual ~RenderEngine() = 0;
// ----- BEGIN DEPRECATED INTERFACE -----
@@ -192,8 +196,14 @@
// also supports background blur. If false, no blur will be applied when drawing layers.
virtual bool supportsBackgroundBlur() = 0;
+ // Returns the current type of RenderEngine instance that was created.
+ // TODO(b/180767535): This is only implemented to allow for backend-specific behavior, which
+ // we should not allow in general, so remove this.
+ RenderEngineType getRenderEngineType() const { return mRenderEngineType; }
+
protected:
friend class threaded::RenderEngineThreaded;
+ const RenderEngineType mRenderEngineType;
};
struct RenderEngineCreationArgs {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 327b04c..cbb02a3 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -266,13 +266,13 @@
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
- : mEGLDisplay(display),
+ : SkiaRenderEngine(args.renderEngineType),
+ mEGLDisplay(display),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
mProtectedPlaceholderSurface(protectedPlaceholder),
- mUseColorManagement(args.useColorManagement),
- mRenderEngineType(args.renderEngineType) {
+ mUseColorManagement(args.useColorManagement) {
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
@@ -404,10 +404,6 @@
return true;
}
-static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) {
- return !!(desc.usage & usage);
-}
-
static float toDegrees(uint32_t transform) {
switch (transform) {
case ui::Transform::ROT_90:
@@ -454,11 +450,6 @@
sourceTransfer != destTransfer;
}
-static bool needsLinearEffect(const mat4& colorTransform, ui::Dataspace sourceDataspace,
- ui::Dataspace destinationDataspace) {
- return colorTransform != mat4() || needsToneMapping(sourceDataspace, destinationDataspace);
-}
-
void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
// Only run this if RE is running on its own thread. This way the access to GL
// operations is guaranteed to be happening on the same thread.
@@ -491,14 +482,19 @@
sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
const LayerSettings* layer,
const DisplaySettings& display,
- bool undoPremultipliedAlpha) {
+ bool undoPremultipliedAlpha,
+ bool requiresLinearEffect) {
if (layer->stretchEffect.hasEffect()) {
// TODO: Implement
}
- if (mUseColorManagement &&
- needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) {
- LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
- .outputDataspace = display.outputDataspace,
+ if (requiresLinearEffect) {
+ const ui::Dataspace inputDataspace =
+ mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
+ const ui::Dataspace outputDataspace =
+ mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
+
+ LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
.undoPremultipliedAlpha = undoPremultipliedAlpha};
auto effectIter = mRuntimeEffects.find(effect);
@@ -556,6 +552,26 @@
canvas->translate(-display.clip.left, -display.clip.top);
}
+class AutoSaveRestore {
+public:
+ AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
+ ~AutoSaveRestore() { restore(); }
+ void replace(SkCanvas* canvas) {
+ mCanvas = canvas;
+ mSaveCount = canvas->save();
+ }
+ void restore() {
+ if (mCanvas) {
+ mCanvas->restoreToCount(mSaveCount);
+ mCanvas = nullptr;
+ }
+ }
+
+private:
+ SkCanvas* mCanvas;
+ int mSaveCount;
+};
+
status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const sp<GraphicBuffer>& buffer,
@@ -586,8 +602,6 @@
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
- LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
- "missing usage");
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
if (useFramebufferCache) {
@@ -610,11 +624,10 @@
}
}
+ const ui::Dataspace dstDataspace =
+ mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement
- ? display.outputDataspace
- : ui::Dataspace::UNKNOWN,
- grContext.get());
+ surfaceTextureRef->getTexture()->getOrCreateSurface(dstDataspace, grContext.get());
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
@@ -650,7 +663,7 @@
}
}
- canvas->save();
+ AutoSaveRestore surfaceAutoSaveRestore(canvas);
// Clear the entire canvas with a transparent black to prevent ghost images.
canvas->clear(SK_ColorTRANSPARENT);
initCanvas(canvas, display);
@@ -671,13 +684,18 @@
SkPaint paint;
sk_sp<SkShader> shader =
SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
- toSkColorSpace(mUseColorManagement ? display.outputDataspace
- : ui::Dataspace::UNKNOWN));
+ toSkColorSpace(dstDataspace));
paint.setShader(shader);
clearRegion.setRects(skRects, numRects);
canvas->drawRegion(clearRegion, paint);
}
+ // setup color filter if necessary
+ sk_sp<SkColorFilter> displayColorTransform;
+ if (display.colorTransform != mat4()) {
+ displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+ }
+
for (const auto& layer : layers) {
ATRACE_NAME("DrawLayer");
@@ -705,7 +723,7 @@
// assign dstCanvas to canvas and ensure that the canvas state is up to date
canvas = dstCanvas;
- canvas->save();
+ surfaceAutoSaveRestore.replace(canvas);
initCanvas(canvas, display);
LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
@@ -717,7 +735,7 @@
activeSurface = dstSurface;
}
- canvas->save();
+ SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
// Record the name of the layer if the capture is running.
std::stringstream layerSettings;
@@ -765,15 +783,34 @@
}
}
- const ui::Dataspace targetDataspace = mUseColorManagement
- ? (needsLinearEffect(layer->colorTransform, layer->sourceDataspace,
- display.outputDataspace)
- // If we need to map to linear space, then mark the source image with the
- // same colorspace as the destination surface so that Skia's color
- // management is a no-op.
- ? display.outputDataspace
- : layer->sourceDataspace)
- : ui::Dataspace::UNKNOWN;
+ // Shadows are assumed to live only on their own layer - it's not valid
+ // to draw the boundary rectangles when there is already a caster shadow
+ // TODO(b/175915334): consider relaxing this restriction to enable more flexible
+ // composition - using a well-defined invalid color is long-term less error-prone.
+ if (layer->shadow.length > 0) {
+ const auto rect = layer->geometry.roundedCornersRadius > 0
+ ? getSkRect(layer->geometry.roundedCornersCrop)
+ : bounds;
+ drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+ continue;
+ }
+
+ const bool requiresLinearEffect = layer->colorTransform != mat4() ||
+ (mUseColorManagement &&
+ needsToneMapping(layer->sourceDataspace, display.outputDataspace));
+
+ // quick abort from drawing the remaining portion of the layer
+ if (layer->alpha == 0 && !requiresLinearEffect &&
+ (!displayColorTransform || displayColorTransform->isAlphaUnchanged())) {
+ continue;
+ }
+
+ // If we need to map to linear space or color management is disabled, then mark the source
+ // image with the same colorspace as the destination surface so that Skia's color
+ // management is a no-op.
+ const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
+ ? dstDataspace
+ : layer->sourceDataspace;
SkPaint paint;
if (layer->source.buffer.buffer) {
@@ -792,7 +829,7 @@
}
sk_sp<SkImage> image =
- imageTextureRef->getTexture()->makeImage(targetDataspace,
+ imageTextureRef->getTexture()->makeImage(layerDataspace,
item.usePremultipliedAlpha
? kPremul_SkAlphaType
: kUnpremul_SkAlphaType,
@@ -848,12 +885,12 @@
if (item.isOpaque) {
shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
SkShaders::Color(SkColors::kBlack,
- toSkColorSpace(targetDataspace)));
+ toSkColorSpace(layerDataspace)));
}
- paint.setShader(
- createRuntimeEffectShader(shader, layer, display,
- !item.isOpaque && item.usePremultipliedAlpha));
+ paint.setShader(createRuntimeEffectShader(shader, layer, display,
+ !item.isOpaque && item.usePremultipliedAlpha,
+ requiresLinearEffect));
paint.setAlphaf(layer->alpha);
} else {
ATRACE_NAME("DrawColor");
@@ -862,35 +899,22 @@
.fG = color.g,
.fB = color.b,
.fA = layer->alpha},
- toSkColorSpace(targetDataspace));
+ toSkColorSpace(layerDataspace));
paint.setShader(createRuntimeEffectShader(shader, layer, display,
- /* undoPremultipliedAlpha */ false));
+ /* undoPremultipliedAlpha */ false,
+ requiresLinearEffect));
}
- sk_sp<SkColorFilter> filter =
- SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+ paint.setColorFilter(displayColorTransform);
- paint.setColorFilter(filter);
-
- if (layer->shadow.length > 0) {
- const auto rect = layer->geometry.roundedCornersRadius > 0
- ? getSkRect(layer->geometry.roundedCornersCrop)
- : bounds;
- drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+ if (layer->geometry.roundedCornersRadius > 0) {
+ paint.setAntiAlias(true);
+ canvas->drawRRect(getRoundedRect(layer), paint);
} else {
- // Shadows are assumed to live only on their own layer - it's not valid
- // to draw the boundary retangles when there is already a caster shadow
- // TODO(b/175915334): consider relaxing this restriction to enable more flexible
- // composition - using a well-defined invalid color is long-term less error-prone.
- // Push the clipRRect onto the clip stack. Draw the image. Pop the clip.
- if (layer->geometry.roundedCornersRadius > 0) {
- canvas->clipRRect(getRoundedRect(layer), true);
- }
canvas->drawRect(bounds, paint);
}
- canvas->restore();
}
- canvas->restore();
+ surfaceAutoSaveRestore.restore();
mCapture->endCapture();
{
ATRACE_NAME("flush surface");
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 5779ae6..1c3a633 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -92,11 +92,12 @@
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
const ShadowSettings& shadowSettings);
- // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
- // shader. Otherwise it returns the input shader.
+ // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
+ // Otherwise it returns the input shader.
sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
const DisplaySettings& display,
- bool undoPremultipliedAlpha);
+ bool undoPremultipliedAlpha,
+ bool requiresLinearEffect);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
@@ -129,10 +130,6 @@
bool mInProtectedContext = false;
// Object to capture commands send to Skia.
std::unique_ptr<SkiaCapture> mCapture;
-
- // Keep this information as a local variable to determine whether the access of the GL
- // operations is working on the same threads.
- const RenderEngineType mRenderEngineType = RenderEngineType::SKIA_GL;
};
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 12b8586..79a1040 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -36,6 +36,7 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
+ SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
~SkiaRenderEngine() override {}
virtual void primeCache() const override{};
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 08b672a..63aa4c8 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -32,7 +32,8 @@
void SetUp() override {
mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
- [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); });
+ [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); },
+ renderengine::RenderEngine::RenderEngineType::THREADED);
}
std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index f448135..7c7d165 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -34,11 +34,13 @@
namespace renderengine {
namespace threaded {
-std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) {
- return std::make_unique<RenderEngineThreaded>(std::move(factory));
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory,
+ RenderEngineType type) {
+ return std::make_unique<RenderEngineThreaded>(std::move(factory), type);
}
-RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) {
+RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type)
+ : RenderEngine(type) {
ATRACE_CALL();
std::lock_guard lockThread(mThreadMutex);
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 8279cbc..d362e17 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -37,9 +37,10 @@
*/
class RenderEngineThreaded : public RenderEngine {
public:
- static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory);
+ static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory,
+ RenderEngineType type);
- RenderEngineThreaded(CreateInstanceFactory factory);
+ RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
void primeCache() const override;
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ac0ae72..74d17ce 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -94,6 +94,7 @@
"libarect",
"libmath",
],
+
}
cc_library_shared {
@@ -238,9 +239,11 @@
},
header_libs: [
"libnativewindow_headers",
+ "libmath_headers",
],
export_header_lib_headers: [
"libnativewindow_headers",
+ "libmath_headers",
],
min_sdk_version: "29",
}
diff --git a/libs/ui/DynamicDisplayInfo.cpp b/libs/ui/DynamicDisplayInfo.cpp
index 11acdae..d5c4ef0 100644
--- a/libs/ui/DynamicDisplayInfo.cpp
+++ b/libs/ui/DynamicDisplayInfo.cpp
@@ -39,7 +39,9 @@
FlattenableHelpers::getFlattenedSize(activeDisplayModeId) +
FlattenableHelpers::getFlattenedSize(supportedColorModes) +
FlattenableHelpers::getFlattenedSize(activeColorMode) +
- FlattenableHelpers::getFlattenedSize(hdrCapabilities);
+ FlattenableHelpers::getFlattenedSize(hdrCapabilities) +
+ FlattenableHelpers::getFlattenedSize(autoLowLatencyModeSupported) +
+ FlattenableHelpers::getFlattenedSize(gameContentTypeSupported);
}
status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const {
@@ -51,6 +53,8 @@
RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedColorModes));
RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeColorMode));
RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, hdrCapabilities));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, autoLowLatencyModeSupported));
+ RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, gameContentTypeSupported));
return OK;
}
@@ -60,6 +64,8 @@
RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedColorModes));
RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeColorMode));
RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &hdrCapabilities));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &autoLowLatencyModeSupported));
+ RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &gameContentTypeSupported));
return OK;
}
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 5110a6c..a0b5fe7 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,3 +1,4 @@
+adyabr@google.com
alecmouri@google.com
chrisforbes@google.com
jreck@google.com
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 6c349b7..a4c2f71 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -41,6 +41,14 @@
ui::ColorMode activeColorMode;
HdrCapabilities hdrCapabilities;
+ // True if the display reports support for HDMI 2.1 Auto Low Latency Mode.
+ // For more information, see the HDMI 2.1 specification.
+ bool autoLowLatencyModeSupported;
+
+ // True if the display reports support for Game Content Type.
+ // For more information, see the HDMI 1.4 specification.
+ bool gameContentTypeSupported;
+
std::optional<ui::DisplayMode> getActiveDisplayMode() const;
bool isFixedSize() const { return false; }
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 5d329ea..4c9c7b7 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,7 @@
#pragma once
+#include <math/HashCombine.h>
#include <ostream>
namespace android {
@@ -62,3 +63,13 @@
}
} // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::FloatRect> {
+ size_t operator()(const android::FloatRect& rect) const {
+ return android::hashCombine(rect.left, rect.top, rect.right, rect.bottom);
+ }
+};
+} // namespace std
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 58323e5..9e24a07 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
+#include <math/HashCombine.h>
#include <ui/FloatRect.h>
#include <ui/Point.h>
#include <ui/Size.h>
@@ -234,4 +235,13 @@
}; // namespace android
+namespace std {
+template <>
+struct hash<android::Rect> {
+ size_t operator()(const android::Rect& rect) const {
+ return android::hashCombine(rect.left, rect.top, rect.right, rect.bottom);
+ }
+};
+} // namespace std
+
#endif // ANDROID_UI_RECT
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 6bb7b8d..927c334 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <ostream>
+#include <math/HashCombine.h>
#include <ui/Rect.h>
#include <utils/Flattenable.h>
@@ -234,4 +235,17 @@
// ---------------------------------------------------------------------------
}; // namespace android
+namespace std {
+template <>
+struct hash<android::Region> {
+ size_t operator()(const android::Region& region) const {
+ size_t hash = 0;
+ for (const android::Rect& rect : region) {
+ android::hashCombineSingle(hash, rect);
+ }
+ return hash;
+ }
+};
+} // namespace std
+
#endif // ANDROID_UI_REGION_H
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
index 5499a5b..9cc36bb 100644
--- a/libs/ui/tests/Rect_test.cpp
+++ b/libs/ui/tests/Rect_test.cpp
@@ -259,4 +259,33 @@
EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
}
+TEST(RectTest, RectHash) {
+ const std::vector<Rect> rects = {
+ Rect(10, 20, 50, 60), Rect(11, 20, 50, 60), Rect(11, 21, 50, 60),
+ Rect(11, 21, 51, 60), Rect(11, 21, 51, 61),
+ };
+
+ for (const auto& a : rects) {
+ for (const auto& b : rects) {
+ const bool hashEq = std::hash<Rect>{}(a) == std::hash<Rect>{}(b);
+ EXPECT_EQ(a == b, hashEq);
+ }
+ }
+}
+
+TEST(RectTest, FloatRectHash) {
+ const std::vector<FloatRect> floatRects = {
+ Rect(10, 20, 50, 60).toFloatRect(), Rect(11, 20, 50, 60).toFloatRect(),
+ Rect(11, 21, 50, 60).toFloatRect(), Rect(11, 21, 51, 60).toFloatRect(),
+ Rect(11, 21, 51, 61).toFloatRect(),
+ };
+
+ for (const auto& a : floatRects) {
+ for (const auto& b : floatRects) {
+ const bool hashEq = std::hash<FloatRect>{}(a) == std::hash<FloatRect>{}(b);
+ EXPECT_EQ(a == b, hashEq);
+ }
+ }
+}
+
} // namespace android::ui
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
index c6b826d..74924bd 100644
--- a/libs/ui/tests/Region_test.cpp
+++ b/libs/ui/tests/Region_test.cpp
@@ -167,5 +167,17 @@
ASSERT_TRUE(touchableRegion.contains(50, 50));
}
+TEST_F(RegionTest, RegionHash) {
+ Region region1;
+ region1.addRectUnchecked(10, 20, 30, 40);
+ region1.addRectUnchecked(40, 30, 20, 10);
+
+ Region region2;
+ region2.addRectUnchecked(11, 20, 30, 40);
+ region2.addRectUnchecked(40, 31, 20, 10);
+
+ EXPECT_NE(std::hash<Region>{}(region1), std::hash<Region>{}(region2));
+}
+
}; // namespace android
diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp
index 2eb2f9f..d4538f1 100644
--- a/libs/vr/libbroadcastring/Android.bp
+++ b/libs/vr/libbroadcastring/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 45bdd35..583ad1d 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index f372bd7..0bda798 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
index fc1f376..e33e03b 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp
+++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp
@@ -5,8 +5,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index e883916..33a0d75 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -5,8 +5,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index 365a676..b0ed950 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 83c30d7..96023dd 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -19,8 +19,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp
index 4ed80a4..fe7feb8 100644
--- a/libs/vr/libdvr/tests/Android.bp
+++ b/libs/vr/libdvr/tests/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
index 9e1e516..fe4dfc7 100644
--- a/libs/vr/libdvrcommon/Android.bp
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index ea73d7a..8046857 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index 532d1a7..216ca9f 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
index 5beee35..38bf4ea 100644
--- a/libs/vr/libperformance/Android.bp
+++ b/libs/vr/libperformance/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 8aca9a5..bf848af 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
index dafd354..095f556 100644
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
index 8f566a0..40a5099 100644
--- a/libs/vr/libvrsensor/Android.bp
+++ b/libs/vr/libvrsensor/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp
index dc8e587..c29181d 100644
--- a/opengl/libs/EGL/egl_angle_platform.cpp
+++ b/opengl/libs/EGL/egl_angle_platform.cpp
@@ -129,8 +129,8 @@
return false;
}
- angleResetDisplayPlatform = reinterpret_cast<ResetDisplayPlatformFunc>(
- eglGetProcAddress("ANGLEResetDisplayPlatform"));
+ angleResetDisplayPlatform =
+ reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform"));
PlatformMethods* platformMethods = nullptr;
if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr,
diff --git a/opengl/tests/configdump/Android.bp b/opengl/tests/configdump/Android.bp
index ffb0c1f..1bb5983 100644
--- a/opengl/tests/configdump/Android.bp
+++ b/opengl/tests/configdump/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/filter/Android.bp b/opengl/tests/filter/Android.bp
index 3b92b37..b93576f 100644
--- a/opengl/tests/filter/Android.bp
+++ b/opengl/tests/filter/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/gl_basic/Android.bp b/opengl/tests/gl_basic/Android.bp
index f777401..f55cd0d 100644
--- a/opengl/tests/gl_basic/Android.bp
+++ b/opengl/tests/gl_basic/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/opengl/tests/tritex/Android.bp b/opengl/tests/tritex/Android.bp
index 759582c..87da93f 100644
--- a/opengl/tests/tritex/Android.bp
+++ b/opengl/tests/tritex/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 4b1f350..33b3e1e 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -50,9 +50,10 @@
// --- NotifyKeyArgs ---
-NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime)
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, nsecs_t downTime)
: NotifyArgs(id, eventTime),
deviceId(deviceId),
source(source),
@@ -63,7 +64,8 @@
keyCode(keyCode),
scanCode(scanCode),
metaState(metaState),
- downTime(downTime) {}
+ downTime(downTime),
+ readTime(readTime) {}
NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other)
: NotifyArgs(other.id, other.eventTime),
@@ -76,13 +78,15 @@
keyCode(other.keyCode),
scanCode(other.scanCode),
metaState(other.metaState),
- downTime(other.downTime) {}
+ downTime(other.downTime),
+ readTime(other.readTime) {}
bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
- return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
- source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
- action == rhs.action && flags == rhs.flags && keyCode == rhs.keyCode &&
- scanCode == rhs.scanCode && metaState == rhs.metaState && downTime == rhs.downTime;
+ return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+ policyFlags == rhs.policyFlags && action == rhs.action && flags == rhs.flags &&
+ keyCode == rhs.keyCode && scanCode == rhs.scanCode && metaState == rhs.metaState &&
+ downTime == rhs.downTime;
}
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -91,16 +95,14 @@
// --- NotifyMotionArgs ---
-NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, MotionClassification classification,
- int32_t edgeFlags, uint32_t pointerCount,
- const PointerProperties* pointerProperties,
- const PointerCoords* pointerCoords, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime,
- const std::vector<TouchVideoFrame>& videoFrames)
+NotifyMotionArgs::NotifyMotionArgs(
+ int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+ int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+ float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+ const std::vector<TouchVideoFrame>& videoFrames)
: NotifyArgs(id, eventTime),
deviceId(deviceId),
source(source),
@@ -119,6 +121,7 @@
xCursorPosition(xCursorPosition),
yCursorPosition(yCursorPosition),
downTime(downTime),
+ readTime(readTime),
videoFrames(videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -145,6 +148,7 @@
xCursorPosition(other.xCursorPosition),
yCursorPosition(other.yCursorPosition),
downTime(other.downTime),
+ readTime(other.readTime),
videoFrames(other.videoFrames) {
for (uint32_t i = 0; i < pointerCount; i++) {
pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -157,11 +161,12 @@
}
bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
- bool equal = id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
- source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
- action == rhs.action && actionButton == rhs.actionButton && flags == rhs.flags &&
- metaState == rhs.metaState && buttonState == rhs.buttonState &&
- classification == rhs.classification && edgeFlags == rhs.edgeFlags &&
+ bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+ deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+ policyFlags == rhs.policyFlags && action == rhs.action &&
+ actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
+ buttonState == rhs.buttonState && classification == rhs.classification &&
+ edgeFlags == rhs.edgeFlags &&
pointerCount == rhs.pointerCount
// PointerProperties and PointerCoords are compared separately below
&& xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index d6b7259..b2ddb42 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -105,7 +105,7 @@
void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
- void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
@@ -249,8 +249,9 @@
const nsecs_t currentTime = now();
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+ NotifyMotionArgs args(/* id */ 0, currentTime, currentTime, DEVICE_ID,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER,
+ AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
pointerProperties, pointerCoords,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 45a007b..ed17e68 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -273,6 +273,7 @@
bool enabled;
int32_t pid;
nsecs_t consumeTime; // time when the event was consumed by InputConsumer
+ int32_t displayId;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index ee6b38b..2db8c13 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -40,110 +40,102 @@
return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
}
-std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
- auto it = mPendingFocusRequests.find(displayId);
- return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
+std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
+ auto it = mFocusRequestByDisplay.find(displayId);
+ return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
}
+/**
+ * 'setInputWindows' is called when the window properties change. Here we will check whether the
+ * currently focused window can remain focused. If the currently focused window remains eligible
+ * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
+ * we will check if the previous focus request is eligible to receive focus.
+ */
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
- // If the current focused window becomes unfocusable, remove focus.
- sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ std::string removeFocusReason;
+
+ // Check if the currently focused window is still focusable.
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
if (currentFocus) {
- FocusResult result = isTokenFocusable(currentFocus, windows);
- if (result != FocusResult::OK) {
- return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
+ Focusability result = isTokenFocusable(currentFocus, windows);
+ if (result == Focusability::OK) {
+ return std::nullopt;
+ }
+ removeFocusReason = NamedEnum::string(result);
+ }
+
+ // We don't have a focused window or the currently focused window is no longer focusable. Check
+ // to see if we can grant focus to the window that previously requested focus.
+ const std::optional<FocusRequest> request = getFocusRequest(displayId);
+ if (request) {
+ sp<IBinder> requestedFocus = request->token;
+ const Focusability result = isTokenFocusable(requestedFocus, windows);
+ const Focusability previousResult = mLastFocusResultByDisplay[displayId];
+ mLastFocusResultByDisplay[displayId] = result;
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId,
+ "Window became focusable. Previous reason: " +
+ NamedEnum::string(previousResult),
+ requestedFocus, request->windowName);
}
}
- // Check if any pending focus requests can be resolved.
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (!pendingRequest) {
- return std::nullopt;
- }
-
- sp<IBinder> requestedFocus = pendingRequest->token;
- std::string windowName = pendingRequest->windowName;
- if (currentFocus == requestedFocus) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
- windowName.c_str(), displayId);
- mPendingFocusRequests.erase(displayId);
- return std::nullopt;
- }
-
- FocusResult result = isTokenFocusable(requestedFocus, windows);
- // If the window from the pending request is now visible, provide it focus.
- if (result == FocusResult::OK) {
- mPendingFocusRequests.erase(displayId);
- return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
- }
-
- if (result != FocusResult::NOT_VISIBLE) {
- // Drop the request if we are unable to change the focus for a reason other than visibility.
- ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
- displayId, NamedEnum::string(result).c_str());
- mPendingFocusRequests.erase(displayId);
- }
- return std::nullopt;
+ // Focused window is no longer focusable and we don't have a suitable focus request to grant.
+ // Remove focus if needed.
+ return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}
std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
const int32_t displayId = request.displayId;
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
- if (request.focusedToken && currentFocus != request.focusedToken) {
- ALOGW("setFocusedWindow %s on display %" PRId32
- " ignored, reason: focusedToken %s is not focused",
- request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
- return std::nullopt;
- }
-
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (pendingRequest) {
- ALOGW("Pending focus request %s on display %" PRId32
- " ignored, reason:replaced by new request",
- pendingRequest->windowName.c_str(), displayId);
-
- // clear any pending focus requests
- mPendingFocusRequests.erase(displayId);
- }
-
if (currentFocus == request.token) {
ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
request.windowName.c_str(), displayId);
return std::nullopt;
}
- FocusResult result = isTokenFocusable(request.token, windows);
- if (result == FocusResult::OK) {
- std::string reason =
- (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
- return updateFocusedWindow(displayId, reason, request.token, request.windowName);
- }
-
- if (result == FocusResult::NOT_VISIBLE) {
- // The requested window is not currently visible. Wait for the window to become visible
- // and then provide it focus. This is to handle situations where a user action triggers
- // a new window to appear. We want to be able to queue any key events after the user
- // action and deliver it to the newly focused window. In order for this to happen, we
- // take focus from the currently focused window so key events can be queued.
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32
- " pending, reason: window is not visible",
- request.windowName.c_str(), displayId);
- mPendingFocusRequests[displayId] = request;
- return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
- } else {
- ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
+ // Handle conditional focus requests, i.e. requests that have a focused token. These requests
+ // are not persistent. If the window is no longer focusable, we expect focus to go back to the
+ // previously focused window.
+ if (request.focusedToken) {
+ if (currentFocus != request.focusedToken) {
+ ALOGW("setFocusedWindow %s on display %" PRId32
+ " ignored, reason: focusedToken %s is not focused",
+ request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
+ return std::nullopt;
+ }
+ Focusability result = isTokenFocusable(request.token, windows);
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId, "setFocusedWindow with focus check",
+ request.token, request.windowName);
+ }
+ ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ return std::nullopt;
}
- return std::nullopt;
+ Focusability result = isTokenFocusable(request.token, windows);
+ // Update focus request. The focus resolver will always try to handle this request if there is
+ // no focused window on the display.
+ mFocusRequestByDisplay[displayId] = request;
+ mLastFocusResultByDisplay[displayId] = result;
+
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId, "setFocusedWindow", request.token,
+ request.windowName);
+ }
+
+ // The requested window is not currently focusable. Wait for the window to become focusable
+ // but remove focus from the current window so that input events can go into a pending queue
+ // and be sent to the window when it becomes focused.
+ return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+ nullptr);
}
-FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
bool allWindowsAreFocusable = true;
bool visibleWindowFound = false;
@@ -165,16 +157,16 @@
}
if (!windowFound) {
- return FocusResult::NO_WINDOW;
+ return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
- return FocusResult::NOT_FOCUSABLE;
+ return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowFound) {
- return FocusResult::NOT_VISIBLE;
+ return Focusability::NOT_VISIBLE;
}
- return FocusResult::OK;
+ return Focusability::OK;
}
std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
@@ -209,15 +201,17 @@
std::string FocusResolver::dump() const {
std::string dump = dumpFocusedWindows();
-
- if (mPendingFocusRequests.empty()) {
- return dump + INDENT "PendingFocusRequests: <none>\n";
+ if (mFocusRequestByDisplay.empty()) {
+ return dump + INDENT "FocusRequests: <none>\n";
}
- dump += INDENT "PendingFocusRequests:\n";
- for (const auto& [displayId, request] : mPendingFocusRequests) {
- dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- request.windowName.c_str());
+ dump += INDENT "FocusRequests:\n";
+ for (const auto& [displayId, request] : mFocusRequestByDisplay) {
+ auto it = mLastFocusResultByDisplay.find(displayId);
+ std::string result =
+ it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
+ displayId, request.windowName.c_str(), result.c_str());
}
return dump;
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index e067ad9..dc5eeeb 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -34,11 +34,18 @@
// is visible with the same token and all window handles with the same token are focusable.
// See FocusResolver::isTokenFocusable
//
-// Focus request - Request will be granted if the window is focusable. If the window is not
-// visible, then the request is kept in a pending state and granted when it becomes visible.
-// If window becomes not focusable, or another request comes in, the pending request is dropped.
+// Focus request - Request will be granted if the window is focusable. If it's not
+// focusable, then the request is persisted and granted when it becomes focusable. The currently
+// focused window will lose focus and any pending keys will be added to a queue so it can be sent
+// to the window when it gets focus.
+//
+// Condition focus request - Request with a focus token specified. Request will be granted if the
+// window is focusable and the focus token is the currently focused. Otherwise, the request is
+// dropped. Conditional focus requests are not persisted. The window will lose focus and go back
+// to the focus token if it becomes not focusable.
//
// Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+// If the previous focus request is focusable, then we will try to grant that window focus.
class FocusResolver {
public:
// Returns the focused window token on the specified display.
@@ -61,7 +68,7 @@
std::string dump() const;
private:
- enum class FocusResult {
+ enum class Focusability {
OK,
NO_WINDOW,
NOT_FOCUSABLE,
@@ -77,8 +84,8 @@
// we expect the focusability of the windows to match since its hard to reason why one window
// can receive focus events and the other cannot when both are backed by the same input channel.
//
- static FocusResult isTokenFocusable(const sp<IBinder>& token,
- const std::vector<sp<InputWindowHandle>>& windows);
+ static Focusability isTokenFocusable(const sp<IBinder>& token,
+ const std::vector<sp<InputWindowHandle>>& windows);
// Focus tracking for keys, trackball, etc. A window token can be associated with one or
// more InputWindowHandles. If a window is mirrored, the window and its mirror will share
@@ -87,15 +94,18 @@
typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
- // This map will store a single pending focus request per display that cannot be currently
- // processed. This can happen if the window requested to be focused is not currently visible.
- // Such a window might become visible later, and these requests would be processed at that time.
- std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
+ // This map will store the focus request per display. When the input window handles are updated,
+ // the current request will be checked to see if it can be processed at that time.
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay;
+
+ // Last reason for not granting a focus request. This is used to add more debug information
+ // in the event logs.
+ std::unordered_map<int32_t /* displayId */, Focusability> mLastFocusResultByDisplay;
std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
int32_t displayId, const std::string& reason, const sp<IBinder>& token,
const std::string& tokenName = "");
- std::optional<FocusRequest> getPendingRequest(int32_t displayId);
+ std::optional<FocusRequest> getFocusRequest(int32_t displayId);
};
} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9898e9d..465e8be 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1439,7 +1439,7 @@
ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
"source=0x%x, sensorType=%s",
entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
- NamedEnum::string(sensorType).c_str());
+ NamedEnum::string(entry->sensorType).c_str());
#endif
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
@@ -2591,6 +2591,7 @@
std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventEntry.eventTime;
commandEntry->userActivityEventType = eventType;
+ commandEntry->displayId = displayId;
postCommandLocked(std::move(commandEntry));
}
@@ -5715,7 +5716,8 @@
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
+ commandEntry->displayId);
mLock.lock();
}
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 909ce54..439d85e 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -134,7 +134,7 @@
uint32_t policyFlags) = 0;
/* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
/* Checks whether a given application pid/uid has permission to inject input events
* into other applications.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 094bc0c..4b7d26d 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -73,12 +73,14 @@
int32_t scanCode;
int32_t metaState;
nsecs_t downTime;
+ nsecs_t readTime;
inline NotifyKeyArgs() { }
- NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime);
+ NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ nsecs_t downTime);
bool operator==(const NotifyKeyArgs& rhs) const;
@@ -120,13 +122,14 @@
float xCursorPosition;
float yCursorPosition;
nsecs_t downTime;
+ nsecs_t readTime;
std::vector<TouchVideoFrame> videoFrames;
inline NotifyMotionArgs() { }
- NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
- int32_t flags, int32_t metaState, int32_t buttonState,
+ NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+ uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, float xCursorPosition,
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e939d1c..1a7fcd5 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,6 +37,7 @@
// #define LOG_NDEBUG 0
#include <android-base/file.h>
+#include <android-base/strings.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <input/KeyCharacterMap.h>
@@ -1361,13 +1362,14 @@
// Some devices report battery capacity as an integer through the "capacity" file
if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
- return std::stoi(buffer);
+ return std::stoi(base::Trim(buffer));
}
// Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
// These values are taken from kernel source code include/linux/power_supply.h
if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
- const auto it = BATTERY_LEVEL.find(buffer);
+ // Remove any white space such as trailing new line
+ const auto it = BATTERY_LEVEL.find(base::Trim(buffer));
if (it != BATTERY_LEVEL.end()) {
return it->second;
}
@@ -1389,9 +1391,8 @@
return std::nullopt;
}
- // Remove trailing new line
- buffer.erase(std::remove(buffer.begin(), buffer.end(), '\n'), buffer.end());
- const auto it = BATTERY_STATUS.find(buffer);
+ // Remove white space like trailing new line
+ const auto it = BATTERY_STATUS.find(base::Trim(buffer));
if (it != BATTERY_STATUS.end()) {
return it->second;
@@ -1567,6 +1568,7 @@
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
event->when = processEventTimestamp(iev);
+ event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
@@ -1769,7 +1771,17 @@
}
}
-status_t EventHub::openDeviceLocked(const std::string& devicePath) {
+void EventHub::openDeviceLocked(const std::string& devicePath) {
+ // If an input device happens to register around the time when EventHub's constructor runs, it
+ // is possible that the same input event node (for example, /dev/input/event3) will be noticed
+ // in both 'inotify' callback and also in the 'scanDirLocked' pass. To prevent duplicate devices
+ // from getting registered, ensure that this path is not already covered by an existing device.
+ for (const auto& [deviceId, device] : mDevices) {
+ if (device->path == devicePath) {
+ return; // device was already registered
+ }
+ }
+
char buffer[80];
ALOGV("Opening device: %s", devicePath.c_str());
@@ -1777,7 +1789,7 @@
int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));
- return -1;
+ return;
}
InputDeviceIdentifier identifier;
@@ -1796,7 +1808,7 @@
if (identifier.name == item) {
ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str());
close(fd);
- return -1;
+ return;
}
}
@@ -1805,7 +1817,7 @@
if (ioctl(fd, EVIOCGVERSION, &driverVersion)) {
ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
- return -1;
+ return;
}
// Get device identifier.
@@ -1813,7 +1825,7 @@
if (ioctl(fd, EVIOCGID, &inputId)) {
ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno));
close(fd);
- return -1;
+ return;
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
@@ -2011,7 +2023,7 @@
if (device->classes == Flags<InputDeviceClass>(0)) {
ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(),
device->identifier.name.c_str());
- return -1;
+ return;
}
// Classify InputDeviceClass::BATTERY.
@@ -2041,7 +2053,7 @@
}
if (registerDeviceForEpollLocked(*device) != OK) {
- return -1;
+ return;
}
device->configureFd();
@@ -2054,7 +2066,6 @@
toString(mBuiltInKeyboardId == deviceId));
addDeviceLocked(std::move(device));
- return OK;
}
void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index cbf3b69..dd1abeb 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -506,8 +506,8 @@
for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
}
-void InputDevice::cancelTouch(nsecs_t when) {
- for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
+void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
}
std::optional<int32_t> InputDevice::getBatteryCapacity() {
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index e6164d3..5e5f85e 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -53,7 +53,11 @@
* A raw event as retrieved from the EventHub.
*/
struct RawEvent {
+ // Time when the event happened
nsecs_t when;
+ // Time when the event was read by EventHub. Only populated for input events.
+ // For other events (device added/removed/etc), this value is undefined and should not be read.
+ nsecs_t readTime;
int32_t deviceId;
int32_t type;
int32_t code;
@@ -563,7 +567,10 @@
bool configureLightsLocked();
};
- status_t openDeviceLocked(const std::string& devicePath);
+ /**
+ * Create a new device for the provided path.
+ */
+ void openDeviceLocked(const std::string& devicePath);
void openVideoDeviceLocked(const std::string& devicePath);
/**
* Try to associate a video device with an input device. If the association succeeds,
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 34c330b..863cd41 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -86,7 +86,7 @@
void cancelVibrate(int32_t token);
bool isVibrating();
std::vector<int32_t> getVibratorIds();
- void cancelTouch(nsecs_t when);
+ void cancelTouch(nsecs_t when, nsecs_t readTime);
bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency);
void disableSensor(InputDeviceSensorType sensorType);
@@ -351,7 +351,7 @@
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mDevice.getAssociatedViewport();
}
- inline void cancelTouch(nsecs_t when) { mDevice.cancelTouch(when); }
+ inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); }
inline void bumpGeneration() { mDevice.bumpGeneration(); }
inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 254b64b..bb12be7 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -265,11 +265,11 @@
mCursorScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ sync(rawEvent->when, rawEvent->readTime);
}
}
-void CursorInputMapper::sync(nsecs_t when) {
+void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
int32_t lastButtonState = mButtonState;
int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
mButtonState = currentButtonState;
@@ -362,8 +362,8 @@
}
// Synthesize key down from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- displayId, policyFlags, lastButtonState, currentButtonState);
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, lastButtonState, currentButtonState);
// Send motion event.
if (downChanged || moved || scrolled || buttonsChanged) {
@@ -383,8 +383,8 @@
while (!released.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
buttonState &= ~actionButton;
- NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
- mSource, displayId, policyFlags,
+ NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
+ getDeviceId(), mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -395,11 +395,11 @@
}
}
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
- MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
- &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
- xCursorPosition, yCursorPosition, downTime,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, motionEventAction, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+ mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
/* videoFrames */ {});
getListener()->notifyMotion(&args);
@@ -408,8 +408,8 @@
while (!pressed.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
buttonState |= actionButton;
- NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags,
+ NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -424,9 +424,10 @@
// Send hover move after UP to tell the application that the mouse is hovering now.
if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
- NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
- 0, metaState, currentButtonState, MotionClassification::NONE,
+ NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
@@ -438,9 +439,10 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, currentButtonState, MotionClassification::NONE,
+ NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags,
+ AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ currentButtonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
@@ -449,7 +451,7 @@
}
// Synthesize key up from buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
displayId, policyFlags, lastButtonState, currentButtonState);
mCursorMotionAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 05bbb26..9a8ca01 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -114,7 +114,7 @@
void configureParameters();
void dumpParameters(std::string& dump);
- void sync(nsecs_t when);
+ void sync(nsecs_t when, nsecs_t readTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 1ce54ae..df1acd4 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -68,7 +68,7 @@
return {};
}
-void InputMapper::cancelTouch(nsecs_t when) {}
+void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {}
bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index bd543e5..15cff1c 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -67,7 +67,7 @@
virtual void cancelVibrate(int32_t token);
virtual bool isVibrating();
virtual std::vector<int32_t> getVibratorIds();
- virtual void cancelTouch(nsecs_t when);
+ virtual void cancelTouch(nsecs_t when, nsecs_t readTime);
virtual bool enableSensor(InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 37aa140..0dc312e 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -299,14 +299,14 @@
case EV_SYN:
switch (rawEvent->code) {
case SYN_REPORT:
- sync(rawEvent->when, false /*force*/);
+ sync(rawEvent->when, rawEvent->readTime, false /*force*/);
break;
}
break;
}
}
-void JoystickInputMapper::sync(nsecs_t when, bool force) {
+void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
if (!filterAxes(force)) {
return;
}
@@ -337,9 +337,10 @@
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
- ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+ AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
&pointerProperties, &pointerCoords, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 0cf60a2..bba95ad 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -92,7 +92,7 @@
// Axes indexed by raw ABS_* axis index.
std::unordered_map<int32_t, Axis> mAxes;
- void sync(nsecs_t when, bool force);
+ void sync(nsecs_t when, nsecs_t readTime, bool force);
bool haveAxis(int32_t axisId);
void pruneAxes(bool ignoreExplicitlyMappedAxes);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8b9f235..2c5a576 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -216,7 +216,8 @@
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
- processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
+ processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
+ usageCode);
}
break;
}
@@ -269,7 +270,8 @@
return false;
}
-void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {
+void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
+ int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
@@ -299,7 +301,7 @@
return;
}
if (policyFlags & POLICY_FLAG_GESTURE) {
- getDeviceContext().cancelTouch(when);
+ getDeviceContext().cancelTouch(when, readTime);
}
KeyDown keyDown;
@@ -350,8 +352,9 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
- policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ getDisplayId(), policyFlags,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 4c0b42a..ca41712 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -86,7 +86,7 @@
bool isKeyboardOrGamepadKey(int32_t scanCode);
bool isMediaKey(int32_t keyCode);
- void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
+ void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode);
bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/mapper/LightInputMapper.h
index 9254720..43141b8 100644
--- a/services/inputflinger/reader/mapper/LightInputMapper.h
+++ b/services/inputflinger/reader/mapper/LightInputMapper.h
@@ -44,7 +44,7 @@
private:
struct Light {
- explicit Light(InputDeviceContext& context, std::string name, int32_t id,
+ explicit Light(InputDeviceContext& context, const std::string& name, int32_t id,
InputDeviceLightType type)
: context(context), name(name), id(id), type(type) {}
virtual ~Light() {}
@@ -65,7 +65,7 @@
};
struct SingleLight : public Light {
- explicit SingleLight(InputDeviceContext& context, std::string name, int32_t id,
+ explicit SingleLight(InputDeviceContext& context, const std::string& name, int32_t id,
int32_t rawId)
: Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {}
int32_t rawId;
@@ -77,7 +77,7 @@
struct RgbLight : public Light {
explicit RgbLight(InputDeviceContext& context, int32_t id,
- std::unordered_map<LightColor, int32_t> rawRgbIds,
+ const std::unordered_map<LightColor, int32_t>& rawRgbIds,
std::optional<int32_t> rawGlobalId)
: Light(context, "RGB", id, InputDeviceLightType::RGB),
rawRgbIds(rawRgbIds),
@@ -98,7 +98,7 @@
};
struct MultiColorLight : public Light {
- explicit MultiColorLight(InputDeviceContext& context, std::string name, int32_t id,
+ explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id,
int32_t rawId)
: Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {}
int32_t rawId;
@@ -109,8 +109,8 @@
};
struct PlayerIdLight : public Light {
- explicit PlayerIdLight(InputDeviceContext& context, std::string name, int32_t id,
- std::unordered_map<int32_t, int32_t> rawLightIds)
+ explicit PlayerIdLight(InputDeviceContext& context, const std::string& name, int32_t id,
+ const std::unordered_map<int32_t, int32_t>& rawLightIds)
: Light(context, name, id, InputDeviceLightType::PLAYER_ID),
rawLightIds(rawLightIds) {}
// Map from player Id to raw light Id
@@ -132,4 +132,4 @@
} // namespace android
-#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 594ff42..e9d0189 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -87,11 +87,11 @@
mRotaryEncoderScrollAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ sync(rawEvent->when, rawEvent->readTime);
}
}
-void RotaryEncoderInputMapper::sync(nsecs_t when) {
+void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
PointerCoords pointerCoords;
pointerCoords.clear();
@@ -121,9 +121,9 @@
int32_t metaState = getContext()->getGlobalMetaState();
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
- NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
- displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
- metaState, /* buttonState */ 0, MotionClassification::NONE,
+ NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0,
+ 0, metaState, /* buttonState */ 0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 7a77b12..e0c9404 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -42,7 +42,7 @@
float mScalingFactor;
int32_t mOrientation;
- void sync(nsecs_t when);
+ void sync(nsecs_t when, nsecs_t readTime);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index a86443d..5344227 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -59,27 +59,27 @@
}
static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
- int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t lastButtonState,
+ 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) {
if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
(currentButtonState & buttonState)) ||
(action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
!(currentButtonState & buttonState))) {
- NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
- action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+ NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
+ policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
context->getListener()->notifyKey(&args);
}
}
static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
- int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t lastButtonState,
+ nsecs_t readTime, int32_t deviceId, uint32_t source,
+ int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
int32_t currentButtonState) {
- synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+ synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
AKEYCODE_BACK);
- synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+ synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
AKEYCODE_FORWARD);
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index d1df37b..16cf010 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1016,7 +1016,8 @@
mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
// Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
+ const nsecs_t readTime = when; // synthetic event
+ abortPointerUsage(when, readTime, 0 /*policyFlags*/);
}
// Inform the dispatcher about the changes.
@@ -1406,11 +1407,11 @@
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- sync(rawEvent->when);
+ sync(rawEvent->when, rawEvent->readTime);
}
}
-void TouchInputMapper::sync(nsecs_t when) {
+void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
const RawState* last =
mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
@@ -1420,6 +1421,7 @@
RawState* next = &mRawStatesPending.back();
next->clear();
next->when = when;
+ next->readTime = readTime;
// Sync button state.
next->buttonState =
@@ -1453,7 +1455,7 @@
void TouchInputMapper::processRawTouches(bool timeout) {
if (mDeviceMode == DeviceMode::DISABLED) {
// Drop all input if the device is disabled.
- cancelTouch(mCurrentRawState.when);
+ cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
mCurrentCookedState.clear();
updateTouchSpots();
return;
@@ -1479,8 +1481,9 @@
mCurrentRawState.copyFrom(next);
if (mCurrentRawState.when < mLastRawState.when) {
mCurrentRawState.when = mLastRawState.when;
+ mCurrentRawState.readTime = mLastRawState.readTime;
}
- cookAndDispatch(mCurrentRawState.when);
+ cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
}
if (count != 0) {
mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
@@ -1494,7 +1497,8 @@
#if DEBUG_STYLUS_FUSION
ALOGD("Timeout expired, synthesizing event with new stylus data");
#endif
- cookAndDispatch(when);
+ const nsecs_t readTime = when; // consider this synthetic event to be zero latency
+ cookAndDispatch(when, readTime);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
@@ -1502,7 +1506,7 @@
}
}
-void TouchInputMapper::cookAndDispatch(nsecs_t when) {
+void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
// Always start with a clean state.
mCurrentCookedState.clear();
@@ -1528,7 +1532,7 @@
// Consume raw off-screen touches before cooking pointer data.
// If touches are consumed, subsequent code will not receive any pointer data.
- if (consumeRawTouches(when, policyFlags)) {
+ if (consumeRawTouches(when, readTime, policyFlags)) {
mCurrentRawState.rawPointerData.clear();
}
@@ -1541,8 +1545,8 @@
applyExternalStylusTouchState(when);
// Synthesize key down from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
- mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+ mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,
mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
@@ -1585,16 +1589,16 @@
pointerUsage = PointerUsage::GESTURES;
}
- dispatchPointerUsage(when, policyFlags, pointerUsage);
+ dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
} else {
updateTouchSpots();
if (!mCurrentMotionAborted) {
- dispatchButtonRelease(when, policyFlags);
- dispatchHoverExit(when, policyFlags);
- dispatchTouches(when, policyFlags);
- dispatchHoverEnterAndMove(when, policyFlags);
- dispatchButtonPress(when, policyFlags);
+ dispatchButtonRelease(when, readTime, policyFlags);
+ dispatchHoverExit(when, readTime, policyFlags);
+ dispatchTouches(when, readTime, policyFlags);
+ dispatchHoverEnterAndMove(when, readTime, policyFlags);
+ dispatchButtonPress(when, readTime, policyFlags);
}
if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
@@ -1603,7 +1607,7 @@
}
// Synthesize key up from raw buttons if needed.
- synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+ synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
mViewport.displayId, policyFlags, mLastCookedState.buttonState,
mCurrentCookedState.buttonState);
@@ -1716,7 +1720,9 @@
void TouchInputMapper::timeoutExpired(nsecs_t when) {
if (mDeviceMode == DeviceMode::POINTER) {
if (mPointerUsage == PointerUsage::GESTURES) {
- dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
+ // Since this is a synthetic event, we can consider its latency to be zero
+ const nsecs_t readTime = when;
+ dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
}
} else if (mDeviceMode == DeviceMode::DIRECT) {
if (mExternalStylusFusionTimeout < when) {
@@ -1738,7 +1744,7 @@
}
}
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
+bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
// Check for release of a virtual key.
if (mCurrentVirtualKey.down) {
if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
@@ -1749,7 +1755,7 @@
ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+ dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
}
return true;
@@ -1776,7 +1782,7 @@
ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+ dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
AKEY_EVENT_FLAG_CANCELED);
}
@@ -1807,7 +1813,7 @@
ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
#endif
- dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN,
+ dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
AKEY_EVENT_FLAG_FROM_SYSTEM |
AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
}
@@ -1838,7 +1844,7 @@
return false;
}
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags) {
int32_t keyCode = mCurrentVirtualKey.keyCode;
int32_t scanCode = mCurrentVirtualKey.scanCode;
@@ -1846,19 +1852,19 @@
int32_t metaState = getContext()->getGlobalMetaState();
policyFlags |= POLICY_FLAG_VIRTUAL;
- NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
- mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
- scanCode, metaState, downTime);
+ NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+ AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
+ keyEventFlags, keyCode, scanCode, metaState, downTime);
getListener()->notifyKey(&args);
}
-void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
if (!currentIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1867,7 +1873,7 @@
}
}
-void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
int32_t metaState = getContext()->getGlobalMetaState();
@@ -1877,8 +1883,8 @@
if (!currentIdBits.isEmpty()) {
// No pointer id changes so this is a move event.
// The listener takes care of batching moves so we don't have to deal with that here.
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1912,7 +1918,7 @@
if (isCanceled) {
ALOGI("Canceling pointer %d for the palm event was detected.", upId);
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
@@ -1927,8 +1933,9 @@
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded && !moveIdBits.isEmpty()) {
ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ metaState, buttonState, 0,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
@@ -1944,8 +1951,8 @@
mDownTime = when;
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
- metaState, buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ 0, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
@@ -1954,13 +1961,13 @@
}
}
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
if (mSentHoverEnter &&
(mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
!mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
int32_t metaState = getContext()->getGlobalMetaState();
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
- mLastCookedState.buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+ metaState, mLastCookedState.buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
mLastCookedState.cookedPointerData.pointerCoords,
mLastCookedState.cookedPointerData.idToIndex,
@@ -1970,13 +1977,14 @@
}
}
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+ uint32_t policyFlags) {
if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
!mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
if (!mSentHoverEnter) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
- metaState, mCurrentRawState.buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
+ 0, 0, metaState, mCurrentRawState.buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1985,8 +1993,8 @@
mSentHoverEnter = true;
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, mCurrentRawState.buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1995,7 +2003,7 @@
}
}
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2003,7 +2011,7 @@
while (!releasedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
buttonState &= ~actionButton;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
actionButton, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -2012,7 +2020,7 @@
}
}
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2020,8 +2028,8 @@
while (!pressedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
buttonState |= actionButton;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
- 0, metaState, buttonState, 0,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2308,38 +2316,38 @@
}
}
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
+void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
PointerUsage pointerUsage) {
if (pointerUsage != mPointerUsage) {
- abortPointerUsage(when, policyFlags);
+ abortPointerUsage(when, readTime, policyFlags);
mPointerUsage = pointerUsage;
}
switch (mPointerUsage) {
case PointerUsage::GESTURES:
- dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+ dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
break;
case PointerUsage::STYLUS:
- dispatchPointerStylus(when, policyFlags);
+ dispatchPointerStylus(when, readTime, policyFlags);
break;
case PointerUsage::MOUSE:
- dispatchPointerMouse(when, policyFlags);
+ dispatchPointerMouse(when, readTime, policyFlags);
break;
case PointerUsage::NONE:
break;
}
}
-void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
switch (mPointerUsage) {
case PointerUsage::GESTURES:
- abortPointerGestures(when, policyFlags);
+ abortPointerGestures(when, readTime, policyFlags);
break;
case PointerUsage::STYLUS:
- abortPointerStylus(when, policyFlags);
+ abortPointerStylus(when, readTime, policyFlags);
break;
case PointerUsage::MOUSE:
- abortPointerMouse(when, policyFlags);
+ abortPointerMouse(when, readTime, policyFlags);
break;
case PointerUsage::NONE:
break;
@@ -2348,7 +2356,8 @@
mPointerUsage = PointerUsage::NONE;
}
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
+void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ bool isTimeout) {
// Update current gesture coordinates.
bool cancelPreviousGesture, finishPreviousGesture;
bool sendEvents =
@@ -2441,8 +2450,8 @@
BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
mPointerGesture.downTime);
@@ -2459,9 +2468,9 @@
while (!upGestureIdBits.isEmpty()) {
uint32_t id = upGestureIdBits.clearFirstMarkedBit();
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
- metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.lastGestureProperties,
+ dispatchMotion(when, readTime, policyFlags, mSource,
+ AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+ AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
0, mPointerGesture.downTime);
@@ -2473,8 +2482,8 @@
// Send motion events for all pointers that moved.
if (moveNeeded) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
@@ -2493,8 +2502,9 @@
mPointerGesture.downTime = when;
}
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
- metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
+ 0, 0, metaState, buttonState, 0,
+ mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
0, mPointerGesture.downTime);
@@ -2503,8 +2513,8 @@
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
mPointerGesture.currentGestureIdToIndex,
@@ -2528,11 +2538,11 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
const int32_t displayId = mPointerController->getDisplayId();
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
- 1, &pointerProperties, &pointerCoords, 0, 0, x, y,
- mPointerGesture.downTime, /* videoFrames */ {});
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+ 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
getListener()->notifyMotion(&args);
}
@@ -2554,13 +2564,13 @@
}
}
-void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
// Cancel previously dispatches pointers.
if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentRawState.buttonState;
- dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+ metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
0, 0, mPointerGesture.downTime);
@@ -3335,7 +3345,7 @@
return true;
}
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
@@ -3363,14 +3373,14 @@
hovering = false;
}
- dispatchPointerSimple(when, policyFlags, down, hovering);
+ dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
}
-void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
+void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+ abortPointerSimple(when, readTime, policyFlags);
}
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
@@ -3417,17 +3427,17 @@
hovering = false;
}
- dispatchPointerSimple(when, policyFlags, down, hovering);
+ dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
}
-void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
- abortPointerSimple(when, policyFlags);
+void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+ abortPointerSimple(when, readTime, policyFlags);
mPointerVelocityControl.reset();
}
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
- bool hovering) {
+void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ bool down, bool hovering) {
int32_t metaState = getContext()->getGlobalMetaState();
if (down || hovering) {
@@ -3448,8 +3458,8 @@
mPointerSimple.down = false;
// Send up.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
mLastRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
&mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
@@ -3462,9 +3472,9 @@
mPointerSimple.hovering = false;
// Send hover exit.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
- mLastRawState.buttonState, MotionClassification::NONE,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+ metaState, mLastRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
&mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
xCursorPosition, yCursorPosition, mPointerSimple.downTime,
@@ -3478,7 +3488,7 @@
mPointerSimple.downTime = when;
// Send down.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
metaState, mCurrentRawState.buttonState,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -3489,8 +3499,8 @@
}
// Send move.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
mCurrentRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
@@ -3504,7 +3514,7 @@
mPointerSimple.hovering = true;
// Send hover enter.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
metaState, mCurrentRawState.buttonState,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -3515,9 +3525,9 @@
}
// Send hover move.
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
- mCurrentRawState.buttonState, MotionClassification::NONE,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+ metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, xCursorPosition, yCursorPosition,
@@ -3537,8 +3547,8 @@
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
- NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
- policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+ displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
mCurrentRawState.buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
&pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
@@ -3556,17 +3566,17 @@
}
}
-void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
- dispatchPointerSimple(when, policyFlags, false, false);
+ dispatchPointerSimple(when, readTime, policyFlags, false, false);
}
-void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
- int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState, int32_t edgeFlags,
- const PointerProperties* properties,
+void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ uint32_t source, int32_t action, int32_t actionButton,
+ int32_t flags, int32_t metaState, int32_t buttonState,
+ int32_t edgeFlags, const PointerProperties* properties,
const PointerCoords* coords, const uint32_t* idToIndex,
BitSet32 idBits, int32_t changedId, float xPrecision,
float yPrecision, nsecs_t downTime) {
@@ -3615,8 +3625,8 @@
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
[this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
- NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
- action, actionButton, flags, metaState, buttonState,
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
+ policyFlags, action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
downTime, std::move(frames));
@@ -3653,9 +3663,9 @@
return changed;
}
-void TouchInputMapper::cancelTouch(nsecs_t when) {
- abortPointerUsage(when, 0 /*policyFlags*/);
- abortTouches(when, 0 /* policyFlags*/);
+void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+ abortPointerUsage(when, readTime, 0 /*policyFlags*/);
+ abortTouches(when, readTime, 0 /* policyFlags*/);
}
// Transform raw coordinate to surface coordinate
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 6621825..cb52e2d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -150,7 +150,7 @@
bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) override;
- void cancelTouch(nsecs_t when) override;
+ void cancelTouch(nsecs_t when, nsecs_t readTime) override;
void timeoutExpired(nsecs_t when) override;
void updateExternalStylusState(const StylusState& state) override;
std::optional<int32_t> getAssociatedDisplayId() override;
@@ -298,6 +298,7 @@
struct RawState {
nsecs_t when;
+ nsecs_t readTime;
// Raw pointer sample data.
RawPointerData rawPointerData;
@@ -310,6 +311,7 @@
void copyFrom(const RawState& other) {
when = other.when;
+ readTime = other.readTime;
rawPointerData.copyFrom(other.rawPointerData);
buttonState = other.buttonState;
rawVScroll = other.rawVScroll;
@@ -318,6 +320,7 @@
void clear() {
when = 0;
+ readTime = 0;
rawPointerData.clear();
buttonState = 0;
rawVScroll = 0;
@@ -702,39 +705,42 @@
void resetExternalStylus();
void clearStylusDataPendingFlags();
- void sync(nsecs_t when);
+ void sync(nsecs_t when, nsecs_t readTime);
- bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
+ bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
void processRawTouches(bool timeout);
- void cookAndDispatch(nsecs_t when);
- void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction,
- int32_t keyEventFlags);
+ void cookAndDispatch(nsecs_t when, nsecs_t readTime);
+ void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ int32_t keyEventAction, int32_t keyEventFlags);
- void dispatchTouches(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
- void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
- void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags);
- void dispatchButtonPress(nsecs_t when, uint32_t policyFlags);
+ void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
void cookPointerData();
- void abortTouches(nsecs_t when, uint32_t policyFlags);
+ void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
- void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ PointerUsage pointerUsage);
+ void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
- void abortPointerGestures(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ bool isTimeout);
+ void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
bool* outFinishPreviousGesture, bool isTimeout);
- void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags);
- void abortPointerStylus(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
- void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+ void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
- void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering);
- void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+ void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down,
+ bool hovering);
+ void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
bool assignExternalStylusId(const RawState& state, bool timeout);
void applyExternalStylusButtonState(nsecs_t when);
@@ -744,9 +750,9 @@
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
// method will take care of setting the index and transmuting the action to DOWN or UP
// it is the first / last pointer to go down / up.
- void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, const PointerProperties* properties,
+ void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ef3dd65..17efb5b 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -18,6 +18,12 @@
#include "../FocusResolver.h"
+#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
+ { \
+ ASSERT_EQ(_oldFocus, _changes->oldFocus); \
+ ASSERT_EQ(_newFocus, _changes->newFocus); \
+ }
+
// atest inputflinger_tests:FocusResolverTest
namespace android::inputdispatcher {
@@ -56,8 +62,7 @@
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
ASSERT_EQ(request.displayId, changes->displayId);
// invisible window cannot get focused
@@ -65,6 +70,7 @@
changes = focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->oldFocus);
ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
// unfocusableWindowToken window cannot get focused
request.token = unfocusableWindowToken;
@@ -99,19 +105,17 @@
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
// mirrored window with one visible window can get focused
request.token = invisibleWindowToken;
changes = focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);
// mirrored window with one or more unfocusable window cannot get focused
request.token = unfocusableWindowToken;
changes = focusResolver.setFocusedWindow(request, windows);
- ASSERT_FALSE(changes);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, SetInputWindows) {
@@ -130,11 +134,10 @@
focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->newFocus);
- // Window visibility changes and the window loses focused
+ // Window visibility changes and the window loses focus
window->setVisible(false);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->newFocus);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, FocusRequestsCanBePending) {
@@ -158,8 +161,100 @@
// Window visibility changes and the window gets focused
invisibleWindow->setVisible(true);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
+}
+
+TEST(FocusResolverTest, FocusRequestsArePersistent) {
+ sp<IBinder> windowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
+ false /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // non-focusable window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = windowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Focusability changes and the window gets focused
+ window->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Visibility changes and the window loses focus
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Visibility changes and the window gets focused
+ window->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Window is gone and the window loses focus
+ changes = focusResolver.setInputWindows(request.displayId, {});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Window returns and the window gains focus
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+}
+
+TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
+ sp<IBinder> hostWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> hostWindow =
+ new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(hostWindow);
+ sp<IBinder> embeddedWindowToken = new BBinder();
+ sp<FakeWindowHandle> embeddedWindow =
+ new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(embeddedWindow);
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = hostWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
+
+ request.focusedToken = hostWindow->getToken();
+ request.token = embeddedWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
+
+ embeddedWindow->setFocusable(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // The embedded window is no longer focusable, provide focus back to the original focused
+ // window.
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
+
+ embeddedWindow->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // The embedded window is focusable again, but we it cannot gain focus unless there is another
+ // focus request.
+ ASSERT_FALSE(changes);
+
+ embeddedWindow->setVisible(false);
+ changes = focusResolver.setFocusedWindow(request, windows);
+ // If the embedded window is not visible/focusable, then we do not grant it focus and the
+ // request is dropped.
+ ASSERT_FALSE(changes);
+
+ embeddedWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ // If the embedded window becomes visble/focusable, nothing changes since the request has been
+ // dropped.
+ ASSERT_FALSE(changes);
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index f58b628..c0ada9d 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -38,13 +38,13 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2);
coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
static constexpr nsecs_t downTime = 2;
- NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
- AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
- AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
- AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
- &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 2 /*readTime*/,
+ 3 /*deviceId*/, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+ 4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/,
+ 0 /*flags*/, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
{} /*videoFrames*/);
return motionArgs;
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index ab74a04..a72df01 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -41,13 +41,13 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
static constexpr nsecs_t downTime = 2;
- NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
- AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
- AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
- AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
- &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 2 /*readTime*/,
+ 3 /*deviceId*/, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+ 4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/,
+ 0 /*flags*/, AMETA_NONE, 0 /*buttonState*/,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/,
+ 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
{} /*videoFrames*/);
return motionArgs;
@@ -85,9 +85,10 @@
TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
// Create a basic key event and send to classifier
- NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
- ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
- AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+ NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/,
+ AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/,
+ AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/,
+ AMETA_NONE, 6 /*downTime*/);
mClassifier->notifyKey(&args);
NotifyKeyArgs outArgs;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d75bf19..51f6f5d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -377,7 +377,7 @@
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
@@ -1236,9 +1236,9 @@
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
- NotifyKeyArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
- POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A, KEY_A,
- AMETA_NONE, currentTime);
+ NotifyKeyArgs args(/* id */ 0, currentTime, 0 /*readTime*/, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A,
+ KEY_A, AMETA_NONE, currentTime);
return args;
}
@@ -1265,7 +1265,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
- NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, source, displayId,
+ NotifyMotionArgs args(/* id */ 0, currentTime, 0 /*readTime*/, DEVICE_ID, source, displayId,
POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2836516..d69bb6a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -50,7 +50,8 @@
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
// An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
// Arbitrary display properties.
static constexpr int32_t DISPLAY_ID = 0;
@@ -442,14 +443,14 @@
device->identifier.name = name;
mDevices.add(deviceId, device);
- enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+ 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, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
}
bool isDeviceEnabled(int32_t deviceId) {
@@ -490,7 +491,7 @@
}
void finishDeviceScan() {
- enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+ enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
}
void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
@@ -604,11 +605,12 @@
device->virtualKeys.push_back(definition);
}
- void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
- int32_t code, int32_t value) {
+ 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;
@@ -1774,18 +1776,21 @@
TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
constexpr int32_t deviceId = END_RESERVED_ID + 1000;
constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+ constexpr nsecs_t when = 0;
constexpr int32_t eventHubId = 1;
+ constexpr nsecs_t readTime = 2;
FakeInputMapper& mapper =
addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
AINPUT_SOURCE_KEYBOARD, nullptr);
- mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1);
+ mFakeEventHub->enqueueEvent(when, readTime, eventHubId, EV_KEY, KEY_A, 1);
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
RawEvent event;
ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
- ASSERT_EQ(0, event.when);
+ ASSERT_EQ(when, event.when);
+ ASSERT_EQ(readTime, event.readTime);
ASSERT_EQ(eventHubId, event.deviceId);
ASSERT_EQ(EV_KEY, event.type);
ASSERT_EQ(KEY_A, event.code);
@@ -2193,12 +2198,14 @@
ASSERT_NE(prevId, keyArgs.id);
prevId = keyArgs.id;
ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+ ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
prevTimestamp = keyArgs.eventTime;
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
ASSERT_NE(prevId, keyArgs.id);
ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+ ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
}
/**
@@ -2675,9 +2682,11 @@
mFakePolicy->clearViewports();
}
- void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, int32_t value) {
+ void 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;
@@ -2756,10 +2765,10 @@
TEST_F(SwitchInputMapperTest, Process) {
SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
- process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
- process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
- process(mapper, ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
NotifySwitchArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
@@ -2929,11 +2938,11 @@
std::chrono::microseconds(10000),
std::chrono::microseconds(0)));
ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, 20000);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, -20000);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Z, 40000);
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
NotifySensorArgs args;
std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
@@ -2959,11 +2968,11 @@
std::chrono::microseconds(10000),
std::chrono::microseconds(0)));
ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RX, 20000);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RY, -20000);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000);
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
NotifySensorArgs args;
std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
@@ -3154,14 +3163,14 @@
int32_t rotatedKeyCode, int32_t displayId) {
NotifyKeyArgs args;
- process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(originalScanCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
- process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(originalScanCode, args.scanCode);
@@ -3194,7 +3203,7 @@
mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Key down by scan code.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -3209,7 +3218,7 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by scan code.
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3223,8 +3232,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key down by usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME, EV_KEY, 0, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, 0, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3238,8 +3247,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, 0, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3253,8 +3262,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key down with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3268,8 +3277,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up with unknown scan code or usage code.
- process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_UNKNOWN, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3283,6 +3292,28 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
}
+/**
+ * Ensure that the readTime is set to the time when the EV_KEY is received.
+ */
+TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ NotifyKeyArgs args;
+
+ // Key down
+ process(mapper, ARBITRARY_TIME, 12 /*readTime*/, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(12, args.readTime);
+
+ // Key up
+ process(mapper, ARBITRARY_TIME, 15 /*readTime*/, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(15, args.readTime);
+}
+
TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
@@ -3299,7 +3330,7 @@
mapper.updateMetaState(AKEYCODE_NUM_LOCK);
// Metakey down.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
@@ -3307,19 +3338,19 @@
ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
// Key down.
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
// Key up.
- process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, KEY_A, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
// Metakey up.
- process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+ process(mapper, ARBITRARY_TIME + 3, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_NONE, args.metaState);
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3406,7 +3437,7 @@
NotifyKeyArgs args;
clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_270);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ 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);
ASSERT_EQ(KEY_UP, args.scanCode);
@@ -3414,7 +3445,7 @@
clearViewports();
prepareDisplay(DISPLAY_ORIENTATION_180);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ 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);
ASSERT_EQ(KEY_UP, args.scanCode);
@@ -3432,16 +3463,16 @@
NotifyKeyArgs args;
// Display id should be ADISPLAY_ID_NONE without any display configuration.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
prepareDisplay(DISPLAY_ORIENTATION_0);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
}
@@ -3462,9 +3493,9 @@
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DISPLAY_ID, args.displayId);
@@ -3472,9 +3503,9 @@
clearViewports();
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(newDisplayId, args.displayId);
}
@@ -3538,48 +3569,48 @@
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle caps lock off.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
// Toggle num lock off.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock off.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
@@ -3607,7 +3638,7 @@
NotifyKeyArgs args;
// Press button "A"
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_A, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_A, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_NONE, args.metaState);
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3615,7 +3646,7 @@
ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
// Button up.
- process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_A, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_A, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AMETA_NONE, args.metaState);
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3722,20 +3753,20 @@
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
@@ -3790,28 +3821,28 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
@@ -3828,28 +3859,28 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_DOWN, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_DOWN, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(uint32_t(0), args.policyFlags);
- process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
@@ -3887,9 +3918,9 @@
int32_t rotatedY) {
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, originalY);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, originalY);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3970,8 +4001,8 @@
// Button press.
// Mostly testing non x/y behavior here so we don't need to check again elsewhere.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -4011,8 +4042,8 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Button release. Should have same down time.
- process(mapper, ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -4059,16 +4090,16 @@
NotifyMotionArgs args;
// Motion in X but not Y.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Motion in Y but not X.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4082,8 +4113,8 @@
NotifyMotionArgs args;
// Button press.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4095,8 +4126,8 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Button release.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4115,10 +4146,10 @@
NotifyMotionArgs args;
// Combined X, Y and Button.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4132,9 +4163,9 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Move X, Y a bit while pressed.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 2);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4142,8 +4173,8 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Release Button.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4228,8 +4259,8 @@
NotifyKeyArgs keyArgs;
// press BTN_LEFT, release BTN_LEFT
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
@@ -4244,8 +4275,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -4268,9 +4299,9 @@
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
@@ -4297,8 +4328,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
@@ -4313,16 +4344,16 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
ASSERT_EQ(0, mFakePointerController->getButtonState());
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(0, motionArgs.buttonState);
@@ -4339,8 +4370,8 @@
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// press BTN_BACK, release BTN_BACK
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4359,8 +4390,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -4380,8 +4411,8 @@
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
// press BTN_SIDE, release BTN_SIDE
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4400,8 +4431,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -4421,8 +4452,8 @@
ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
// press BTN_FORWARD, release BTN_FORWARD
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4441,8 +4472,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -4462,8 +4493,8 @@
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
// press BTN_EXTRA, release BTN_EXTRA
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4482,8 +4513,8 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 0);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
ASSERT_EQ(0, motionArgs.buttonState);
@@ -4513,9 +4544,9 @@
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -4541,9 +4572,9 @@
NotifyMotionArgs args;
// Move.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -4552,8 +4583,8 @@
ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
// Button press.
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -4566,8 +4597,8 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Button release.
- process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 2, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+ process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
@@ -4580,9 +4611,9 @@
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
// Another move.
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 30);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 40);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 30);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 40);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -4601,9 +4632,9 @@
ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -4629,9 +4660,9 @@
mFakePointerController->setButtonState(0);
NotifyMotionArgs args;
- process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -4854,46 +4885,46 @@
}
void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
}
void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
}
void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 0);
}
void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_PRESSURE, pressure);
}
void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
}
void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_DISTANCE, distance);
}
void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX,
int32_t tiltY) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_X, tiltX);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_Y, tiltY);
}
void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
int32_t value) {
- process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
@@ -6181,64 +6212,64 @@
void MultiTouchInputMapperTest::processPosition(MultiTouchInputMapper& mapper, int32_t x,
int32_t y) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x);
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
}
void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper,
int32_t touchMajor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
}
void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper,
int32_t touchMinor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
}
void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
}
void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
}
void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper,
int32_t orientation) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
}
void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
}
void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
}
void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
}
void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_SLOT, slot);
}
void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) {
- process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
}
void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code,
int32_t value) {
- process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
}
void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
}
void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
}
TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
@@ -7672,6 +7703,32 @@
}
/**
+ * Ensure that the readTime is set to the SYN_REPORT value when processing touch events.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareAxes(POSITION);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ prepareDisplay(DISPLAY_ORIENTATION_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);
+ process(mapper, 25, 26 /*readTime*/, EV_SYN, SYN_REPORT, 0);
+
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(26, args.readTime);
+
+ process(mapper, 30, 31 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 110);
+ process(mapper, 30, 32 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 220);
+ process(mapper, 30, 33 /*readTime*/, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(33, args.readTime);
+}
+
+/**
* When the viewport is not active (isActive=false), the touch mapper should be disabled and the
* events should not be delivered to the listener.
*/
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index ad93a65..a489253 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
cc_benchmark {
name: "libpowermanager_benchmarks",
srcs: [
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 0f5037e..69e4041 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
cc_test {
name: "libpowermanager_test",
test_suites: ["device-tests"],
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 625f315..470059a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -154,6 +154,7 @@
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"Layer.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 097f7de..a96dffd 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -414,12 +414,6 @@
return true;
}
- // If the next planned present time is not current, return and try again later
- if (frameIsEarly(expectedPresentTime)) {
- ATRACE_NAME("frameIsEarly()");
- return false;
- }
-
// If this layer doesn't have a frame is shouldn't be presented
if (!hasFrameUpdate()) {
return false;
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 50bc5ed..f9e5b9a 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -29,6 +29,7 @@
"liblog",
"libnativewindow",
"libprotobuf-cpp-lite",
+ "libSurfaceFlingerProp",
"libtimestats",
"libui",
"libutils",
@@ -51,6 +52,11 @@
name: "libcompositionengine",
defaults: ["libcompositionengine_defaults"],
srcs: [
+ "src/planner/CachedSet.cpp",
+ "src/planner/Flattener.cpp",
+ "src/planner/LayerState.cpp",
+ "src/planner/Planner.cpp",
+ "src/planner/Predictor.cpp",
"src/ClientCompositionRequestCache.cpp",
"src/CompositionEngine.cpp",
"src/Display.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 018a687..1fd07b0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -135,6 +135,9 @@
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
+
+ // Gets the sequence number: a serial number that uniquely identifies a Layer
+ virtual int32_t getSequence() const = 0;
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 3be1cc4..4976213 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -31,6 +31,7 @@
#include <ui/Region.h>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
#include "DisplayHardware/DisplayIdentification.h"
@@ -183,6 +184,9 @@
// Outputs a string with a state dump
virtual void dump(std::string&) const = 0;
+ // Outputs planner information
+ virtual void dumpPlannerInfo(const Vector<String16>& args, std::string&) const = 0;
+
// Gets the debug name for the output
virtual const std::string& getName() const = 0;
@@ -264,7 +268,9 @@
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
- virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
+ virtual void updateCompositionState(const CompositionRefreshArgs&) = 0;
+ virtual void planComposition() = 0;
+ virtual void writeCompositionState(const CompositionRefreshArgs&) = 0;
virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
virtual void beginFrame() = 0;
@@ -274,6 +280,7 @@
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
virtual void postFramebuffer() = 0;
+ virtual void renderCachedSets() = 0;
virtual void chooseCompositionStrategy() = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index fb19216..3a84327 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -30,6 +30,8 @@
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
+#include "LayerFE.h"
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -43,7 +45,6 @@
class CompositionEngine;
class Output;
-class LayerFE;
namespace impl {
struct OutputLayerCompositionState;
@@ -88,8 +89,9 @@
// Writes the geometry state to the HWC, or does nothing if this layer does
// not use the HWC. If includeGeometry is false, the geometry state can be
- // skipped.
- virtual void writeStateToHWC(bool includeGeometry) = 0;
+ // skipped. If skipLayer is true, then the alpha of the layer is forced to
+ // 0 so that HWC will ignore it.
+ virtual void writeStateToHWC(bool includeGeometry, bool skipLayer) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
@@ -115,6 +117,10 @@
// Returns true if the composition settings scale pixels
virtual bool needsFiltering() const = 0;
+ // Returns a composition list to be used by RenderEngine if the layer has been overridden
+ // during the composition process
+ virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0;
+
// Debugging
virtual void dump(std::string& result) const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 651230c..eeb20fc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -28,10 +28,15 @@
namespace android::compositionengine::impl {
+namespace planner {
+class Planner;
+} // namespace planner
+
// The implementation class contains the common implementation, but does not
// actually contain the final output state.
class Output : public virtual compositionengine::Output {
public:
+ Output();
~Output() override;
// compositionengine::Output overrides
@@ -48,6 +53,7 @@
void setColorProfile(const ColorProfile&) override;
void dump(std::string&) const override;
+ void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override;
const std::string& getName() const override;
void setName(const std::string&) override;
@@ -77,7 +83,9 @@
void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
- void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+ void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+ void planComposition() override;
+ void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override;
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
@@ -86,12 +94,14 @@
std::optional<base::unique_fd> composeSurfaces(
const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
void postFramebuffer() override;
+ void renderCachedSets() override;
void cacheClientCompositionRequests(uint32_t) override;
// Testing
const ReleasedLayers& getReleasedLayersForTest() const;
void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+ bool plannerEnabled() const { return mPlanner != nullptr; }
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -130,6 +140,7 @@
ReleasedLayers mReleasedLayers;
OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
+ std::unique_ptr<planner::Planner> mPlanner;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 8cb5ae8..f113c34 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
@@ -41,7 +42,7 @@
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
- void writeStateToHWC(bool) override;
+ void writeStateToHWC(bool includeGeometry, bool skipLayer) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -51,6 +52,7 @@
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
+ std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
void dump(std::string&) const override;
@@ -66,7 +68,8 @@
private:
Rect calculateInitialCrop() const;
void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
- void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+ void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
+ bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 9a118d3..5f834be 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -84,6 +84,13 @@
// The Z order index of this layer on this output
uint32_t z{0};
+ // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
+ struct {
+ sp<GraphicBuffer> buffer = nullptr;
+ sp<Fence> acquireFence = nullptr;
+ Rect displayFrame = {};
+ } overrideInfo;
+
/*
* HWC state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
new file mode 100644
index 0000000..00424b2
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+#include <chrono>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+
+std::string durationString(std::chrono::milliseconds duration);
+
+class LayerState;
+
+class CachedSet {
+public:
+ class Layer {
+ public:
+ Layer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ const LayerState* getState() const { return mState; }
+ const std::string& getName() const { return mState->getName(); }
+ Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
+ const sp<GraphicBuffer>& getBuffer() const { return mState->getBuffer(); }
+ int64_t getFramesSinceBufferUpdate() const { return mState->getFramesSinceBufferUpdate(); }
+ NonBufferHash getHash() const { return mHash; }
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+
+ private:
+ const LayerState* mState;
+ NonBufferHash mHash;
+ std::chrono::steady_clock::time_point mLastUpdate;
+ };
+
+ CachedSet(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+ CachedSet(Layer layer);
+
+ void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate);
+
+ std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; }
+ NonBufferHash getFingerprint() const { return mFingerprint; }
+ size_t getLayerCount() const { return mLayers.size(); }
+ const Layer& getFirstLayer() const { return mLayers[0]; }
+ const Rect& getBounds() const { return mBounds; }
+ size_t getAge() const { return mAge; }
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<Fence>& getDrawFence() const { return mDrawFence; }
+
+ NonBufferHash getNonBufferHash() const;
+
+ size_t getComponentDisplayCost() const;
+ size_t getCreationCost() const;
+ size_t getDisplayCost() const;
+
+ bool hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const;
+ bool hasReadyBuffer() const;
+
+ // Decomposes this CachedSet into a vector of its layers as individual CachedSets
+ std::vector<CachedSet> decompose() const;
+
+ void updateAge(std::chrono::steady_clock::time_point now);
+
+ void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
+ void append(const CachedSet& other) {
+ mBuffer = nullptr;
+ mDrawFence = nullptr;
+
+ mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(other.mBounds);
+ mBounds = boundingRegion.getBounds();
+ }
+ void incrementAge() { ++mAge; }
+
+ void render(renderengine::RenderEngine&);
+
+ void dump(std::string& result) const;
+
+private:
+ CachedSet() = default;
+
+ NonBufferHash mFingerprint = 0;
+ std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
+ std::vector<Layer> mLayers;
+ Rect mBounds = Rect::EMPTY_RECT;
+ size_t mAge = 0;
+ sp<GraphicBuffer> mBuffer;
+ sp<Fence> mDrawFence;
+
+ static const bool sDebugHighlighLayers;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
new file mode 100644
index 0000000..6c86408
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/CachedSet.h>
+#include <compositionengine/impl/planner/LayerState.h>
+
+#include <vector>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+using namespace std::chrono_literals;
+
+class LayerState;
+class Predictor;
+
+class Flattener {
+public:
+ Flattener(Predictor& predictor) : mPredictor(predictor) {}
+
+ void setDisplaySize(ui::Size size) { mDisplaySize = size; }
+
+ NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash);
+
+ void renderCachedSets(renderengine::RenderEngine&);
+
+ void reset();
+
+ void dump(std::string& result) const;
+
+private:
+ size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
+
+ void resetActivities(NonBufferHash, std::chrono::steady_clock::time_point now);
+
+ void updateLayersHash();
+
+ bool mergeWithCachedSets(const std::vector<const LayerState*>& layers,
+ std::chrono::steady_clock::time_point now);
+
+ void buildCachedSets(std::chrono::steady_clock::time_point now);
+
+ Predictor& mPredictor;
+
+ ui::Size mDisplaySize;
+
+ NonBufferHash mCurrentGeometry;
+ std::chrono::steady_clock::time_point mLastGeometryUpdate;
+
+ std::vector<CachedSet> mLayers;
+ NonBufferHash mLayersHash = 0;
+ std::optional<CachedSet> mNewCachedSet;
+
+ // Statistics
+ size_t mUnflattenedDisplayCost = 0;
+ size_t mFlattenedDisplayCost = 0;
+ std::unordered_map<size_t, size_t> mInitialLayerCounts;
+ std::unordered_map<size_t, size_t> mFinalLayerCounts;
+ size_t mCachedSetCreationCount = 0;
+ size_t mCachedSetCreationCost = 0;
+ std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
+
+ static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
new file mode 100644
index 0000000..d19ac62
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2021 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-base/strings.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <input/Flags.h>
+
+#include <string>
+
+#include "DisplayHardware/Hal.h"
+
+namespace std {
+template <typename T>
+struct hash<android::sp<T>> {
+ size_t operator()(const android::sp<T>& p) { return std::hash<void*>()(p.get()); }
+};
+} // namespace std
+
+namespace android::compositionengine::impl::planner {
+
+using LayerId = int32_t;
+
+// clang-format off
+enum class LayerStateField : uint32_t {
+ Id = 1u << 0,
+ Name = 1u << 1,
+ DisplayFrame = 1u << 2,
+ SourceCrop = 1u << 3,
+ ZOrder = 1u << 4,
+ BufferTransform = 1u << 5,
+ BlendMode = 1u << 6,
+ Alpha = 1u << 7,
+ VisibleRegion = 1u << 8,
+ Dataspace = 1u << 9,
+ ColorTransform = 1u << 10,
+ CompositionType = 1u << 11,
+ SidebandStream = 1u << 12,
+ Buffer = 1u << 13,
+ SolidColor = 1u << 14,
+};
+// clang-format on
+
+std::string to_string(LayerStateField field);
+
+// An abstract interface allows us to iterate over all of the OutputLayerState fields
+// without having to worry about their templated types.
+// See `LayerState::getNonUniqueFields` below.
+class StateInterface {
+public:
+ virtual ~StateInterface() = default;
+
+ virtual Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) = 0;
+
+ virtual size_t getHash(Flags<LayerStateField> skipFields) const = 0;
+
+ virtual LayerStateField getField() const = 0;
+
+ virtual Flags<LayerStateField> getFieldIfDifferent(const StateInterface* other) const = 0;
+
+ virtual bool equals(const StateInterface* other) const = 0;
+
+ virtual std::vector<std::string> toStrings() const = 0;
+};
+
+template <typename T, LayerStateField FIELD>
+class OutputLayerState : public StateInterface {
+public:
+ using ReadFromLayerState = std::function<T(const compositionengine::OutputLayer* layer)>;
+ using ToStrings = std::function<std::vector<std::string>(const T&)>;
+ using Equals = std::function<bool(const T&, const T&)>;
+
+ static ToStrings getDefaultToStrings() {
+ return [](const T& value) {
+ using std::to_string;
+ return std::vector<std::string>{to_string(value)};
+ };
+ }
+
+ static ToStrings getHalToStrings() {
+ return [](const T& value) { return std::vector<std::string>{toString(value)}; };
+ }
+
+ static Equals getDefaultEquals() {
+ return [](const T& lhs, const T& rhs) { return lhs == rhs; };
+ }
+
+ OutputLayerState(ReadFromLayerState reader,
+ ToStrings toStrings = OutputLayerState::getDefaultToStrings(),
+ Equals equals = OutputLayerState::getDefaultEquals())
+ : mReader(reader), mToStrings(toStrings), mEquals(equals) {}
+
+ ~OutputLayerState() override = default;
+
+ // Returns this member's field flag if it was changed
+ Flags<LayerStateField> update(const compositionengine::OutputLayer* layer) override {
+ T newValue = mReader(layer);
+ if (!mEquals(mValue, newValue)) {
+ mValue = newValue;
+ mHash = {};
+ return FIELD;
+ }
+ return {};
+ }
+
+ LayerStateField getField() const override { return FIELD; }
+ const T& get() const { return mValue; }
+
+ size_t getHash(Flags<LayerStateField> skipFields) const override {
+ if (skipFields.test(FIELD)) {
+ return 0;
+ }
+ if (!mHash) {
+ mHash = std::hash<T>{}(mValue);
+ }
+ return *mHash;
+ }
+
+ Flags<LayerStateField> getFieldIfDifferent(const StateInterface* other) const override {
+ if (other->getField() != FIELD) {
+ return {};
+ }
+
+ // The early return ensures that this downcast is sound
+ const OutputLayerState* otherState = static_cast<const OutputLayerState*>(other);
+ return *this != *otherState ? FIELD : Flags<LayerStateField>{};
+ }
+
+ bool equals(const StateInterface* other) const override {
+ if (other->getField() != FIELD) {
+ return false;
+ }
+
+ // The early return ensures that this downcast is sound
+ const OutputLayerState* otherState = static_cast<const OutputLayerState*>(other);
+ return *this == *otherState;
+ }
+
+ std::vector<std::string> toStrings() const override { return mToStrings(mValue); }
+
+ bool operator==(const OutputLayerState& other) const { return mEquals(mValue, other.mValue); }
+ bool operator!=(const OutputLayerState& other) const { return !(*this == other); }
+
+private:
+ const ReadFromLayerState mReader;
+ const ToStrings mToStrings;
+ const Equals mEquals;
+ T mValue = {};
+ mutable std::optional<size_t> mHash = {};
+};
+
+class LayerState {
+public:
+ LayerState(compositionengine::OutputLayer* layer);
+
+ // Returns which fields were updated
+ Flags<LayerStateField> update(compositionengine::OutputLayer*);
+
+ size_t getHash(Flags<LayerStateField> skipFields) const;
+
+ Flags<LayerStateField> getDifferingFields(const LayerState& other,
+ Flags<LayerStateField> skipFields) const;
+
+ compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; }
+ int32_t getId() const { return mId.get(); }
+ const std::string& getName() const { return mName.get(); }
+ Rect getDisplayFrame() const { return mDisplayFrame.get(); }
+ hardware::graphics::composer::hal::Composition getCompositionType() const {
+ return mCompositionType.get();
+ }
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer.get(); }
+
+ void incrementFramesSinceBufferUpdate() { ++mFramesSinceBufferUpdate; }
+ void resetFramesSinceBufferUpdate() { mFramesSinceBufferUpdate = 0; }
+ int64_t getFramesSinceBufferUpdate() const { return mFramesSinceBufferUpdate; }
+
+ void dump(std::string& result) const;
+ std::optional<std::string> compare(const LayerState& other) const;
+
+ // This makes LayerState's private members accessible to the operator
+ friend bool operator==(const LayerState& lhs, const LayerState& rhs);
+ friend bool operator!=(const LayerState& lhs, const LayerState& rhs) { return !(lhs == rhs); }
+
+private:
+ compositionengine::OutputLayer* mOutputLayer = nullptr;
+
+ OutputLayerState<LayerId, LayerStateField::Id> mId{
+ [](const compositionengine::OutputLayer* layer) {
+ return layer->getLayerFE().getSequence();
+ }};
+
+ OutputLayerState<std::string, LayerStateField::Name>
+ mName{[](auto layer) { return layer->getLayerFE().getDebugName(); },
+ [](const std::string& name) { return std::vector<std::string>{name}; }};
+
+ // Output-dependent geometry state
+
+ OutputLayerState<Rect, LayerStateField::DisplayFrame>
+ mDisplayFrame{[](auto layer) { return layer->getState().displayFrame; },
+ [](const Rect& rect) {
+ return std::vector<std::string>{
+ base::StringPrintf("[%d, %d, %d, %d]", rect.left, rect.top,
+ rect.right, rect.bottom)};
+ }};
+
+ OutputLayerState<FloatRect, LayerStateField::SourceCrop>
+ mSourceCrop{[](auto layer) { return layer->getState().sourceCrop; },
+ [](const FloatRect& rect) {
+ return std::vector<std::string>{
+ base::StringPrintf("[%.2f, %.2f, %.2f, %.2f]", rect.left,
+ rect.top, rect.right, rect.bottom)};
+ }};
+
+ OutputLayerState<uint32_t, LayerStateField::ZOrder> mZOrder{
+ [](auto layer) { return layer->getState().z; }};
+
+ using BufferTransformState = OutputLayerState<hardware::graphics::composer::hal::Transform,
+ LayerStateField::BufferTransform>;
+ BufferTransformState mBufferTransform{[](auto layer) {
+ return layer->getState().bufferTransform;
+ },
+ BufferTransformState::getHalToStrings()};
+
+ // Output-independent geometry state
+
+ using BlendModeState = OutputLayerState<hardware::graphics::composer::hal::BlendMode,
+ LayerStateField::BlendMode>;
+ BlendModeState mBlendMode{[](auto layer) {
+ return layer->getLayerFE().getCompositionState()->blendMode;
+ },
+ BlendModeState::getHalToStrings()};
+
+ OutputLayerState<float, LayerStateField::Alpha> mAlpha{
+ [](auto layer) { return layer->getLayerFE().getCompositionState()->alpha; }};
+
+ // TODO(b/180638831): Generic layer metadata
+
+ // Output-dependent per-frame state
+
+ OutputLayerState<Region, LayerStateField::VisibleRegion>
+ mVisibleRegion{[](auto layer) { return layer->getState().visibleRegion; },
+ [](const Region& region) {
+ using namespace std::string_literals;
+ std::string dump;
+ region.dump(dump, "");
+ std::vector<std::string> split = base::Split(dump, "\n"s);
+ split.erase(split.begin()); // Strip the header
+ split.pop_back(); // Strip the last (empty) line
+ for (std::string& line : split) {
+ line.erase(0, 4); // Strip leading padding before each rect
+ }
+ return split;
+ },
+ [](const Region& lhs, const Region& rhs) {
+ return lhs.hasSameRects(rhs);
+ }};
+
+ using DataspaceState = OutputLayerState<ui::Dataspace, LayerStateField::Dataspace>;
+ DataspaceState mDataspace{[](auto layer) { return layer->getState().dataspace; },
+ DataspaceState::getHalToStrings()};
+
+ // TODO(b/180638831): Buffer format
+
+ // Output-independent per-frame state
+
+ OutputLayerState<mat4, LayerStateField::ColorTransform>
+ mColorTransform{[](auto layer) {
+ const auto state = layer->getLayerFE().getCompositionState();
+ return state->colorTransformIsIdentity ? mat4{}
+ : state->colorTransform;
+ },
+ [](const mat4& mat) {
+ using namespace std::string_literals;
+ std::vector<std::string> split =
+ base::Split(std::string(mat.asString().string()), "\n"s);
+ split.pop_back(); // Strip the last (empty) line
+ return split;
+ }};
+
+ // TODO(b/180638831): Surface damage
+
+ using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
+ LayerStateField::CompositionType>;
+ CompositionTypeState
+ mCompositionType{[](auto layer) {
+ return layer->getState().forceClientComposition
+ ? hardware::graphics::composer::hal::Composition::CLIENT
+ : layer->getLayerFE()
+ .getCompositionState()
+ ->compositionType;
+ },
+ CompositionTypeState::getHalToStrings()};
+
+ OutputLayerState<void*, LayerStateField::SidebandStream>
+ mSidebandStream{[](auto layer) {
+ return layer->getLayerFE()
+ .getCompositionState()
+ ->sidebandStream.get();
+ },
+ [](void* p) {
+ return std::vector<std::string>{base::StringPrintf("%p", p)};
+ }};
+
+ OutputLayerState<sp<GraphicBuffer>, LayerStateField::Buffer>
+ mBuffer{[](auto layer) { return layer->getLayerFE().getCompositionState()->buffer; },
+ [](const sp<GraphicBuffer>& buffer) {
+ return std::vector<std::string>{base::StringPrintf("%p", buffer.get())};
+ }};
+
+ int64_t mFramesSinceBufferUpdate = 0;
+
+ OutputLayerState<half4, LayerStateField::SolidColor>
+ mSolidColor{[](auto layer) { return layer->getLayerFE().getCompositionState()->color; },
+ [](const half4& vec) {
+ std::stringstream stream;
+ stream << vec;
+ return std::vector<std::string>{stream.str()};
+ }};
+
+ std::array<StateInterface*, 13> getNonUniqueFields() {
+ std::array<const StateInterface*, 13> constFields =
+ const_cast<const LayerState*>(this)->getNonUniqueFields();
+ std::array<StateInterface*, 13> fields;
+ std::transform(constFields.cbegin(), constFields.cend(), fields.begin(),
+ [](const StateInterface* constField) {
+ return const_cast<StateInterface*>(constField);
+ });
+ return fields;
+ }
+
+ std::array<const StateInterface*, 13> getNonUniqueFields() const {
+ return {
+ &mDisplayFrame, &mSourceCrop, &mZOrder, &mBufferTransform,
+ &mBlendMode, &mAlpha, &mVisibleRegion, &mDataspace,
+ &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer,
+ &mSolidColor,
+ };
+ }
+};
+
+using NonBufferHash = size_t;
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>&);
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
new file mode 100644
index 0000000..e96abb7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2021 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 <compositionengine/Output.h>
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace android {
+
+namespace renderengine {
+class RenderEngine;
+} // namespace renderengine
+
+namespace compositionengine::impl::planner {
+
+// This is the top level class for layer caching. It is responsible for
+// heuristically determining the composition strategy of the current layer stack,
+// and flattens inactive layers into an override buffer so it can be used
+// as a more efficient representation of parts of the layer stack.
+class Planner {
+public:
+ Planner() : mFlattener(mPredictor) {}
+
+ void setDisplaySize(ui::Size);
+
+ // Updates the Planner with the current set of layers before a composition strategy is
+ // determined.
+ // The Planner will call to the Flattener to determine to:
+ // 1. Replace any cached sets with a newly available flattened cached set
+ // 2. Create a new cached set if possible
+ void plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+
+ // Updates the Planner with the current set of layers after a composition strategy is
+ // determined.
+ void reportFinalPlan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
+
+ // The planner will call to the Flattener to render any pending cached set
+ void renderCachedSets(renderengine::RenderEngine&);
+
+ void dump(const Vector<String16>& args, std::string&);
+
+private:
+ void dumpUsage(std::string&) const;
+
+ std::unordered_map<LayerId, LayerState> mPreviousLayers;
+
+ std::vector<const LayerState*> mCurrentLayers;
+
+ Predictor mPredictor;
+ Flattener mFlattener;
+
+ std::optional<Predictor::PredictedPlan> mPredictedPlan;
+ NonBufferHash mFlattenedHash = 0;
+};
+
+} // namespace compositionengine::impl::planner
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
new file mode 100644
index 0000000..422af77
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+namespace android::compositionengine::impl::planner {
+
+class LayerStack {
+public:
+ LayerStack(const std::vector<const LayerState*>& layers) : mLayers(copyLayers(layers)) {}
+
+ struct ApproximateMatch {
+ bool operator==(const ApproximateMatch& other) const {
+ return differingIndex == other.differingIndex &&
+ differingFields == other.differingFields;
+ }
+
+ size_t differingIndex;
+ Flags<LayerStateField> differingFields;
+ };
+
+ std::optional<ApproximateMatch> getApproximateMatch(
+ const std::vector<const LayerState*>& other) const;
+
+ void compare(const LayerStack& other, std::string& result) const {
+ if (mLayers.size() != other.mLayers.size()) {
+ base::StringAppendF(&result, "Cannot compare stacks of different sizes (%zd vs. %zd)\n",
+ mLayers.size(), other.mLayers.size());
+ return;
+ }
+
+ for (size_t l = 0; l < mLayers.size(); ++l) {
+ const auto& thisLayer = mLayers[l];
+ const auto& otherLayer = other.mLayers[l];
+ base::StringAppendF(&result, "\n+ - - - - - - - - - Layer %d [%s]\n", thisLayer.getId(),
+ thisLayer.getName().c_str());
+ auto comparisonOpt = thisLayer.compare(otherLayer);
+ base::StringAppendF(&result,
+ " %s + - - - - - - - - - - - - - - - - - - - - - - - "
+ "- Layer %d [%s]\n",
+ comparisonOpt ? " " : "Identical", otherLayer.getId(),
+ otherLayer.getName().c_str());
+ if (comparisonOpt) {
+ result.append(*comparisonOpt);
+ }
+ }
+ }
+
+ void dump(std::string& result) const {
+ for (const LayerState& layer : mLayers) {
+ base::StringAppendF(&result, "+ - - - - - - - - - Layer %d [%s]\n", layer.getId(),
+ layer.getName().c_str());
+ layer.dump(result);
+ }
+ }
+
+ void dumpLayerNames(std::string& result, const std::string& prefix = " ") const {
+ for (const LayerState& layer : mLayers) {
+ result.append(prefix);
+ result.append(layer.getName());
+ result.append("\n");
+ }
+ }
+
+private:
+ std::vector<const LayerState> copyLayers(const std::vector<const LayerState*>& layers) {
+ std::vector<const LayerState> copiedLayers;
+ copiedLayers.reserve(layers.size());
+ std::transform(layers.cbegin(), layers.cend(), std::back_inserter(copiedLayers),
+ [](const LayerState* layerState) { return *layerState; });
+ return copiedLayers;
+ }
+
+ std::vector<const LayerState> mLayers;
+
+ // TODO(b/180976743): Tune kMaxDifferingFields
+ constexpr static int kMaxDifferingFields = 6;
+};
+
+class Plan {
+public:
+ static std::optional<Plan> fromString(const std::string&);
+
+ void reset() { mLayerTypes.clear(); }
+ void addLayerType(hardware::graphics::composer::hal::Composition type) {
+ mLayerTypes.emplace_back(type);
+ }
+
+ friend std::string to_string(const Plan& plan);
+
+ friend bool operator==(const Plan& lhs, const Plan& rhs) {
+ return lhs.mLayerTypes == rhs.mLayerTypes;
+ }
+ friend bool operator!=(const Plan& lhs, const Plan& rhs) { return !(lhs == rhs); }
+
+private:
+ std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
+};
+
+} // namespace android::compositionengine::impl::planner
+
+namespace std {
+template <>
+struct hash<android::compositionengine::impl::planner::Plan> {
+ size_t operator()(const android::compositionengine::impl::planner::Plan& plan) const {
+ return std::hash<std::string>{}(to_string(plan));
+ }
+};
+} // namespace std
+
+namespace android::compositionengine::impl::planner {
+
+class Prediction {
+public:
+ enum class Type {
+ Exact,
+ Approximate,
+ Total,
+ };
+
+ friend std::string to_string(Type type) {
+ using namespace std::string_literals;
+
+ switch (type) {
+ case Type::Exact:
+ return "Exact";
+ case Type::Approximate:
+ return "Approximate";
+ case Type::Total:
+ return "Total";
+ }
+ }
+
+ Prediction(const std::vector<const LayerState*>& layers, Plan plan)
+ : mExampleLayerStack(layers), mPlan(std::move(plan)) {}
+
+ const LayerStack& getExampleLayerStack() const { return mExampleLayerStack; }
+ const Plan& getPlan() const { return mPlan; }
+
+ size_t getHitCount(Type type) const {
+ if (type == Type::Total) {
+ return getHitCount(Type::Exact) + getHitCount(Type::Approximate);
+ }
+ return getStatsForType(type).hitCount;
+ }
+
+ size_t getMissCount(Type type) const {
+ if (type == Type::Total) {
+ return getMissCount(Type::Exact) + getMissCount(Type::Approximate);
+ }
+ return getStatsForType(type).missCount;
+ }
+
+ void recordHit(Type type) { ++getStatsForType(type).hitCount; }
+
+ void recordMiss(Type type) { ++getStatsForType(type).missCount; }
+
+ void dump(std::string&) const;
+
+private:
+ struct Stats {
+ void dump(std::string& result) const {
+ const size_t totalAttempts = hitCount + missCount;
+ base::StringAppendF(&result, "%.2f%% (%zd/%zd)", 100.0f * hitCount / totalAttempts,
+ hitCount, totalAttempts);
+ }
+
+ size_t hitCount = 0;
+ size_t missCount = 0;
+ };
+
+ const Stats& getStatsForType(Type type) const {
+ return (type == Type::Exact) ? mExactStats : mApproximateStats;
+ }
+
+ Stats& getStatsForType(Type type) {
+ return const_cast<Stats&>(const_cast<const Prediction*>(this)->getStatsForType(type));
+ }
+
+ LayerStack mExampleLayerStack;
+ Plan mPlan;
+
+ Stats mExactStats;
+ Stats mApproximateStats;
+};
+
+class Predictor {
+public:
+ struct PredictedPlan {
+ NonBufferHash hash;
+ Plan plan;
+ Prediction::Type type;
+ };
+
+ std::optional<PredictedPlan> getPredictedPlan(const std::vector<const LayerState*>&,
+ NonBufferHash) const;
+
+ void recordResult(std::optional<PredictedPlan> predictedPlan, NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>&, bool hasSkippedLayers, Plan result);
+
+ void dump(std::string&) const;
+
+ void compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash, std::string&) const;
+ void describeLayerStack(NonBufferHash, std::string&) const;
+ void listSimilarStacks(Plan, std::string&) const;
+
+private:
+ // Retrieves a prediction from either the main prediction list or from the candidate list
+ const Prediction& getPrediction(NonBufferHash) const;
+ Prediction& getPrediction(NonBufferHash);
+
+ std::optional<Plan> getExactMatch(NonBufferHash) const;
+ std::optional<NonBufferHash> getApproximateMatch(
+ const std::vector<const LayerState*>& layers) const;
+
+ void promoteIfCandidate(NonBufferHash);
+ void recordPredictedResult(PredictedPlan, const std::vector<const LayerState*>& layers,
+ Plan result);
+ bool findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result);
+
+ void dumpPredictionsByFrequency(std::string&) const;
+
+ struct PromotionCandidate {
+ PromotionCandidate(NonBufferHash hash, Prediction&& prediction)
+ : hash(hash), prediction(std::move(prediction)) {}
+
+ NonBufferHash hash;
+ Prediction prediction;
+ };
+
+ static constexpr const size_t MAX_CANDIDATES = 4;
+ std::deque<PromotionCandidate> mCandidates;
+ decltype(mCandidates)::const_iterator getCandidateEntryByHash(NonBufferHash hash) const {
+ const auto candidateMatches = [&](const PromotionCandidate& candidate) {
+ return candidate.hash == hash;
+ };
+
+ return std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches);
+ }
+
+ std::unordered_map<NonBufferHash, Prediction> mPredictions;
+ std::unordered_map<Plan, std::vector<NonBufferHash>> mSimilarStacks;
+
+ struct ApproximateStack {
+ ApproximateStack(NonBufferHash hash, LayerStack::ApproximateMatch match)
+ : hash(hash), match(match) {}
+
+ bool operator==(const ApproximateStack& other) const {
+ return hash == other.hash && match == other.match;
+ }
+
+ NonBufferHash hash;
+ LayerStack::ApproximateMatch match;
+ };
+
+ std::vector<ApproximateStack> mApproximateStacks;
+
+ mutable size_t mExactHitCount = 0;
+ mutable size_t mApproximateHitCount = 0;
+ mutable size_t mMissCount = 0;
+};
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 45891a7..dde8999 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -42,6 +42,7 @@
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
+ MOCK_CONST_METHOD0(getSequence, int32_t());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 95db4da..5aa53e5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -45,6 +45,7 @@
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&));
MOCK_CONST_METHOD0(getName, const std::string&());
MOCK_METHOD1(setName, void(const std::string&));
@@ -85,7 +86,9 @@
MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
- MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
+ MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&));
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
@@ -104,6 +107,7 @@
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 81e1fc7..2454ff7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -39,7 +39,7 @@
MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
- MOCK_METHOD1(writeStateToHWC, void(bool));
+ MOCK_METHOD2(writeStateToHWC, void(bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
@@ -49,6 +49,7 @@
MOCK_METHOD0(prepareForDeviceLayerRequests, void());
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
+ MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>());
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3907ac5..dc1aacc 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -27,6 +27,9 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+#include <SurfaceFlingerProperties.sysprop.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -38,6 +41,7 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include <android-base/properties.h>
#include <ui/DebugUtils.h>
#include <ui/HdrCapabilities.h>
#include <utils/Trace.h>
@@ -50,6 +54,18 @@
namespace impl {
+Output::Output() {
+ const bool enableLayerCaching = [] {
+ const bool enable =
+ android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false);
+ return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable);
+ }();
+
+ if (enableLayerCaching) {
+ mPlanner = std::make_unique<planner::Planner>();
+ }
+}
+
namespace {
template <typename T>
@@ -182,6 +198,10 @@
const Rect newOrientedBounds(orientedSize);
state.orientedDisplaySpace.bounds = newOrientedBounds;
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
+
dirtyEntireOutput();
}
@@ -269,6 +289,15 @@
}
}
+void Output::dumpPlannerInfo(const Vector<String16>& args, std::string& out) const {
+ if (!mPlanner) {
+ base::StringAppendF(&out, "Planner is disabled\n");
+ return;
+ }
+ base::StringAppendF(&out, "Planner info for display [%s]\n", mName.c_str());
+ mPlanner->dump(args, out);
+}
+
compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
return mDisplayColorProfile.get();
}
@@ -292,7 +321,11 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
- editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize());
+ const auto size = mRenderSurface->getSize();
+ editState().framebufferSpace.bounds = Rect(size);
+ if (mPlanner) {
+ mPlanner->setDisplaySize(size);
+ }
dirtyEntireOutput();
}
@@ -368,13 +401,16 @@
ALOGV(__FUNCTION__);
updateColorProfile(refreshArgs);
- updateAndWriteCompositionState(refreshArgs);
+ updateCompositionState(refreshArgs);
+ planComposition();
+ writeCompositionState(refreshArgs);
setColorTransform(refreshArgs);
beginFrame();
prepareFrame();
devOptRepaintFlash(refreshArgs);
finishFrame(refreshArgs);
postFramebuffer();
+ renderCachedSets();
}
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
@@ -632,8 +668,7 @@
}
}
-void Output::updateAndWriteCompositionState(
- const compositionengine::CompositionRefreshArgs& refreshArgs) {
+void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -653,9 +688,45 @@
if (mLayerRequestingBackgroundBlur == layer) {
forceClientComposition = false;
}
+ }
+}
- // Send the updated state to the HWC, if appropriate.
- layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+void Output::planComposition() {
+ if (!mPlanner || !getState().isEnabled) {
+ return;
+ }
+
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ mPlanner->plan(getOutputLayersOrderedByZ());
+}
+
+void Output::writeCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ if (!getState().isEnabled) {
+ return;
+ }
+
+ sp<GraphicBuffer> previousOverride = nullptr;
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ bool skipLayer = false;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ if (previousOverride != nullptr &&
+ layer->getState().overrideInfo.buffer == previousOverride) {
+ ALOGV("Skipping redundant buffer");
+ skipLayer = true;
+ }
+ previousOverride = layer->getState().overrideInfo.buffer;
+ }
+
+ // TODO(b/181172795): We now update geometry for all flattened layers. We should update it
+ // only when the geometry actually changes
+ const bool includeGeometry = refreshArgs.updatingGeometryThisFrame ||
+ layer->getState().overrideInfo.buffer != nullptr || skipLayer;
+ layer->writeStateToHWC(includeGeometry, skipLayer);
}
}
@@ -826,6 +897,10 @@
chooseCompositionStrategy();
+ if (mPlanner) {
+ mPlanner->reportFinalPlan(getOutputLayersOrderedByZ());
+ }
+
mRenderSurface->prepareFrame(outputState.usesClientComposition,
outputState.usesDeviceComposition);
}
@@ -1075,10 +1150,16 @@
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
.disableBlurs = disableBlurs};
- std::vector<LayerFE::LayerSettings> results =
- layerFE.prepareClientCompositionList(targetSettings);
- if (realContentIsVisible && !results.empty()) {
- layer->editState().clientCompositionTimestamp = systemTime();
+
+ std::vector<LayerFE::LayerSettings> results;
+ if (layer->getState().overrideInfo.buffer != nullptr) {
+ results = layer->getOverrideCompositionList();
+ ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
+ } else {
+ results = layerFE.prepareClientCompositionList(targetSettings);
+ if (realContentIsVisible && !results.empty()) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ }
}
clientCompositionLayers.insert(clientCompositionLayers.end(),
@@ -1169,6 +1250,12 @@
mReleasedLayers.clear();
}
+void Output::renderCachedSets() {
+ if (mPlanner) {
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine());
+ }
+}
+
void Output::dirtyEntireOutput() {
auto& outputState = editState();
outputState.dirtyRegion.set(outputState.displaySpace.bounds);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0faab6f..54784a2 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,7 +16,6 @@
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/LayerFE.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -313,7 +312,7 @@
}
}
-void OutputLayer::writeStateToHWC(bool includeGeometry) {
+void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -336,7 +335,8 @@
if (includeGeometry) {
writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
- writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+ writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState,
+ skipLayer);
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
@@ -352,23 +352,27 @@
HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
const auto& outputDependentState = getState();
- if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
- getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
- outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
- outputDependentState.displayFrame.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ Rect displayFrame = outputDependentState.displayFrame;
+ FloatRect sourceCrop = outputDependentState.sourceCrop;
+ if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
+ displayFrame = outputDependentState.overrideInfo.displayFrame;
+ sourceCrop = displayFrame.toFloatRect();
}
- if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
- error != hal::Error::NONE) {
+ ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
+ displayFrame.right, displayFrame.bottom);
+
+ if (auto error = hwcLayer->setDisplayFrame(displayFrame); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+ getLayerFE().getDebugName(), displayFrame.left, displayFrame.top, displayFrame.right,
+ displayFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ if (auto error = hwcLayer->setSourceCrop(sourceCrop); error != hal::Error::NONE) {
ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
"%s (%d)",
- getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
- outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
- outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ getLayerFE().getDebugName(), sourceCrop.left, sourceCrop.top, sourceCrop.right,
+ sourceCrop.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
}
if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
@@ -389,7 +393,8 @@
}
void OutputLayer::writeOutputIndependentGeometryStateToHWC(
- HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+ HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
+ bool skipLayer) {
if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
error != hal::Error::NONE) {
ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
@@ -397,10 +402,12 @@
static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+ const float alpha = skipLayer ? 0.0f : outputIndependentState.alpha;
+ ALOGV("Writing alpha %f", alpha);
+
+ if (auto error = hwcLayer->setPlaneAlpha(alpha); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(), alpha,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
for (const auto& [name, entry] : outputIndependentState.metadata) {
@@ -509,19 +516,26 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
+ sp<GraphicBuffer> buffer = outputIndependentState.buffer;
+ sp<Fence> acquireFence = outputIndependentState.acquireFence;
+ if (getState().overrideInfo.buffer != nullptr) {
+ buffer = getState().overrideInfo.buffer;
+ acquireFence = getState().overrideInfo.acquireFence;
+ }
+
+ 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(outputIndependentState.bufferSlot,
- outputIndependentState.buffer, &hwcSlot,
- &hwcBuffer);
+ editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer,
+ &hwcSlot, &hwcBuffer);
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
- outputIndependentState.buffer->handle, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
@@ -652,6 +666,26 @@
sourceCrop.getWidth() != displayFrame.getWidth();
}
+std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+ if (getState().overrideInfo.buffer == nullptr) {
+ return {};
+ }
+
+ LayerFE::LayerSettings settings;
+ settings.geometry = renderengine::Geometry{
+ .boundaries = getState().overrideInfo.displayFrame.toFloatRect(),
+ };
+ settings.bufferId = getState().overrideInfo.buffer->getId();
+ settings.source =
+ renderengine::PixelSource{.buffer = renderengine::Buffer{
+ .buffer = getState().overrideInfo.buffer,
+ .fence = getState().overrideInfo.acquireFence,
+ }};
+ settings.alpha = 1.0f;
+
+ return {static_cast<LayerFE::LayerSettings>(settings)};
+}
+
void OutputLayer::dump(std::string& out) const {
using android::base::StringAppendF;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
new file mode 100644
index 0000000..ab3fe9e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2021 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 "Planner"
+// #define LOG_NDEBUG 0
+
+#include <android-base/properties.h>
+#include <compositionengine/impl/planner/CachedSet.h>
+#include <math/HashCombine.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/RenderEngine.h>
+
+namespace android::compositionengine::impl::planner {
+
+const bool CachedSet::sDebugHighlighLayers =
+ base::GetBoolProperty(std::string("debug.sf.layer_caching_highlight"), false);
+
+std::string durationString(std::chrono::milliseconds duration) {
+ using namespace std::chrono_literals;
+
+ std::string result;
+
+ if (duration >= 1h) {
+ const auto hours = std::chrono::duration_cast<std::chrono::hours>(duration);
+ base::StringAppendF(&result, "%d hr ", static_cast<int>(hours.count()));
+ duration -= hours;
+ }
+ if (duration >= 1min) {
+ const auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
+ base::StringAppendF(&result, "%d min ", static_cast<int>(minutes.count()));
+ duration -= minutes;
+ }
+ base::StringAppendF(&result, "%.3f sec ", duration.count() / 1000.0f);
+
+ return result;
+}
+
+CachedSet::Layer::Layer(const LayerState* state, std::chrono::steady_clock::time_point lastUpdate)
+ : mState(state), mHash(state->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {}
+
+CachedSet::CachedSet(const LayerState* layer, std::chrono::steady_clock::time_point lastUpdate)
+ : mFingerprint(layer->getHash(LayerStateField::Buffer)), mLastUpdate(lastUpdate) {
+ addLayer(layer, lastUpdate);
+}
+
+CachedSet::CachedSet(Layer layer)
+ : mFingerprint(layer.getHash()),
+ mLastUpdate(layer.getLastUpdate()),
+ mBounds(layer.getDisplayFrame()) {
+ mLayers.emplace_back(std::move(layer));
+}
+
+void CachedSet::addLayer(const LayerState* layer,
+ std::chrono::steady_clock::time_point lastUpdate) {
+ mLayers.emplace_back(layer, lastUpdate);
+
+ Region boundingRegion;
+ boundingRegion.orSelf(mBounds);
+ boundingRegion.orSelf(layer->getDisplayFrame());
+ mBounds = boundingRegion.getBounds();
+}
+
+NonBufferHash CachedSet::getNonBufferHash() const {
+ if (mLayers.size() == 1) {
+ return mFingerprint;
+ }
+
+ // TODO(b/181192080): Add all fields which contribute to geometry of override layer (e.g.,
+ // dataspace)
+ size_t hash = 0;
+ android::hashCombineSingle(hash, mBounds);
+ return hash;
+}
+
+size_t CachedSet::getComponentDisplayCost() const {
+ size_t displayCost = 0;
+
+ for (const Layer& layer : mLayers) {
+ displayCost += static_cast<size_t>(layer.getDisplayFrame().width() *
+ layer.getDisplayFrame().height());
+ }
+
+ return displayCost;
+}
+
+size_t CachedSet::getCreationCost() const {
+ if (mLayers.size() == 1) {
+ return 0;
+ }
+
+ // Reads
+ size_t creationCost = getComponentDisplayCost();
+
+ // Write - assumes that the output buffer only gets written once per pixel
+ creationCost += static_cast<size_t>(mBounds.width() * mBounds.height());
+
+ return creationCost;
+}
+
+size_t CachedSet::getDisplayCost() const {
+ return static_cast<size_t>(mBounds.width() * mBounds.height());
+}
+
+bool CachedSet::hasBufferUpdate(std::vector<const LayerState*>::const_iterator layers) const {
+ for (const Layer& layer : mLayers) {
+ if (layer.getFramesSinceBufferUpdate() == 0) {
+ return true;
+ }
+ ++layers;
+ }
+ return false;
+}
+
+bool CachedSet::hasReadyBuffer() const {
+ return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+}
+
+std::vector<CachedSet> CachedSet::decompose() const {
+ std::vector<CachedSet> layers;
+
+ std::transform(mLayers.begin(), mLayers.end(), std::back_inserter(layers),
+ [](Layer layer) { return CachedSet(std::move(layer)); });
+
+ return layers;
+}
+
+void CachedSet::updateAge(std::chrono::steady_clock::time_point now) {
+ LOG_ALWAYS_FATAL_IF(mLayers.size() > 1, "[%s] This should only be called on single-layer sets",
+ __func__);
+
+ if (mLayers[0].getFramesSinceBufferUpdate() == 0) {
+ mLastUpdate = now;
+ mAge = 0;
+ }
+}
+
+void CachedSet::render(renderengine::RenderEngine& renderEngine) {
+ renderengine::DisplaySettings displaySettings{
+ .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
+ .clip = mBounds,
+ };
+
+ Region clearRegion = Region::INVALID_REGION;
+ Rect viewport = mBounds;
+ LayerFE::ClientCompositionTargetSettings targetSettings{
+ .clip = Region(mBounds),
+ .needsFiltering = false,
+ .isSecure = true,
+ .supportsProtectedContent = false,
+ .clearRegion = clearRegion,
+ .viewport = viewport,
+ // TODO(181192086): Propagate the Output's dataspace instead of using UNKNOWN
+ .dataspace = ui::Dataspace::UNKNOWN,
+ .realContentIsVisible = true,
+ .clearContent = false,
+ .disableBlurs = false,
+ };
+
+ std::vector<renderengine::LayerSettings> layerSettings;
+ for (const auto& layer : mLayers) {
+ const auto clientCompositionList =
+ layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+ targetSettings);
+ layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
+ clientCompositionList.cend());
+ }
+
+ std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
+ std::transform(layerSettings.cbegin(), layerSettings.cend(),
+ std::back_inserter(layerSettingsPointers),
+ [](const renderengine::LayerSettings& settings) { return &settings; });
+
+ if (sDebugHighlighLayers) {
+ renderengine::LayerSettings highlight{
+ .geometry =
+ renderengine::Geometry{
+ .boundaries = FloatRect(0.0f, 0.0f,
+ static_cast<float>(mBounds.getWidth()),
+ static_cast<float>(mBounds.getHeight())),
+ },
+ .source =
+ renderengine::PixelSource{
+ .solidColor = half3(0.25f, 0.0f, 0.5f),
+ },
+ .alpha = half(0.05f),
+ };
+
+ layerSettingsPointers.emplace_back(&highlight);
+ }
+
+ const uint64_t usageFlags = GraphicBuffer::USAGE_HW_RENDER | GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> buffer = new GraphicBuffer(static_cast<uint32_t>(mBounds.getWidth()),
+ static_cast<uint32_t>(mBounds.getHeight()),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
+ LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
+ base::unique_fd drawFence;
+ status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
+ base::unique_fd(), &drawFence);
+
+ if (result == NO_ERROR) {
+ mBuffer = buffer;
+ mDrawFence = new Fence(drawFence.release());
+ }
+}
+
+void CachedSet::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastUpdate);
+ base::StringAppendF(&result, " + Fingerprint %016zx, last update %sago, age %zd\n",
+ mFingerprint, durationString(lastUpdate).c_str(), mAge);
+
+ if (mLayers.size() == 1) {
+ base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
+ base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
+ } else {
+ result.append(" Cached set of:");
+ for (const Layer& layer : mLayers) {
+ base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
+ }
+ }
+
+ base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost());
+ base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
new file mode 100644
index 0000000..0c09714
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2021 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 "Planner"
+// #define LOG_NDEBUG 0
+
+#include <compositionengine/impl/planner/Flattener.h>
+#include <compositionengine/impl/planner/LayerState.h>
+#include <compositionengine/impl/planner/Predictor.h>
+
+using time_point = std::chrono::steady_clock::time_point;
+using namespace std::chrono_literals;
+
+namespace android::compositionengine::impl::planner {
+
+NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
+ NonBufferHash hash) {
+ const auto now = std::chrono::steady_clock::now();
+
+ const size_t unflattenedDisplayCost = calculateDisplayCost(layers);
+ mUnflattenedDisplayCost += unflattenedDisplayCost;
+
+ if (mCurrentGeometry != hash) {
+ resetActivities(hash, now);
+ mFlattenedDisplayCost += unflattenedDisplayCost;
+ return hash;
+ }
+
+ ++mInitialLayerCounts[layers.size()];
+
+ if (mergeWithCachedSets(layers, now)) {
+ hash = mLayersHash;
+ }
+
+ ++mFinalLayerCounts[mLayers.size()];
+
+ buildCachedSets(now);
+
+ return hash;
+}
+
+void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+ if (!mNewCachedSet) {
+ return;
+ }
+
+ mNewCachedSet->render(renderEngine);
+}
+
+void Flattener::reset() {
+ resetActivities(0, std::chrono::steady_clock::now());
+
+ mUnflattenedDisplayCost = 0;
+ mFlattenedDisplayCost = 0;
+ mInitialLayerCounts.clear();
+ mFinalLayerCounts.clear();
+ mCachedSetCreationCount = 0;
+ mCachedSetCreationCost = 0;
+ mInvalidatedCachedSetAges.clear();
+}
+
+void Flattener::dump(std::string& result) const {
+ const auto now = std::chrono::steady_clock::now();
+
+ base::StringAppendF(&result, "Flattener state:\n");
+
+ result.append("\n Statistics:\n");
+
+ result.append(" Display cost (in screen-size buffers):\n");
+ const size_t displayArea = static_cast<size_t>(mDisplaySize.width * mDisplaySize.height);
+ base::StringAppendF(&result, " Unflattened: %.2f\n",
+ static_cast<float>(mUnflattenedDisplayCost) / displayArea);
+ base::StringAppendF(&result, " Flattened: %.2f\n",
+ static_cast<float>(mFlattenedDisplayCost) / displayArea);
+
+ const auto compareLayerCounts = [](const std::pair<size_t, size_t>& left,
+ const std::pair<size_t, size_t>& right) {
+ return left.first < right.first;
+ };
+
+ const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(),
+ mInitialLayerCounts.cend(), compareLayerCounts)
+ ->first;
+
+ result.append("\n Initial counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t initial = mInitialLayerCounts.count(count) > 0 ? mInitialLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, initial);
+ }
+
+ result.append("\n Final counts:\n");
+ for (size_t count = 1; count < maxLayerCount; ++count) {
+ size_t final = mFinalLayerCounts.count(count) > 0 ? mFinalLayerCounts.at(count) : 0;
+ base::StringAppendF(&result, " % 2zd: %zd\n", count, final);
+ }
+
+ base::StringAppendF(&result, "\n Cached sets created: %zd\n", mCachedSetCreationCount);
+ base::StringAppendF(&result, " Cost: %.2f\n",
+ static_cast<float>(mCachedSetCreationCost) / displayArea);
+
+ const auto lastUpdate =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - mLastGeometryUpdate);
+ base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry,
+ durationString(lastUpdate).c_str());
+
+ result.append(" Current layers:");
+ for (const CachedSet& layer : mLayers) {
+ result.append("\n");
+ layer.dump(result);
+ }
+}
+
+size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const {
+ Region coveredRegion;
+ size_t displayCost = 0;
+ bool hasClientComposition = false;
+
+ for (const LayerState* layer : layers) {
+ coveredRegion.orSelf(layer->getDisplayFrame());
+
+ // Regardless of composition type, we always have to read each input once
+ displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
+ layer->getDisplayFrame().height());
+
+ hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+ }
+
+ if (hasClientComposition) {
+ // If there is client composition, the client target buffer has to be both written by the
+ // GPU and read by the DPU, so we pay its cost twice
+ displayCost += 2 *
+ static_cast<size_t>(coveredRegion.bounds().width() *
+ coveredRegion.bounds().height());
+ }
+
+ return displayCost;
+}
+
+void Flattener::resetActivities(NonBufferHash hash, time_point now) {
+ ALOGV("[%s]", __func__);
+
+ mCurrentGeometry = hash;
+ mLastGeometryUpdate = now;
+
+ for (const CachedSet& cachedSet : mLayers) {
+ if (cachedSet.getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[cachedSet.getAge()];
+ }
+ }
+
+ mLayers.clear();
+
+ if (mNewCachedSet) {
+ ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
+ mNewCachedSet = std::nullopt;
+ }
+}
+
+void Flattener::updateLayersHash() {
+ size_t hash = 0;
+ for (const auto& layer : mLayers) {
+ android::hashCombineSingleHashed(hash, layer.getNonBufferHash());
+ }
+ mLayersHash = hash;
+}
+
+bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) {
+ std::vector<CachedSet> merged;
+
+ if (mLayers.empty()) {
+ merged.reserve(layers.size());
+ for (const LayerState* layer : layers) {
+ merged.emplace_back(layer, now);
+ mFlattenedDisplayCost += merged.back().getDisplayCost();
+ }
+ mLayers = std::move(merged);
+ return false;
+ }
+
+ ALOGV("[%s] Incoming layers:", __func__);
+ for (const LayerState* layer : layers) {
+ ALOGV("%s", layer->getName().c_str());
+ }
+
+ ALOGV("[%s] Current layers:", __func__);
+ for (const CachedSet& layer : mLayers) {
+ std::string dump;
+ layer.dump(dump);
+ ALOGV("%s", dump.c_str());
+ }
+
+ auto currentLayerIter = mLayers.begin();
+ auto incomingLayerIter = layers.begin();
+ while (incomingLayerIter != layers.end()) {
+ if (mNewCachedSet &&
+ mNewCachedSet->getFingerprint() ==
+ (*incomingLayerIter)->getHash(LayerStateField::Buffer)) {
+ if (mNewCachedSet->hasBufferUpdate(incomingLayerIter)) {
+ ALOGV("[%s] Dropping new cached set", __func__);
+ ++mInvalidatedCachedSetAges[0];
+ mNewCachedSet = std::nullopt;
+ } else if (mNewCachedSet->hasReadyBuffer()) {
+ ALOGV("[%s] Found ready buffer", __func__);
+ size_t skipCount = mNewCachedSet->getLayerCount();
+ while (skipCount != 0) {
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = mNewCachedSet->getBuffer(),
+ .acquireFence = mNewCachedSet->getDrawFence(),
+ .displayFrame = mNewCachedSet->getBounds(),
+ };
+ ++incomingLayerIter;
+ }
+
+ if (currentLayerIter->getLayerCount() > 1) {
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ }
+ ++currentLayerIter;
+
+ skipCount -= layerCount;
+ }
+ merged.emplace_back(std::move(*mNewCachedSet));
+ mNewCachedSet = std::nullopt;
+ continue;
+ }
+ }
+
+ if (!currentLayerIter->hasBufferUpdate(incomingLayerIter)) {
+ currentLayerIter->incrementAge();
+ merged.emplace_back(*currentLayerIter);
+
+ // Skip the incoming layers corresponding to this valid current layer
+ const size_t layerCount = currentLayerIter->getLayerCount();
+ for (size_t i = 0; i < layerCount; ++i) {
+ OutputLayer::CompositionState& state =
+ (*incomingLayerIter)->getOutputLayer()->editState();
+ state.overrideInfo = {
+ .buffer = currentLayerIter->getBuffer(),
+ .acquireFence = currentLayerIter->getDrawFence(),
+ .displayFrame = currentLayerIter->getBounds(),
+ };
+ ++incomingLayerIter;
+ }
+ } else if (currentLayerIter->getLayerCount() > 1) {
+ // Break the current layer into its constituent layers
+ ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
+ for (CachedSet& layer : currentLayerIter->decompose()) {
+ layer.updateAge(now);
+ merged.emplace_back(layer);
+ ++incomingLayerIter;
+ }
+ } else {
+ currentLayerIter->updateAge(now);
+ merged.emplace_back(*currentLayerIter);
+ ++incomingLayerIter;
+ }
+ ++currentLayerIter;
+ }
+
+ for (const CachedSet& layer : merged) {
+ mFlattenedDisplayCost += layer.getDisplayCost();
+ }
+
+ mLayers = std::move(merged);
+ updateLayersHash();
+ return true;
+}
+
+void Flattener::buildCachedSets(time_point now) {
+ struct Run {
+ Run(std::vector<CachedSet>::const_iterator start, size_t length)
+ : start(start), length(length) {}
+
+ std::vector<CachedSet>::const_iterator start;
+ size_t length;
+ };
+
+ if (mLayers.empty()) {
+ ALOGV("[%s] No layers found, returning", __func__);
+ return;
+ }
+
+ std::vector<Run> runs;
+ bool isPartOfRun = false;
+ for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
+ if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) {
+ // Layer is inactive
+ if (isPartOfRun) {
+ runs.back().length += currentSet->getLayerCount();
+ } else {
+ // Runs can't start with a non-buffer layer
+ if (currentSet->getFirstLayer().getBuffer() == nullptr) {
+ ALOGV("[%s] Skipping initial non-buffer layer", __func__);
+ } else {
+ runs.emplace_back(currentSet, currentSet->getLayerCount());
+ isPartOfRun = true;
+ }
+ }
+ } else {
+ // Runs must be at least 2 sets long or there's nothing to combine
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ isPartOfRun = false;
+ }
+ }
+
+ // Check for at least 2 sets one more time in case the set includes the last layer
+ if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) {
+ runs.pop_back();
+ }
+
+ ALOGV("[%s] Found %zu candidate runs", __func__, runs.size());
+
+ if (runs.empty()) {
+ return;
+ }
+
+ mNewCachedSet.emplace(*runs[0].start);
+ mNewCachedSet->setLastUpdate(now);
+ auto currentSet = runs[0].start;
+ while (mNewCachedSet->getLayerCount() < runs[0].length) {
+ ++currentSet;
+ mNewCachedSet->append(*currentSet);
+ }
+
+ // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run
+ mPredictor.getPredictedPlan({}, 0);
+
+ ++mCachedSetCreationCount;
+ mCachedSetCreationCost += mNewCachedSet->getCreationCost();
+ std::string setDump;
+ mNewCachedSet->dump(setDump);
+ ALOGV("[%s] Added new cached set:\n%s", __func__, setDump.c_str());
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
new file mode 100644
index 0000000..7cf4819
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2021 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 <compositionengine/impl/planner/LayerState.h>
+
+namespace android::compositionengine::impl::planner {
+
+LayerState::LayerState(compositionengine::OutputLayer* layer) : mOutputLayer(layer) {
+ update(layer);
+}
+
+Flags<LayerStateField> LayerState::update(compositionengine::OutputLayer* layer) {
+ ALOGE_IF(layer != mOutputLayer, "[%s] Expected mOutputLayer to never change", __func__);
+
+ Flags<LayerStateField> differences;
+
+ // Update the unique fields as well, since we have to set them at least
+ // once from the OutputLayer
+ differences |= mId.update(layer);
+ differences |= mName.update(layer);
+
+ for (StateInterface* field : getNonUniqueFields()) {
+ differences |= field->update(layer);
+ }
+
+ return differences;
+}
+
+size_t LayerState::getHash(
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ size_t hash = 0;
+ for (const StateInterface* field : getNonUniqueFields()) {
+ android::hashCombineSingleHashed(hash, field->getHash(skipFields));
+ }
+
+ return hash;
+}
+
+Flags<LayerStateField> LayerState::getDifferingFields(
+ const LayerState& other,
+ Flags<LayerStateField> skipFields = static_cast<LayerStateField>(0)) const {
+ Flags<LayerStateField> differences;
+ auto myFields = getNonUniqueFields();
+ auto otherFields = other.getNonUniqueFields();
+ for (size_t i = 0; i < myFields.size(); ++i) {
+ if (skipFields.test(myFields[i]->getField())) {
+ continue;
+ }
+
+ differences |= myFields[i]->getFieldIfDifferent(otherFields[i]);
+ }
+
+ return differences;
+}
+
+void LayerState::dump(std::string& result) const {
+ for (const StateInterface* field : getNonUniqueFields()) {
+ if (auto viewOpt = flag_name(field->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ bool first = true;
+ for (const std::string& line : field->toStrings()) {
+ base::StringAppendF(&result, "%s%s\n", first ? "" : " ",
+ line.c_str());
+ first = false;
+ }
+ }
+ result.append("\n");
+}
+
+std::optional<std::string> LayerState::compare(const LayerState& other) const {
+ std::string result;
+
+ const auto& thisFields = getNonUniqueFields();
+ const auto& otherFields = other.getNonUniqueFields();
+ for (size_t f = 0; f < thisFields.size(); ++f) {
+ const auto& thisField = thisFields[f];
+ const auto& otherField = otherFields[f];
+ // Skip comparing buffers
+ if (thisField->getField() == LayerStateField::Buffer) {
+ continue;
+ }
+
+ if (thisField->equals(otherField)) {
+ continue;
+ }
+
+ if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
+ base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
+ } else {
+ result.append("<UNKNOWN FIELD>:\n");
+ }
+
+ const auto& thisStrings = thisField->toStrings();
+ const auto& otherStrings = otherField->toStrings();
+ bool first = true;
+ for (size_t line = 0; line < std::max(thisStrings.size(), otherStrings.size()); ++line) {
+ if (!first) {
+ result.append(" ");
+ }
+ first = false;
+
+ if (line < thisStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", thisStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+
+ if (line < otherStrings.size()) {
+ base::StringAppendF(&result, "%-48.48s", otherStrings[line].c_str());
+ } else {
+ result.append(" ");
+ }
+ result.append("\n");
+ }
+ }
+
+ return result.empty() ? std::nullopt : std::make_optional(result);
+}
+
+bool operator==(const LayerState& lhs, const LayerState& rhs) {
+ return lhs.mId == rhs.mId && lhs.mName == rhs.mName && lhs.mDisplayFrame == rhs.mDisplayFrame &&
+ lhs.mSourceCrop == rhs.mSourceCrop && lhs.mZOrder == rhs.mZOrder &&
+ lhs.mBufferTransform == rhs.mBufferTransform && lhs.mBlendMode == rhs.mBlendMode &&
+ lhs.mAlpha == rhs.mAlpha && lhs.mVisibleRegion == rhs.mVisibleRegion &&
+ lhs.mDataspace == rhs.mDataspace && lhs.mColorTransform == rhs.mColorTransform &&
+ lhs.mCompositionType == rhs.mCompositionType &&
+ lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
+ (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+ lhs.mSolidColor == rhs.mSolidColor);
+}
+
+NonBufferHash getNonBufferHash(const std::vector<const LayerState*>& layers) {
+ size_t hash = 0;
+ for (const auto layer : layers) {
+ android::hashCombineSingleHashed(hash, layer->getHash(LayerStateField::Buffer));
+ }
+
+ return hash;
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
new file mode 100644
index 0000000..52efff5
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2021 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
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/impl/planner/Planner.h>
+
+namespace android::compositionengine::impl::planner {
+
+void Planner::setDisplaySize(ui::Size size) {
+ mFlattener.setDisplaySize(size);
+}
+
+void Planner::plan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ std::unordered_set<LayerId> removedLayers;
+ removedLayers.reserve(mPreviousLayers.size());
+
+ std::transform(mPreviousLayers.begin(), mPreviousLayers.end(),
+ std::inserter(removedLayers, removedLayers.begin()),
+ [](const auto& layer) { return layer.first; });
+
+ std::vector<LayerId> currentLayerIds;
+ for (auto layer : layers) {
+ LayerId id = layer->getLayerFE().getSequence();
+ if (const auto layerEntry = mPreviousLayers.find(id); layerEntry != mPreviousLayers.end()) {
+ // Track changes from previous info
+ LayerState& state = layerEntry->second;
+ Flags<LayerStateField> differences = state.update(layer);
+ if (differences.get() == 0) {
+ state.incrementFramesSinceBufferUpdate();
+ } else {
+ ALOGV("Layer %s changed: %s", state.getName().c_str(),
+ differences.string().c_str());
+
+ if (differences.test(LayerStateField::Buffer)) {
+ state.resetFramesSinceBufferUpdate();
+ } else {
+ state.incrementFramesSinceBufferUpdate();
+ }
+ }
+ } else {
+ LayerState state(layer);
+ ALOGV("Added layer %s", state.getName().c_str());
+ mPreviousLayers.emplace(std::make_pair(id, std::move(state)));
+ }
+
+ currentLayerIds.emplace_back(id);
+
+ if (const auto found = removedLayers.find(id); found != removedLayers.end()) {
+ removedLayers.erase(found);
+ }
+ }
+
+ for (LayerId removedLayer : removedLayers) {
+ if (const auto layerEntry = mPreviousLayers.find(removedLayer);
+ layerEntry != mPreviousLayers.end()) {
+ const auto& [id, state] = *layerEntry;
+ ALOGV("Removed layer %s", state.getName().c_str());
+ mPreviousLayers.erase(removedLayer);
+ }
+ }
+
+ mCurrentLayers.clear();
+ mCurrentLayers.reserve(currentLayerIds.size());
+ std::transform(currentLayerIds.cbegin(), currentLayerIds.cend(),
+ std::back_inserter(mCurrentLayers), [this](LayerId id) {
+ LayerState* state = &mPreviousLayers.at(id);
+ state->getOutputLayer()->editState().overrideInfo = {};
+ return state;
+ });
+
+ const NonBufferHash hash = getNonBufferHash(mCurrentLayers);
+ mFlattenedHash = mFlattener.flattenLayers(mCurrentLayers, hash);
+ const bool layersWereFlattened = hash != mFlattenedHash;
+ ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash);
+
+ mPredictedPlan =
+ mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>()
+ : mCurrentLayers,
+ mFlattenedHash);
+ if (mPredictedPlan) {
+ ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str());
+ } else {
+ ALOGV("[%s] No prediction found\n", __func__);
+ }
+}
+
+void Planner::reportFinalPlan(
+ compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) {
+ Plan finalPlan;
+ const GraphicBuffer* currentOverrideBuffer = nullptr;
+ bool hasSkippedLayers = false;
+ for (auto layer : layers) {
+ const GraphicBuffer* overrideBuffer = layer->getState().overrideInfo.buffer.get();
+ if (overrideBuffer != nullptr && overrideBuffer == currentOverrideBuffer) {
+ // Skip this layer since it is part of a previous cached set
+ hasSkippedLayers = true;
+ continue;
+ }
+
+ currentOverrideBuffer = overrideBuffer;
+
+ const bool forcedOrRequestedClient =
+ layer->getState().forceClientComposition || layer->requiresClientComposition();
+
+ finalPlan.addLayerType(
+ forcedOrRequestedClient
+ ? hardware::graphics::composer::hal::Composition::CLIENT
+ : layer->getLayerFE().getCompositionState()->compositionType);
+ }
+
+ mPredictor.recordResult(mPredictedPlan, mFlattenedHash, mCurrentLayers, hasSkippedLayers,
+ finalPlan);
+}
+
+void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine) {
+ mFlattener.renderCachedSets(renderEngine);
+}
+
+void Planner::dump(const Vector<String16>& args, std::string& result) {
+ if (args.size() > 1) {
+ const String8 command(args[1]);
+ if (command == "--compare" || command == "-c") {
+ if (args.size() < 4) {
+ base::StringAppendF(&result,
+ "Expected two layer stack hashes, e.g. '--planner %s "
+ "<left_hash> <right_hash>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 4) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <left_hash> "
+ "<right_hash>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 leftHashString(args[2]);
+ size_t leftHash = 0;
+ int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ leftHashString.string());
+ return;
+ }
+
+ const String8 rightHashString(args[3]);
+ size_t rightHash = 0;
+ fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ rightHashString.string());
+ return;
+ }
+
+ mPredictor.compareLayerStacks(leftHash, rightHash, result);
+ } else if (command == "--describe" || command == "-d") {
+ if (args.size() < 3) {
+ base::StringAppendF(&result,
+ "Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 3) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <hash>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 hashString(args[2]);
+ size_t hash = 0;
+ const int fieldsRead = sscanf(hashString.string(), "%zx", &hash);
+ if (fieldsRead != 1) {
+ base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
+ hashString.string());
+ return;
+ }
+
+ mPredictor.describeLayerStack(hash, result);
+ } else if (command == "--help" || command == "-h") {
+ dumpUsage(result);
+ } else if (command == "--similar" || command == "-s") {
+ if (args.size() < 3) {
+ base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
+ command.string());
+ return;
+ }
+ if (args.size() > 3) {
+ base::StringAppendF(&result,
+ "Too many arguments found, expected '--planner %s <plan>'\n",
+ command.string());
+ return;
+ }
+
+ const String8 planString(args[2]);
+ std::optional<Plan> plan = Plan::fromString(std::string(planString.string()));
+ if (!plan) {
+ base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string());
+ return;
+ }
+
+ mPredictor.listSimilarStacks(*plan, result);
+ } else {
+ base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
+ dumpUsage(result);
+ }
+ return;
+ }
+
+ // If there are no specific commands, dump the usual state
+
+ mFlattener.dump(result);
+ result.append("\n");
+
+ mPredictor.dump(result);
+}
+
+void Planner::dumpUsage(std::string& result) const {
+ result.append("Planner command line interface usage\n");
+ result.append(" dumpsys SurfaceFlinger --planner <command> [arguments]\n\n");
+
+ result.append("If run without a command, dumps current Planner state\n\n");
+
+ result.append("Commands:\n");
+
+ result.append("[--compare|-c] <left_hash> <right_hash>\n");
+ result.append(" Compares the predictions <left_hash> and <right_hash> by showing differences"
+ " in their example layer stacks\n");
+
+ result.append("[--describe|-d] <hash>\n");
+ result.append(" Prints the example layer stack and prediction statistics for <hash>\n");
+
+ result.append("[--help|-h]\n");
+ result.append(" Shows this message\n");
+
+ result.append("[--similar|-s] <plan>\n");
+ result.append(" Prints the example layer names for similar stacks matching <plan>\n");
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
new file mode 100644
index 0000000..ba5e64d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2021 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
+
+#undef LOG_TAG
+#define LOG_TAG "Planner"
+
+#include <compositionengine/impl/planner/Predictor.h>
+
+namespace android::compositionengine::impl::planner {
+
+std::optional<LayerStack::ApproximateMatch> LayerStack::getApproximateMatch(
+ const std::vector<const LayerState*>& other) const {
+ // Differing numbers of layers are never an approximate match
+ if (mLayers.size() != other.size()) {
+ return std::nullopt;
+ }
+
+ std::optional<ApproximateMatch> approximateMatch = {};
+ for (size_t i = 0; i < mLayers.size(); ++i) {
+ // Skip identical layers
+ if (mLayers[i].getHash(LayerStateField::Buffer) ==
+ other[i]->getHash(LayerStateField::Buffer)) {
+ continue;
+ }
+
+ // Skip layers where both are client-composited, since that doesn't change the
+ // composition plan
+ if (mLayers[i].getCompositionType() == hal::Composition::CLIENT &&
+ other[i]->getCompositionType() == hal::Composition::CLIENT) {
+ continue;
+ }
+
+ // If layers differ in composition type, their stacks are too different
+ if (mLayers[i].getCompositionType() != other[i]->getCompositionType()) {
+ return std::nullopt;
+ }
+
+ // If layers are not identical, but we already have a prior approximate match,
+ // the LayerStacks differ by too much, so return nothing
+ if (approximateMatch) {
+ return std::nullopt;
+ }
+
+ Flags<LayerStateField> differingFields =
+ mLayers[i].getDifferingFields(*other[i], LayerStateField::Buffer);
+
+ // If we don't find an approximate match on this layer, then the LayerStacks differ
+ // by too much, so return nothing
+ const int differingFieldCount = __builtin_popcount(differingFields.get());
+ if (differingFieldCount <= kMaxDifferingFields) {
+ approximateMatch = ApproximateMatch{
+ .differingIndex = i,
+ .differingFields = differingFields,
+ };
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ // If we make it through the layer-by-layer comparison without an approximate match,
+ // it means that all layers were either identical or had client-composited layers in common,
+ // which don't affect the composition strategy, so return a successful result with
+ // no differences.
+ return ApproximateMatch{
+ .differingIndex = 0,
+ .differingFields = {},
+ };
+}
+
+std::optional<Plan> Plan::fromString(const std::string& string) {
+ Plan plan;
+ for (char c : string) {
+ switch (c) {
+ case 'C':
+ plan.addLayerType(hal::Composition::CLIENT);
+ continue;
+ case 'U':
+ plan.addLayerType(hal::Composition::CURSOR);
+ continue;
+ case 'D':
+ plan.addLayerType(hal::Composition::DEVICE);
+ continue;
+ case 'I':
+ plan.addLayerType(hal::Composition::INVALID);
+ continue;
+ case 'B':
+ plan.addLayerType(hal::Composition::SIDEBAND);
+ continue;
+ case 'S':
+ plan.addLayerType(hal::Composition::SOLID_COLOR);
+ continue;
+ default:
+ return std::nullopt;
+ }
+ }
+ return plan;
+}
+
+std::string to_string(const Plan& plan) {
+ std::string result;
+ for (auto type : plan.mLayerTypes) {
+ switch (type) {
+ case hal::Composition::CLIENT:
+ result.append("C");
+ break;
+ case hal::Composition::CURSOR:
+ result.append("U");
+ break;
+ case hal::Composition::DEVICE:
+ result.append("D");
+ break;
+ case hal::Composition::INVALID:
+ result.append("I");
+ break;
+ case hal::Composition::SIDEBAND:
+ result.append("B");
+ break;
+ case hal::Composition::SOLID_COLOR:
+ result.append("S");
+ break;
+ }
+ }
+ return result;
+}
+
+void Prediction::dump(std::string& result) const {
+ result.append(to_string(mPlan));
+ result.append(" [Exact ");
+ mExactStats.dump(result);
+ result.append("] [Approximate ");
+ mApproximateStats.dump(result);
+ result.append("]");
+}
+
+std::optional<Predictor::PredictedPlan> Predictor::getPredictedPlan(
+ const std::vector<const LayerState*>& layers, NonBufferHash hash) const {
+ // First check for an exact match
+ if (std::optional<Plan> exactMatch = getExactMatch(hash); exactMatch) {
+ ALOGV("[%s] Found an exact match for %zx", __func__, hash);
+ return PredictedPlan{.hash = hash, .plan = *exactMatch, .type = Prediction::Type::Exact};
+ }
+
+ // If only a hash was passed in for a layer stack with a cached set, don't perform
+ // approximate matches and return early
+ if (layers.empty()) {
+ ALOGV("[%s] Only hash was passed, but no exact match was found", __func__);
+ return std::nullopt;
+ }
+
+ // Then check for approximate matches
+ if (std::optional<NonBufferHash> approximateMatch = getApproximateMatch(layers);
+ approximateMatch) {
+ ALOGV("[%s] Found an approximate match for %zx", __func__, *approximateMatch);
+ const Prediction& prediction = getPrediction(*approximateMatch);
+ return PredictedPlan{.hash = *approximateMatch,
+ .plan = prediction.getPlan(),
+ .type = Prediction::Type::Approximate};
+ }
+
+ return std::nullopt;
+}
+
+void Predictor::recordResult(std::optional<PredictedPlan> predictedPlan,
+ NonBufferHash flattenedHash,
+ const std::vector<const LayerState*>& layers, bool hasSkippedLayers,
+ Plan result) {
+ if (predictedPlan) {
+ recordPredictedResult(*predictedPlan, layers, std::move(result));
+ return;
+ }
+
+ ++mMissCount;
+
+ if (!hasSkippedLayers && findSimilarPrediction(layers, result)) {
+ return;
+ }
+
+ ALOGV("[%s] Adding novel candidate %zx", __func__, flattenedHash);
+ mCandidates.emplace_front(flattenedHash, Prediction(layers, result));
+ if (mCandidates.size() > MAX_CANDIDATES) {
+ mCandidates.pop_back();
+ }
+}
+
+void Predictor::dump(std::string& result) const {
+ result.append("Predictor state:\n");
+
+ const size_t hitCount = mExactHitCount + mApproximateHitCount;
+ const size_t totalAttempts = hitCount + mMissCount;
+ base::StringAppendF(&result, "Global non-skipped hit rate: %.2f%% (%zd/%zd)\n",
+ 100.0f * hitCount / totalAttempts, hitCount, totalAttempts);
+ base::StringAppendF(&result, " Exact hits: %zd\n", mExactHitCount);
+ base::StringAppendF(&result, " Approximate hits: %zd\n", mApproximateHitCount);
+ base::StringAppendF(&result, " Misses: %zd\n\n", mMissCount);
+
+ dumpPredictionsByFrequency(result);
+}
+
+void Predictor::compareLayerStacks(NonBufferHash leftHash, NonBufferHash rightHash,
+ std::string& result) const {
+ const auto& [leftPredictionEntry, rightPredictionEntry] =
+ std::make_tuple(mPredictions.find(leftHash), mPredictions.find(rightHash));
+ if (leftPredictionEntry == mPredictions.end()) {
+ base::StringAppendF(&result, "No prediction found for %zx\n", leftHash);
+ return;
+ }
+ if (rightPredictionEntry == mPredictions.end()) {
+ base::StringAppendF(&result, "No prediction found for %zx\n", rightHash);
+ return;
+ }
+
+ base::StringAppendF(&result,
+ "Comparing %-16zx %-16zx\n",
+ leftHash, rightHash);
+
+ const auto& [leftPrediction, rightPrediction] =
+ std::make_tuple(leftPredictionEntry->second, rightPredictionEntry->second);
+ const auto& [leftStack, rightStack] = std::make_tuple(leftPrediction.getExampleLayerStack(),
+ rightPrediction.getExampleLayerStack());
+ leftStack.compare(rightStack, result);
+}
+
+void Predictor::describeLayerStack(NonBufferHash hash, std::string& result) const {
+ base::StringAppendF(&result, "Describing %zx:\n\n", hash);
+
+ if (const auto predictionsEntry = mPredictions.find(hash);
+ predictionsEntry != mPredictions.cend()) {
+ const auto& [hash, prediction] = *predictionsEntry;
+
+ prediction.getExampleLayerStack().dump(result);
+
+ result.append("Prediction: ");
+ prediction.dump(result);
+ result.append("\n");
+ } else {
+ result.append("No predictions found\n");
+ }
+}
+
+void Predictor::listSimilarStacks(Plan plan, std::string& result) const {
+ base::StringAppendF(&result, "Similar stacks for plan %s:\n", to_string(plan).c_str());
+
+ if (const auto similarStacksEntry = mSimilarStacks.find(plan);
+ similarStacksEntry != mSimilarStacks.end()) {
+ const auto& [_, similarStacks] = *similarStacksEntry;
+ for (NonBufferHash hash : similarStacks) {
+ base::StringAppendF(&result, "\nPrediction hash %zx:\n", hash);
+ const Prediction& prediction = mPredictions.at(hash);
+ prediction.getExampleLayerStack().dumpLayerNames(result);
+ }
+ } else {
+ result.append("No similar stacks found\n");
+ }
+}
+
+const Prediction& Predictor::getPrediction(NonBufferHash hash) const {
+ if (const auto predictionEntry = mPredictions.find(hash);
+ predictionEntry != mPredictions.end()) {
+ const auto& [_, prediction] = *predictionEntry;
+ return prediction;
+ } else {
+ const auto candidateEntry = getCandidateEntryByHash(hash);
+ ALOGE_IF(candidateEntry == mCandidates.cend(),
+ "Hash should have been found in either predictions or candidates");
+ const auto& [_, prediction] = *candidateEntry;
+ return prediction;
+ }
+}
+
+Prediction& Predictor::getPrediction(NonBufferHash hash) {
+ return const_cast<Prediction&>(const_cast<const Predictor*>(this)->getPrediction(hash));
+}
+
+std::optional<Plan> Predictor::getExactMatch(NonBufferHash hash) const {
+ const Prediction* match = nullptr;
+ if (const auto predictionEntry = mPredictions.find(hash);
+ predictionEntry != mPredictions.end()) {
+ const auto& [hash, prediction] = *predictionEntry;
+ match = &prediction;
+ } else if (const auto candidateEntry = getCandidateEntryByHash(hash);
+ candidateEntry != mCandidates.cend()) {
+ match = &(candidateEntry->prediction);
+ }
+
+ if (match == nullptr) {
+ return std::nullopt;
+ }
+
+ if (match->getMissCount(Prediction::Type::Exact) != 0) {
+ ALOGV("[%s] Skipping exact match for %zx because of prior miss", __func__, hash);
+ return std::nullopt;
+ }
+
+ return match->getPlan();
+}
+
+std::optional<NonBufferHash> Predictor::getApproximateMatch(
+ const std::vector<const LayerState*>& layers) const {
+ const auto approximateStackMatches = [&](const ApproximateStack& approximateStack) {
+ const auto& exampleStack = mPredictions.at(approximateStack.hash).getExampleLayerStack();
+ if (const auto approximateMatchOpt = exampleStack.getApproximateMatch(layers);
+ approximateMatchOpt) {
+ return *approximateMatchOpt == approximateStack.match;
+ }
+ return false;
+ };
+
+ const auto candidateMatches = [&](const PromotionCandidate& candidate) {
+ ALOGV("[getApproximateMatch] checking against %zx", candidate.hash);
+ return candidate.prediction.getExampleLayerStack().getApproximateMatch(layers) !=
+ std::nullopt;
+ };
+
+ const Prediction* match = nullptr;
+ NonBufferHash hash;
+ if (const auto approximateStackIter =
+ std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(),
+ approximateStackMatches);
+ approximateStackIter != mApproximateStacks.cend()) {
+ match = &mPredictions.at(approximateStackIter->hash);
+ hash = approximateStackIter->hash;
+ } else if (const auto candidateEntry =
+ std::find_if(mCandidates.cbegin(), mCandidates.cend(), candidateMatches);
+ candidateEntry != mCandidates.cend()) {
+ match = &(candidateEntry->prediction);
+ hash = candidateEntry->hash;
+ }
+
+ if (match == nullptr) {
+ return std::nullopt;
+ }
+
+ if (match->getMissCount(Prediction::Type::Approximate) != 0) {
+ ALOGV("[%s] Skipping approximate match for %zx because of prior miss", __func__, hash);
+ return std::nullopt;
+ }
+
+ return hash;
+}
+
+void Predictor::promoteIfCandidate(NonBufferHash predictionHash) {
+ // Return if the candidate has already been promoted
+ if (mPredictions.count(predictionHash) != 0) {
+ return;
+ }
+
+ ALOGV("[%s] Promoting %zx from candidate to prediction", __func__, predictionHash);
+
+ auto candidateEntry = getCandidateEntryByHash(predictionHash);
+ ALOGE_IF(candidateEntry == mCandidates.end(), "Expected to find candidate");
+
+ mSimilarStacks[candidateEntry->prediction.getPlan()].push_back(predictionHash);
+ mPredictions.emplace(predictionHash, std::move(candidateEntry->prediction));
+ mCandidates.erase(candidateEntry);
+}
+
+void Predictor::recordPredictedResult(PredictedPlan predictedPlan,
+ const std::vector<const LayerState*>& layers, Plan result) {
+ Prediction& prediction = getPrediction(predictedPlan.hash);
+ if (prediction.getPlan() != result) {
+ ALOGV("[%s] %s prediction missed, expected %s, found %s", __func__,
+ to_string(predictedPlan.type).c_str(), to_string(prediction.getPlan()).c_str(),
+ to_string(result).c_str());
+ prediction.recordMiss(predictedPlan.type);
+ ++mMissCount;
+ return;
+ }
+
+ switch (predictedPlan.type) {
+ case Prediction::Type::Approximate:
+ ++mApproximateHitCount;
+ break;
+ case Prediction::Type::Exact:
+ ++mExactHitCount;
+ break;
+ default:
+ break;
+ }
+
+ ALOGV("[%s] %s prediction hit", __func__, to_string(predictedPlan.type).c_str());
+ ALOGV("[%s] Plan: %s", __func__, to_string(result).c_str());
+ prediction.recordHit(predictedPlan.type);
+
+ const auto stackMatchesHash = [hash = predictedPlan.hash](const ApproximateStack& stack) {
+ return stack.hash == hash;
+ };
+
+ if (predictedPlan.type == Prediction::Type::Approximate) {
+ // If this approximate match is not already in the list of approximate stacks, add it
+ if (std::find_if(mApproximateStacks.cbegin(), mApproximateStacks.cend(),
+ stackMatchesHash) == mApproximateStacks.cend()) {
+ ALOGV("[%s] Adding approximate match to list", __func__);
+ const auto approximateMatchOpt =
+ prediction.getExampleLayerStack().getApproximateMatch(layers);
+ ALOGE_IF(!approximateMatchOpt, "Expected an approximate match");
+ mApproximateStacks.emplace_back(predictedPlan.hash, *approximateMatchOpt);
+ }
+ }
+
+ promoteIfCandidate(predictedPlan.hash);
+}
+
+bool Predictor::findSimilarPrediction(const std::vector<const LayerState*>& layers, Plan result) {
+ const auto stacksEntry = mSimilarStacks.find(result);
+ if (stacksEntry == mSimilarStacks.end()) {
+ return false;
+ }
+
+ std::optional<ApproximateStack> bestMatch;
+ const auto& [plan, similarStacks] = *stacksEntry;
+ for (NonBufferHash hash : similarStacks) {
+ const Prediction& prediction = mPredictions.at(hash);
+ auto approximateMatch = prediction.getExampleLayerStack().getApproximateMatch(layers);
+ if (!approximateMatch) {
+ continue;
+ }
+
+ const int differingFieldCount = __builtin_popcount(approximateMatch->differingFields.get());
+ if (!bestMatch ||
+ differingFieldCount < __builtin_popcount(bestMatch->match.differingFields.get())) {
+ bestMatch = {hash, *approximateMatch};
+ }
+ }
+
+ if (!bestMatch) {
+ return false;
+ }
+
+ ALOGV("[%s] Adding %zx to approximate stacks", __func__, bestMatch->hash);
+
+ mApproximateStacks.emplace_back(*bestMatch);
+ return true;
+}
+
+void Predictor::dumpPredictionsByFrequency(std::string& result) const {
+ struct HashFrequency {
+ HashFrequency(NonBufferHash hash, size_t totalAttempts)
+ : hash(hash), totalAttempts(totalAttempts) {}
+
+ NonBufferHash hash;
+ size_t totalAttempts;
+ };
+
+ std::vector<HashFrequency> hashFrequencies;
+ for (const auto& [hash, prediction] : mPredictions) {
+ hashFrequencies.emplace_back(hash,
+ prediction.getHitCount(Prediction::Type::Total) +
+ prediction.getMissCount(Prediction::Type::Total));
+ }
+
+ std::sort(hashFrequencies.begin(), hashFrequencies.end(),
+ [](const HashFrequency& lhs, const HashFrequency& rhs) {
+ return lhs.totalAttempts > rhs.totalAttempts;
+ });
+
+ result.append("Predictions:\n");
+ for (const auto& [hash, totalAttempts] : hashFrequencies) {
+ base::StringAppendF(&result, " %016zx ", hash);
+ mPredictions.at(hash).dump(result);
+ result.append("\n");
+ }
+}
+
+} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dcfc162..9dd199d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -840,19 +840,19 @@
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -861,7 +861,7 @@
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -891,7 +891,7 @@
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
expectSetColorCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -901,7 +901,7 @@
expectSetSidebandHandleCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -911,7 +911,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -921,7 +921,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -934,7 +934,7 @@
expectSetColorCall();
expectNoSetCompositionTypeCall();
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -944,7 +944,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -956,7 +956,7 @@
expectSetColorCall();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -969,7 +969,7 @@
expectGenericLayerMetadataCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(true);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -980,7 +980,7 @@
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
- mOutputLayer.writeStateToHWC(false);
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c4ae3a7..3059beb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -96,6 +96,8 @@
EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("InjectedLayer"));
}
mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>};
@@ -111,6 +113,8 @@
EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+ EXPECT_CALL(*layerFE, getSequence()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*layerFE, getDebugName()).WillRepeatedly(Return("NonInjectedLayer"));
}
mock::OutputLayer outputLayer;
@@ -751,7 +755,9 @@
mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
@@ -766,7 +772,9 @@
injectOutputLayer(layer3);
CompositionRefreshArgs args;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
@@ -775,11 +783,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -791,7 +802,9 @@
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
args.internalDisplayRotationFlags = ui::Transform::ROT_180;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
@@ -800,11 +813,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -815,7 +831,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = true;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
@@ -824,11 +842,14 @@
InjectedLayer layer3;
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -839,7 +860,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = true;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
/*
@@ -877,6 +900,9 @@
mOutput.editState().usesDeviceComposition = true;
EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
+ if (mOutput.plannerEnabled()) {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ }
EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
mOutput.prepareFrame();
@@ -1621,14 +1647,17 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD1(updateAndWriteCompositionState,
+ MOCK_METHOD1(updateCompositionState,
void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(renderCachedSets, void());
};
StrictMock<OutputPartialMock> mOutput;
@@ -1639,13 +1668,16 @@
InSequence seq;
EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
- EXPECT_CALL(mOutput, updateAndWriteCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, updateCompositionState(Ref(args)));
+ EXPECT_CALL(mOutput, planComposition());
+ EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
EXPECT_CALL(mOutput, finishFrame(Ref(args)));
EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, renderCachedSets());
mOutput.present(args);
}
@@ -3461,7 +3493,8 @@
mOutput.editState().isEnabled = true;
EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(mLayer.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
@@ -3476,7 +3509,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
mRefreshArgs.blursAreExpensive = true;
- mOutput.updateAndWriteCompositionState(mRefreshArgs);
+ mOutput.updateCompositionState(mRefreshArgs);
+ mOutput.planComposition();
+ mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
@@ -3484,7 +3519,9 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
mRefreshArgs.blursAreExpensive = false;
- mOutput.updateAndWriteCompositionState(mRefreshArgs);
+ mOutput.updateCompositionState(mRefreshArgs);
+ mOutput.planComposition();
+ mOutput.writeCompositionState(mRefreshArgs);
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
@@ -4038,11 +4075,14 @@
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4055,7 +4095,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
@@ -4065,11 +4107,14 @@
// Layer requesting blur, or below, should request client composition.
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer1.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer,
+ writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);
@@ -4083,7 +4128,9 @@
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = false;
- mOutput->updateAndWriteCompositionState(args);
+ mOutput->updateCompositionState(args);
+ mOutput->planComposition();
+ mOutput->writeCompositionState(args);
}
TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
new file mode 100644
index 0000000..c7dbf88
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021 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 "FpsReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FpsReporter.h"
+
+#include "Layer.h"
+
+namespace android {
+
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline)
+ : mFrameTimeline(frameTimeline) {}
+
+void FpsReporter::dispatchLayerFps() const {
+ std::vector<TrackedListener> localListeners;
+ {
+ std::scoped_lock lock(mMutex);
+ if (mListeners.empty()) {
+ return;
+ }
+
+ std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners),
+ [](const std::pair<wp<IBinder>, TrackedListener>& entry) {
+ return entry.second;
+ });
+ }
+
+ for (const auto& listener : localListeners) {
+ sp<Layer> promotedLayer = listener.layer.promote();
+ if (promotedLayer != nullptr) {
+ std::unordered_set<int32_t> layerIds;
+
+ promotedLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+
+ listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+ }
+ }
+}
+
+void FpsReporter::binderDied(const wp<IBinder>& who) {
+ std::scoped_lock lock(mMutex);
+ mListeners.erase(who);
+}
+
+void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer) {
+ sp<IBinder> asBinder = IInterface::asBinder(listener);
+ asBinder->linkToDeath(this);
+ std::lock_guard lock(mMutex);
+ mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, layer});
+}
+
+void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
+ std::lock_guard lock(mMutex);
+ mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
new file mode 100644
index 0000000..d64b3dc
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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-base/thread_annotations.h>
+#include <android/gui/IFpsListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+#include "FrameTimeline/FrameTimeline.h"
+
+namespace android {
+
+class Layer;
+
+class FpsReporter : public IBinder::DeathRecipient {
+public:
+ FpsReporter(frametimeline::FrameTimeline& frameTimeline);
+
+ // Dispatches updated layer fps values for the registered listeners
+ // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+ // must be held when calling this method.
+ void dispatchLayerFps() const EXCLUDES(mMutex);
+
+ // Override for IBinder::DeathRecipient
+ void binderDied(const wp<IBinder>&) override;
+
+ // Registers an Fps listener that listens to fps updates for the provided layer
+ void addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer);
+ // Deregisters an Fps listener
+ void removeListener(const sp<gui::IFpsListener>& listener);
+
+private:
+ mutable std::mutex mMutex;
+ struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const {
+ return std::hash<IBinder*>()(p.unsafe_get());
+ }
+ };
+
+ struct TrackedListener {
+ sp<gui::IFpsListener> listener;
+ wp<Layer> layer;
+ };
+
+ frametimeline::FrameTimeline& mFrameTimeline;
+ std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 1e6d21e..10a5833 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -1,3 +1,12 @@
+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: "libframetimeline",
defaults: ["surfaceflinger_defaults"],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index da04202..75229e9 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -19,12 +19,15 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "FrameTimeline.h"
+
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/Trace.h>
+
#include <chrono>
#include <cinttypes>
#include <numeric>
+#include <unordered_set>
namespace android::frametimeline {
@@ -277,8 +280,8 @@
}
SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid,
- uid_t ownerUid, std::string layerName, std::string debugName,
- PredictionState predictionState,
+ uid_t ownerUid, int32_t layerId, std::string layerName,
+ std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
@@ -289,6 +292,7 @@
mOwnerUid(ownerUid),
mLayerName(std::move(layerName)),
mDebugName(std::move(debugName)),
+ mLayerId(layerId),
mPresentState(PresentState::Unknown),
mPredictionState(predictionState),
mPredictions(predictions),
@@ -397,6 +401,8 @@
StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
mRenderRate ? mRenderRate->getIntValue() : 0);
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer ID : %d\n", mLayerId);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
if (mPresentState == PresentState::Dropped) {
@@ -458,7 +464,9 @@
const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
- const nsecs_t deltaToVsync = std::abs(presentDelta) % refreshRate.getPeriodNsecs();
+ const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
+ : 0;
if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
@@ -496,6 +504,17 @@
if (mLastLatchTime != 0 && mPredictions.endTime <= mLastLatchTime) {
// Buffer Stuffing.
mJankType |= JankType::BufferStuffing;
+ // In a stuffed state, the frame could be stuck on a dequeue wait for quite some time.
+ // Because of this dequeue wait, it can be hard to tell if a frame was genuinely late.
+ // We try to do this by moving the deadline. Since the queue could be stuffed by more
+ // than one buffer, we take the last latch time as reference and give one vsync
+ // worth of time for the frame to be ready.
+ nsecs_t adjustedDeadline = mLastLatchTime + refreshRate.getPeriodNsecs();
+ if (adjustedDeadline > mActuals.endTime) {
+ mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+ } else {
+ mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+ }
}
if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
// Finish on time, Present late
@@ -503,14 +522,18 @@
// Propagate displayFrame's jank if it exists
mJankType |= displayFrameJankType;
} else {
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= refreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold) {
- // Delta factor of vsync
- mJankType |= JankType::SurfaceFlingerScheduling;
- } else {
- // Delta not a factor of vsync
- mJankType |= JankType::PredictionError;
+ if (!(mJankType & JankType::BufferStuffing)) {
+ // In a stuffed state, if the app finishes on time and there is no display frame
+ // jank, only buffer stuffing is the root cause of the jank.
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= refreshRate.getPeriodNsecs() -
+ mJankClassificationThresholds.presentThreshold) {
+ // Delta factor of vsync
+ mJankType |= JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta not a factor of vsync
+ mJankType |= JankType::PredictionError;
+ }
}
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
@@ -675,13 +698,14 @@
}
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds)
+ JankClassificationThresholds thresholds, nsecs_t hwcDuration)
: mMaxDisplayFrames(kDefaultMaxDisplayFrames),
mTimeStats(std::move(timeStats)),
mSurfaceFlingerPid(surfaceFlingerPid),
- mJankClassificationThresholds(thresholds) {
- mCurrentDisplayFrame =
- std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds(thresholds),
+ mHwcDuration(hwcDuration) {
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, thresholds, hwcDuration,
+ &mTraceCookieCounter);
}
void FrameTimeline::onBootFinished() {
@@ -698,11 +722,11 @@
}
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+ const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
std::string layerName, std::string debugName) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter);
@@ -710,13 +734,13 @@
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
if (predictions) {
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
&mTraceCookieCounter);
}
- return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+ return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
mJankClassificationThresholds, &mTraceCookieCounter);
@@ -724,11 +748,13 @@
FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
+ nsecs_t hwcDuration,
TraceCookieCounter* traceCookieCounter)
: mSurfaceFlingerPredictions(TimelineItem()),
mSurfaceFlingerActuals(TimelineItem()),
mTimeStats(timeStats),
mJankClassificationThresholds(thresholds),
+ mHwcDuration(hwcDuration),
mTraceCookieCounter(*traceCookieCounter) {
mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
}
@@ -800,11 +826,13 @@
const nsecs_t presentDelta =
mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
const nsecs_t deadlineDelta =
- mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime;
+ mSurfaceFlingerActuals.endTime - (mSurfaceFlingerPredictions.endTime - mHwcDuration);
// How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
// was a prediction error or not.
- nsecs_t deltaToVsync = std::abs(presentDelta) % mRefreshRate.getPeriodNsecs();
+ nsecs_t deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+ ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
+ : 0;
if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
@@ -812,8 +840,9 @@
mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
}
- if (mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime >
- mJankClassificationThresholds.deadlineThreshold) {
+ if (mSurfaceFlingerActuals.endTime > mSurfaceFlingerPredictions.endTime - mHwcDuration) {
+ // SF needs to have finished at least mHwcDuration ahead of the deadline for it to be
+ // on time.
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
} else {
mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
@@ -865,8 +894,13 @@
mJankType = JankType::PredictionError;
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
- // Finish late, Present late
- mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ if (mFrameStartMetadata == FrameStartMetadata::LateStart) {
+ // Late start, Late finish, Late Present
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // OnTime start, Finish late, Present late
+ mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ }
} else {
// Finish time unknown
mJankType = JankType::Unknown;
@@ -968,6 +1002,65 @@
}
}
+float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
+ if (layerIds.empty()) {
+ return 0.0f;
+ }
+
+ std::vector<nsecs_t> presentTimes;
+ {
+ std::scoped_lock lock(mMutex);
+ presentTimes.reserve(mDisplayFrames.size());
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ const auto& displayFrame = mDisplayFrames[i];
+ if (displayFrame->getActuals().presentTime <= 0) {
+ continue;
+ }
+ for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
+ layerIds.count(surfaceFrame->getLayerId()) > 0) {
+ // We're looking for DisplayFrames that presents at least one layer from
+ // layerIds, so push the present time and skip looking through the rest of the
+ // SurfaceFrames.
+ presentTimes.push_back(displayFrame->getActuals().presentTime);
+ break;
+ }
+ }
+ }
+ }
+
+ // FPS can't be computed when there's fewer than 2 presented frames.
+ if (presentTimes.size() <= 1) {
+ return 0.0f;
+ }
+
+ nsecs_t priorPresentTime = -1;
+ nsecs_t totalPresentToPresentWalls = 0;
+
+ for (const nsecs_t presentTime : presentTimes) {
+ if (priorPresentTime == -1) {
+ priorPresentTime = presentTime;
+ continue;
+ }
+
+ totalPresentToPresentWalls += (presentTime - priorPresentTime);
+ priorPresentTime = presentTime;
+ }
+
+ if (CC_UNLIKELY(totalPresentToPresentWalls <= 0)) {
+ ALOGW("Invalid total present-to-present duration when computing fps: %" PRId64,
+ totalPresentToPresentWalls);
+ return 0.0f;
+ }
+
+ const constexpr nsecs_t kOneSecond =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ // (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
+ // M frames / second
+ return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
+ static_cast<float>(totalPresentToPresentWalls);
+}
+
void FrameTimeline::flushPendingPresentFences() {
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
@@ -997,7 +1090,7 @@
mDisplayFrames.push_back(mCurrentDisplayFrame);
mCurrentDisplayFrame.reset();
mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter);
+ mHwcDuration, &mTraceCookieCounter);
}
nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 8f3157d..7c6a0cc 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -156,9 +156,10 @@
// Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
// TokenManager), Thresholds and TimeStats pointer.
SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName, PredictionState predictionState,
- TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats,
- JankClassificationThresholds thresholds, TraceCookieCounter* traceCookieCounter);
+ int32_t layerId, std::string layerName, std::string debugName,
+ PredictionState predictionState, TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+ TraceCookieCounter* traceCookieCounter);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -199,6 +200,7 @@
// Getter functions used only by FrameTimelineTests and SurfaceFrame internally
TimelineItem getActuals() const;
pid_t getOwnerPid() const { return mOwnerPid; };
+ int32_t getLayerId() const { return mLayerId; };
PredictionState getPredictionState() const;
PresentState getPresentState() const;
FrameReadyMetadata getFrameReadyMetadata() const;
@@ -221,6 +223,7 @@
const uid_t mOwnerUid;
const std::string mLayerName;
const std::string mDebugName;
+ const int32_t mLayerId;
PresentState mPresentState GUARDED_BY(mMutex);
const PredictionState mPredictionState;
const TimelineItem mPredictions;
@@ -267,7 +270,7 @@
// Debug name is the human-readable debugging string for dumpsys.
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName) = 0;
+ int32_t layerId, std::string layerName, std::string debugName) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -292,6 +295,11 @@
// Sets the max number of display frames that can be stored. Called by SF backdoor.
virtual void setMaxDisplayFrames(uint32_t size);
+ // Computes the historical fps for the provided set of layer IDs
+ // The fps is compted from the linear timeline of present timestamps for DisplayFrames
+ // containing at least one layer ID.
+ virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
+
// Restores the max number of display frames to default. Called by SF backdoor.
virtual void reset() = 0;
};
@@ -334,7 +342,7 @@
class DisplayFrame {
public:
DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter);
+ nsecs_t hwcDuration, TraceCookieCounter* traceCookieCounter);
virtual ~DisplayFrame() = default;
// Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
// SurfaceFrame is janky.
@@ -363,6 +371,7 @@
// Functions to be used only in testing.
TimelineItem getActuals() const { return mSurfaceFlingerActuals; };
TimelineItem getPredictions() const { return mSurfaceFlingerPredictions; };
+ FrameStartMetadata getFrameStartMetadata() const { return mFrameStartMetadata; };
FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
int32_t getJankType() const { return mJankType; }
@@ -386,6 +395,7 @@
TimelineItem mSurfaceFlingerActuals;
std::shared_ptr<TimeStats> mTimeStats;
const JankClassificationThresholds mJankClassificationThresholds;
+ const nsecs_t mHwcDuration;
// Collection of predictions and actual values sent over by Layers
std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
@@ -411,19 +421,21 @@
};
FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds = {});
+ JankClassificationThresholds thresholds = {},
+ nsecs_t hwcDuration = kDefaultHwcDuration);
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- std::string layerName, std::string debugName) override;
+ int32_t layerId, std::string layerName, std::string debugName) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
+ float computeFps(const std::unordered_set<int32_t>& layerIds) override;
void reset() override;
// Sets up the perfetto tracing backend and data source.
@@ -455,6 +467,11 @@
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
const JankClassificationThresholds mJankClassificationThresholds;
+ // In SF Predictions, both end & present are the same. The predictions consider the time used by
+ // composer as well, but we have no way to estimate how much time the composer needs. We are
+ // assuming an arbitrary time for the composer work.
+ const nsecs_t mHwcDuration;
+ static constexpr nsecs_t kDefaultHwcDuration = std::chrono::nanoseconds(3ms).count();
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although
// this number doesn't represent any bounds on the number of surface frames that can go in a
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 141a112..937868a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1595,7 +1595,8 @@
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
const FrameTimelineInfo& info, nsecs_t postTime) {
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName,
mTransactionName);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1611,8 +1612,8 @@
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
- debugName);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+ getSequence(), mName, debugName);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 14bb5b7..a072554 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -501,6 +501,7 @@
// one empty rect.
virtual void useSurfaceDamage() {}
virtual void useEmptyDamage() {}
+ Region getVisibleRegion(const DisplayDevice*) const;
virtual void incrementPendingBufferCount() {}
@@ -723,7 +724,7 @@
// Returns the bounds of the layer without any buffer scaling.
FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const;
- int32_t getSequence() const { return sequence; }
+ int32_t getSequence() const override { return sequence; }
// For tracing.
// TODO: Replace with raw buffer id from buffer metadata when that becomes available.
@@ -923,6 +924,8 @@
pid_t getOwnerPid() { return mOwnerPid; }
+ virtual bool frameIsEarly(nsecs_t /*expectedPresentTime*/) const { return false; }
+
// This layer is not a clone, but it's the parent to the cloned hierarchy. The
// variable mClonedChild represents the top layer that will be cloned so this
// layer will be the parent of mClonedChild.
@@ -1009,6 +1012,7 @@
// For unit tests
friend class TestableSurfaceFlinger;
+ friend class FpsReporterTest;
friend class RefreshRateSelectionTest;
friend class SetFrameRateTest;
friend class TransactionFrameTracerTest;
@@ -1165,7 +1169,6 @@
virtual bool canDrawShadows() const { return true; }
Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
- Region getVisibleRegion(const DisplayDevice*) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 09615f9..e06bc88 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -481,6 +481,12 @@
// 1) The region sampling thread is the last owner of the buffer, and the freeing of the buffer
// happens in this thread, as opposed to the main thread.
// 2) The listener(s) receive their notifications prior to freeing the buffer.
+ if (mCachedBuffer != nullptr && mCachedBuffer != buffer) {
+ if (mFlinger.getRenderEngine().getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
+ mFlinger.getRenderEngine().unbindExternalTextureBuffer(mCachedBuffer->getId());
+ }
+ }
mCachedBuffer = buffer;
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4ee759f..350a3c8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -108,6 +108,7 @@
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
+#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
@@ -254,6 +255,11 @@
std::function<void()> mCallback;
};
+enum Permission {
+ ACCESS_SURFACE_FLINGER = 0x1,
+ ROTATE_SURFACE_FLINGER = 0x2,
+};
+
} // namespace anonymous
struct SetInputWindowsListener : os::BnSetInputWindowsListener {
@@ -490,6 +496,9 @@
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
+ // Sever the link to inputflinger since its gone as well.
+ static_cast<void>(schedule([=] { mInputFlinger = nullptr; }));
+
// restore initial conditions (default device unblank, etc)
initializeDisplays();
@@ -966,9 +975,18 @@
}
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
- info->supportedColorModes = getDisplayColorModes(display->getPhysicalId());
+ const auto displayId = display->getPhysicalId();
+ info->supportedColorModes = getDisplayColorModes(displayId);
info->hdrCapabilities = display->getHdrCapabilities();
+ info->autoLowLatencyModeSupported =
+ getHwComposer().hasDisplayCapability(displayId,
+ hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
+ std::vector<hal::ContentType> types;
+ getHwComposer().getSupportedContentTypes(displayId, &types);
+ info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
+ return type == hal::ContentType::GAME;
+ });
return NO_ERROR;
}
@@ -1243,24 +1261,6 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
- bool* outSupport) const {
- if (!displayToken) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
- return NAME_NOT_FOUND;
- }
- *outSupport =
- getHwComposer().hasDisplayCapability(*displayId,
- hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
- return NO_ERROR;
-}
-
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
static_cast<void>(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
@@ -1271,27 +1271,6 @@
}));
}
-status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
- bool* outSupport) const {
- if (!displayToken) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
- return NAME_NOT_FOUND;
- }
-
- std::vector<hal::ContentType> types;
- getHwComposer().getSupportedContentTypes(*displayId, &types);
-
- *outSupport = std::any_of(types.begin(), types.end(),
- [](auto type) { return type == hal::ContentType::GAME; });
- return NO_ERROR;
-}
-
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
static_cast<void>(schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
@@ -1452,6 +1431,25 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+
+ const wp<Layer> layer = fromHandle(layerHandle);
+ mFpsReporter->addListener(listener, layer);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+ if (!listener) {
+ return BAD_VALUE;
+ }
+ mFpsReporter->removeListener(listener);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
if (!displayToken || !outSupport) {
@@ -2138,6 +2136,13 @@
}
});
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (mFpsReporter) {
+ mFpsReporter->dispatchLayerFps();
+ }
+ }
+
mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
mTransactionCallbackInvoker.sendCallbacks();
@@ -2954,6 +2959,7 @@
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
+ mFpsReporter = new FpsReporter(*mFrameTimeline);
// Dispatch a mode change request for the primary display on scheduler
// initialization, so that the EventThreads always contain a reference to a
// prior configuration.
@@ -3304,6 +3310,7 @@
transactions.push_back(transaction);
}
mTransactionQueue.pop();
+ ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
}
@@ -3313,7 +3320,7 @@
transaction.displays, transaction.flags,
transaction.inputWindowCommands, transaction.desiredPresentTime,
transaction.isAutoTimestamp, transaction.buffer,
- transaction.postTime, transaction.privileged,
+ transaction.postTime, transaction.permissions,
transaction.hasListenerCallbacks, transaction.listenerCallbacks,
transaction.originPid, transaction.originUid, transaction.id);
}
@@ -3357,6 +3364,10 @@
if (!layer) {
continue;
}
+ if (layer->frameIsEarly(expectedPresentTime)) {
+ ATRACE_NAME("frameIsEarly()");
+ return false;
+ }
if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
ATRACE_NAME("!isVsyncValidForUid");
@@ -3378,94 +3389,51 @@
return ready;
}
-status_t SurfaceFlinger::setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
- ATRACE_CALL();
+void SurfaceFlinger::queueTransaction(TransactionState state) {
+ Mutex::Autolock _l(mQueueLock);
- {
- Mutex::Autolock _l(mQueueLock);
-
- const int64_t postTime = systemTime();
- bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
-
- IPCThreadState* ipc = IPCThreadState::self();
- const int originPid = ipc->getCallingPid();
- const int originUid = ipc->getCallingUid();
-
- // If its TransactionQueue already has a pending TransactionState or if it is pending
- auto itr = mPendingTransactionQueues.find(applyToken);
- // if this is an animation frame, wait until prior animation frame has
- // been applied by SF
- if (flags & eAnimation) {
- while (itr != mPendingTransactionQueues.end()) {
- status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- ALOGW_IF(err == TIMED_OUT,
- "setTransactionState timed out "
- "waiting for animation frame to apply");
- break;
- }
- itr = mPendingTransactionQueues.find(applyToken);
+ // If its TransactionQueue already has a pending TransactionState or if it is pending
+ auto itr = mPendingTransactionQueues.find(state.applyToken);
+ // if this is an animation frame, wait until prior animation frame has
+ // been applied by SF
+ if (state.flags & eAnimation) {
+ while (itr != mPendingTransactionQueues.end()) {
+ status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5));
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ ALOGW_IF(err == TIMED_OUT,
+ "setTransactionState timed out "
+ "waiting for animation frame to apply");
+ break;
}
+ itr = mPendingTransactionQueues.find(state.applyToken);
}
-
- const bool pendingTransactions = itr != mPendingTransactionQueues.end();
- // Expected present time is computed and cached on invalidate, so it may be stale.
- if (!pendingTransactions) {
- const auto now = systemTime();
- const bool nextVsyncPending = now < mExpectedPresentTime.load();
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
- mExpectedPresentTime = calculateExpectedPresentTime(stats);
- // The transaction might arrive just before the next vsync but after
- // invalidate was called. In that case we need to get the next vsync
- // afterwards.
- if (nextVsyncPending) {
- mExpectedPresentTime += stats.vsyncPeriod;
- }
- }
-
- mTransactionQueue.emplace(frameTimelineInfo, states, displays, flags, applyToken,
- inputWindowCommands, desiredPresentTime, isAutoTimestamp,
- uncacheBuffer, postTime, privileged, hasListenerCallbacks,
- listenerCallbacks, originPid, originUid, transactionId);
-
- if (pendingTransactions ||
- (!isAutoTimestamp && desiredPresentTime > mExpectedPresentTime.load())) {
- setTransactionFlags(eTransactionFlushNeeded);
- return NO_ERROR;
- }
-
- // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
- if (flags & eEarlyWakeup) {
- ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
- }
-
- if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
- flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
- }
-
- const auto schedule = [](uint32_t flags) {
- if (flags & eEarlyWakeup) return TransactionSchedule::Early;
- if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
- if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
- return TransactionSchedule::Late;
- }(flags);
- setTransactionFlags(eTransactionFlushNeeded, schedule);
}
- // if this is a synchronous transaction, wait for it to take effect
- // before returning.
- const bool synchronous = flags & eSynchronous;
- const bool syncInput = inputWindowCommands.syncInputWindows;
- if (!synchronous && !syncInput) {
- return NO_ERROR;
+ mTransactionQueue.emplace(state);
+ ATRACE_INT("TransactionQueue", mTransactionQueue.size());
+
+ // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
+ if (state.flags & eEarlyWakeup) {
+ ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
}
+ if (!(state.permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+ (state.flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+ state.flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ }
+
+ const auto schedule = [](uint32_t flags) {
+ if (flags & eEarlyWakeup) return TransactionSchedule::Early;
+ if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
+ if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart;
+ return TransactionSchedule::Late;
+ }(state.flags);
+
+ setTransactionFlags(eTransactionFlushNeeded, schedule);
+}
+
+void SurfaceFlinger::waitForSynchronousTransaction(bool synchronous, bool syncInput) {
Mutex::Autolock _l(mStateLock);
if (synchronous) {
mTransactionPending = true;
@@ -3489,6 +3457,41 @@
break;
}
}
+}
+
+status_t SurfaceFlinger::setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
+ const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+ ATRACE_CALL();
+
+ uint32_t permissions =
+ callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
+ // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
+ // permissions.
+ if ((permissions & Permission::ACCESS_SURFACE_FLINGER) ||
+ callingThreadHasRotateSurfaceFlingerAccess()) {
+ permissions |= Permission::ROTATE_SURFACE_FLINGER;
+ }
+
+ const int64_t postTime = systemTime();
+
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int originPid = ipc->getCallingPid();
+ const int originUid = ipc->getCallingUid();
+
+ queueTransaction({frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands,
+ desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, permissions,
+ hasListenerCallbacks, listenerCallbacks, originPid, originUid,
+ transactionId});
+
+ const bool synchronous = flags & eSynchronous;
+ const bool syncInput = inputWindowCommands.syncInputWindows;
+ if (synchronous || syncInput) {
+ waitForSynchronousTransaction(synchronous, syncInput);
+ }
return NO_ERROR;
}
@@ -3499,12 +3502,11 @@
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer,
- const int64_t postTime, bool privileged,
+ const int64_t postTime, uint32_t permissions,
bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
-
for (const DisplayState& display : displays) {
transactionFlags |= setDisplayStateLocked(display);
}
@@ -3522,7 +3524,7 @@
for (const ComposerState& state : states) {
clientStateFlags |=
setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
- postTime, privileged, listenerCallbacksWithSurfaces);
+ postTime, permissions, listenerCallbacksWithSurfaces);
if ((flags & eAnimation) && state.state.surface) {
if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
mScheduler->recordLayerHistory(layer.get(),
@@ -3542,7 +3544,7 @@
}
transactionFlags |= clientStateFlags;
- if (privileged) {
+ if (permissions & Permission::ACCESS_SURFACE_FLINGER) {
transactionFlags |= addInputWindowCommands(inputWindowCommands);
} else if (!inputWindowCommands.empty()) {
ALOGE("Only privileged callers are allowed to send input commands.");
@@ -3646,10 +3648,10 @@
uint32_t SurfaceFlinger::setClientStateLocked(
const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
- int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
+ int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
const layer_state_t& s = composerState.state;
-
+ const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
for (auto& listener : s.listeners) {
// note that startRegistration will not re-register if the listener has
// already be registered for a prior surface control
@@ -3774,8 +3776,8 @@
// ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER
// (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle
// preserving transformations.
- bool allowNonRectPreservingTransforms =
- privileged || callingThreadHasRotateSurfaceFlingerAccess();
+ const bool allowNonRectPreservingTransforms =
+ permissions & Permission::ROTATE_SURFACE_FLINGER;
if (layer->setMatrix(s.matrix, allowNonRectPreservingTransforms)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eTransparentRegionChanged) {
@@ -4377,6 +4379,7 @@
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
{"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
{"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
{"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
@@ -4513,6 +4516,13 @@
mScheduler->dumpVsync(result);
}
+void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
+ for (const auto& [token, display] : mDisplays) {
+ const auto compositionDisplay = display->getCompositionDisplay();
+ compositionDisplay->dumpPlannerInfo(args, result);
+ }
+}
+
void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
result.append("Static screen stats:\n");
for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
@@ -5004,6 +5014,8 @@
// This is not sensitive information, so should not require permission control.
return OK;
}
+ case ADD_FPS_LISTENER:
+ case REMOVE_FPS_LISTENER:
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
@@ -5860,6 +5872,15 @@
regionSampling, grayscale, captureResults);
});
+ // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
+ // Only do this when we're not doing region sampling, to allow the region sampling thread to
+ // manage buffer lifecycle itself.
+ if (!regionSampling &&
+ getRenderEngine().getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED) {
+ getRenderEngine().unbindExternalTextureBuffer(buffer->getId());
+ }
+
captureResults.result = result;
captureListener->onScreenCaptureCompleted(captureResults);
}));
@@ -5979,8 +6000,12 @@
base::unique_fd bufferFence;
base::unique_fd drawFence;
getRenderEngine().useProtectedContext(useProtected);
+
+ // TODO(b/180767535): Remove this once we optimize buffer lifecycle for RenderEngine
+ const bool useFramebufferCache = getRenderEngine().getRenderEngineType() ==
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
- /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
+ useFramebufferCache, std::move(bufferFence), &drawFence);
if (drawFence >= 0) {
sp<Fence> releaseFence = new Fence(dup(drawFence));
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 21cd2a5..1615ab6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -89,6 +89,7 @@
class Client;
class EventThread;
+class FpsReporter;
class HWComposer;
struct SetInputWindowsListener;
class IGraphicBufferProducer;
@@ -344,7 +345,8 @@
virtual uint32_t setClientStateLocked(
const FrameTimelineInfo& info, const ComposerState& composerState,
- int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
+ int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime,
+ uint32_t permissions,
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
REQUIRES(mStateLock);
virtual void commitTransactionLocked();
@@ -441,7 +443,7 @@
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
- int64_t postTime, bool privileged, bool hasListenerCallbacks,
+ int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
int originUid, uint64_t transactionId)
: frameTimelineInfo(frameTimelineInfo),
@@ -454,7 +456,7 @@
isAutoTimestamp(isAutoTimestamp),
buffer(uncacheBuffer),
postTime(postTime),
- privileged(privileged),
+ permissions(permissions),
hasListenerCallbacks(hasListenerCallbacks),
listenerCallbacks(listenerCallbacks),
originPid(originPid),
@@ -471,7 +473,7 @@
const bool isAutoTimestamp;
client_cache_t buffer;
const int64_t postTime;
- bool privileged;
+ uint32_t permissions;
bool hasListenerCallbacks;
std::vector<ListenerCallbacks> listenerCallbacks;
int originPid;
@@ -561,11 +563,7 @@
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
ui::DisplayPrimaries&) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
- status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
- bool* outSupported) const override;
void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
- status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
- bool* outSupported) const override;
void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
status_t clearAnimationFrameStats() override;
@@ -592,6 +590,9 @@
status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
+ status_t addFpsListener(const sp<IBinder>& layerHandle,
+ const sp<gui::IFpsListener>& listener) override;
+ status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
ui::DisplayModeId displayModeId, bool allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -740,7 +741,7 @@
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
- bool privileged, bool hasListenerCallbacks,
+ uint32_t permissions, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId)
REQUIRES(mStateLock);
@@ -1016,6 +1017,7 @@
LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
EXCLUDES(mStateLock);
void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
+ void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
bool isLayerTripleBufferingDisabled() const {
return this->mLayerTripleBufferingDisabled;
@@ -1039,6 +1041,10 @@
// either AID_GRAPHICS or AID_SYSTEM.
status_t CheckTransactCodeCredentials(uint32_t code);
+ // Add transaction to the Transaction Queue
+ void queueTransaction(TransactionState state) EXCLUDES(mQueueLock);
+ void waitForSynchronousTransaction(bool synchronous, bool syncInput) EXCLUDES(mStateLock);
+
/*
* Generic Layer Metadata
*/
@@ -1268,6 +1274,7 @@
bool mLumaSampling = true;
sp<RegionSamplingThread> mRegionSamplingThread;
+ sp<FpsReporter> mFpsReporter;
ui::DisplayPrimaries mInternalDisplayPrimaries;
const float mInternalDisplayDensity;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index c043866..b3dca78 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -380,5 +380,9 @@
return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue);
}
+bool enable_layer_caching(bool defaultValue) {
+ return SurfaceFlingerProperties::enable_layer_caching().value_or(defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 816cb09..b19d216 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -100,6 +100,8 @@
bool enable_frame_rate_override(bool defaultValue);
+bool enable_layer_caching(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 4d25a7a..ee5542d 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -454,3 +454,12 @@
access: Readonly
prop_name: "ro.surface_flinger.enable_frame_rate_override"
}
+
+# Enables Layer Caching
+prop {
+ api_name: "enable_layer_caching"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.enable_layer_caching"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0e0be09..47e14f6 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -45,6 +45,10 @@
prop_name: "ro.surface_flinger.enable_frame_rate_override"
}
prop {
+ api_name: "enable_layer_caching"
+ prop_name: "ro.surface_flinger.enable_layer_caching"
+ }
+ prop {
api_name: "enable_protected_contents"
prop_name: "ro.surface_flinger.protected_contents"
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 2ac6b09..3c1b9d8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -53,6 +53,7 @@
"DisplayDevice_GetBestColorModeTest.cpp",
"DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
+ "FpsReporterTest.cpp",
"FpsTest.cpp",
"FrameTimelineTest.cpp",
"HWComposerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
new file mode 100644
index 0000000..a9e5df3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2021 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 "FpsReporterTest"
+
+#include <android/gui/BnFpsListener.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "FpsReporter.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockFrameTimeline.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::UnorderedElementsAre;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+struct TestableFpsListener : public gui::BnFpsListener {
+ TestableFpsListener() {}
+
+ float lastReportedFps = 0;
+
+ binder::Status onFpsReported(float fps) override {
+ lastReportedFps = fps;
+ return binder::Status::ok();
+ }
+};
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class FpsReporterTest : public testing::Test {
+public:
+ FpsReporterTest();
+ ~FpsReporterTest() override;
+
+protected:
+ static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+ static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+ static constexpr int32_t PRIORITY_UNSET = -1;
+
+ void setupScheduler();
+ void setupComposer(uint32_t virtualDisplayCount);
+ sp<BufferStateLayer> createBufferStateLayer();
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::FrameTimeline mFrameTimeline =
+ mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
+
+ sp<Client> mClient;
+ sp<Layer> mParent;
+ sp<Layer> mTarget;
+ sp<Layer> mChild;
+ sp<Layer> mGrandChild;
+ sp<Layer> mUnrelated;
+
+ sp<TestableFpsListener> mFpsListener;
+ sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline);
+};
+
+FpsReporterTest::FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ setupScheduler();
+ setupComposer(0);
+ mFpsListener = new TestableFpsListener();
+}
+
+FpsReporterTest::~FpsReporterTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer() {
+ sp<Client> client;
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferStateLayer(args);
+}
+
+void FpsReporterTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread));
+}
+
+void FpsReporterTest::setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+
+TEST_F(FpsReporterTest, callsListeners) {
+ mParent = createBufferStateLayer();
+ mTarget = createBufferStateLayer();
+ mChild = createBufferStateLayer();
+ mGrandChild = createBufferStateLayer();
+ mUnrelated = createBufferStateLayer();
+ mParent->addChild(mTarget);
+ mTarget->addChild(mChild);
+ mChild->addChild(mGrandChild);
+ mParent->commitChildList();
+
+ float expectedFps = 44.0;
+
+ EXPECT_CALL(mFrameTimeline,
+ computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(),
+ mGrandChild->getSequence())))
+ .WillOnce(Return(expectedFps));
+
+ mFpsReporter->addListener(mFpsListener, mTarget);
+ mFpsReporter->dispatchLayerFps();
+ EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
+ mFpsReporter->removeListener(mFpsListener);
+ Mock::VerifyAndClearExpectations(&mFrameTimeline);
+
+ EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0);
+ mFpsReporter->dispatchLayerFps();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index b8c1607..7e6141e 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -67,7 +67,7 @@
void SetUp() override {
mTimeStats = std::make_shared<mock::TimeStats>();
mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
- kTestThresholds);
+ kTestThresholds, kHwcDuration);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
@@ -162,6 +162,7 @@
static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
kDeadlineThreshold,
kStartThreshold};
+ static constexpr nsecs_t kHwcDuration = std::chrono::nanoseconds(3ns).count();
};
static const std::string sLayerNameOne = "layer1";
@@ -170,6 +171,8 @@
static constexpr pid_t sPidOne = 10;
static constexpr pid_t sPidTwo = 20;
static constexpr int32_t sInputEventId = 5;
+static constexpr int32_t sLayerIdOne = 1;
+static constexpr int32_t sLayerIdTwo = 2;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -187,17 +190,20 @@
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -206,7 +212,7 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -215,7 +221,7 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -226,7 +232,7 @@
constexpr int32_t inputEventId = 1;
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -236,7 +242,7 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -263,10 +269,12 @@
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameTwo, sLayerNameTwo);
+ sUidOne, sLayerIdTwo, sLayerNameTwo,
+ sLayerNameTwo);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -307,8 +315,8 @@
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
- sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -329,7 +337,8 @@
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -343,18 +352,18 @@
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -367,8 +376,8 @@
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -383,8 +392,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -399,8 +408,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne);
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -412,36 +421,29 @@
// Tests related to TimeStats
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
- Fps refreshRate = Fps(11);
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ // Deadline delta is 2ms because, sf's adjusted deadline is 60 - composerTime(3) = 57ms.
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
sLayerNameOne,
- JankType::SurfaceFlingerCpuDeadlineMissed,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(3ms)
- .count(),
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(10ms)
- .count(),
+ JankType::SurfaceFlingerCpuDeadlineMissed, 2, 10,
0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
+ surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(70ms).count());
+ presentFence1->signalForTest(70);
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(59ms).count(), presentFence1);
+ mFrameTimeline->setSfPresent(59, presentFence1);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
@@ -449,180 +451,153 @@
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
sLayerNameOne, JankType::DisplayHAL,
- 0, 0, 0}));
+ -1, 0, 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
+ surfaceFrame1->setAcquireFenceTime(20);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
Fps refreshRate = Fps(11.0);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne, JankType::AppDeadlineMissed, 0, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(25ms)
- .count()}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::AppDeadlineMissed, -1, 0,
+ 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(),
- std::chrono::nanoseconds(90ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(45);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) {
- Fps refreshRate = Fps::fromPeriodNsecs(std::chrono::nanoseconds(32ms).count());
+ Fps refreshRate = Fps::fromPeriodNsecs(32);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
- JankType::SurfaceFlingerScheduling, 0, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(-10ms)
- .count()}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::SurfaceFlingerScheduling,
+ -1, 0, -10}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(40ms).count(), std::chrono::nanoseconds(60ms).count(),
- std::chrono::nanoseconds(92ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({40, 60, 92});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(50ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(60ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1);
+ presentFence1->signalForTest(60);
+ mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) {
- Fps refreshRate = Fps(16.66f);
+ Fps refreshRate = Fps::fromPeriodNsecs(16);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne, JankType::PredictionError, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(5ms)
- .count(),
- 0}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::PredictionError, -1, 5,
+ 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(30ms).count(), std::chrono::nanoseconds(40ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(),
- std::chrono::nanoseconds(60ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(65ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1);
+ presentFence1->signalForTest(65);
+ mFrameTimeline->setSfPresent(56, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) {
- Fps refreshRate = Fps::fromPeriodNsecs(std::chrono::nanoseconds(32ms).count());
+ Fps refreshRate = Fps::fromPeriodNsecs(32);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(
- TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
- sLayerNameOne,
- JankType::BufferStuffing |
- JankType::SurfaceFlingerScheduling,
- 0, 0, 0}));
+ incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+ sLayerNameOne,
+ JankType::BufferStuffing, -1, 0,
+ 0}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(30ms).count(), std::chrono::nanoseconds(40ms).count(),
- std::chrono::nanoseconds(58ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(),
- std::chrono::nanoseconds(90ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 58});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(82ms).count(), refreshRate);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented,
- /*previousLatchTime*/
- std::chrono::nanoseconds(56ms).count());
+ /*previousLatchTime*/ 56);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
- EXPECT_EQ(surfaceFrame1->getJankType(),
- JankType::BufferStuffing | JankType::SurfaceFlingerScheduling);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
- Fps refreshRate = Fps(11.0);
- Fps renderRate = Fps(30.0);
+ Fps refreshRate = Fps::fromPeriodNsecs(11);
+ Fps renderRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(
TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
- JankType::AppDeadlineMissed, 0, 0,
- std::chrono::duration_cast<
- std::chrono::nanoseconds>(25ms)
- .count()}));
+ JankType::AppDeadlineMissed, -1, 0, 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(),
- std::chrono::nanoseconds(60ms).count()});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(),
- std::chrono::nanoseconds(90ms).count()});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
- surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
- mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(45);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setRenderRate(renderRate);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1);
+ presentFence1->signalForTest(90);
+ mFrameTimeline->setSfPresent(86, presentFence1);
EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
}
@@ -641,7 +616,7 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -667,7 +642,7 @@
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -710,8 +685,9 @@
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
- sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -877,7 +853,7 @@
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
- int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30});
+ int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
@@ -917,7 +893,7 @@
// Packet - 1 : FrameEnd (ExpectedDisplayFrame)
const auto& packet1 = packets[1];
ASSERT_TRUE(packet1.has_timestamp());
- EXPECT_EQ(packet1.timestamp(), 25u);
+ EXPECT_EQ(packet1.timestamp(), 30u);
ASSERT_TRUE(packet1.has_frame_timeline_event());
const auto& event1 = packet1.frame_timeline_event();
@@ -1015,10 +991,12 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1161,12 +1139,9 @@
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
- constexpr nsecs_t appStartTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count();
- constexpr nsecs_t appEndTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count();
- constexpr nsecs_t appPresentTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count();
+ constexpr nsecs_t appStartTime = std::chrono::nanoseconds(10ms).count();
+ constexpr nsecs_t appEndTime = std::chrono::nanoseconds(20ms).count();
+ constexpr nsecs_t appPresentTime = std::chrono::nanoseconds(30ms).count();
int64_t surfaceFrameToken =
mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime});
@@ -1174,17 +1149,14 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
- sPidOne, sUidOne, sLayerNameOne,
+ sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
sLayerNameOne);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
- constexpr nsecs_t sfStartTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count();
- constexpr nsecs_t sfEndTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count();
- constexpr nsecs_t sfPresentTime =
- std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count();
+ constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(20ms).count();
+ constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
+ constexpr nsecs_t sfPresentTime = std::chrono::nanoseconds(30ms).count();
int64_t displayFrameToken =
mTokenManager->generateTokenForPredictions({sfStartTime, sfEndTime, sfPresentTime});
@@ -1243,10 +1215,11 @@
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1273,8 +1246,8 @@
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
Fps vsyncRate = Fps::fromPeriodNsecs(11);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
@@ -1312,8 +1285,8 @@
TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
Fps vsyncRate = Fps::fromPeriodNsecs(11);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
@@ -1392,21 +1365,44 @@
EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
}
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) {
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11));
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(52);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
+ EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
- mFrameTimeline->setSfPresent(26, presentFence1);
+ mFrameTimeline->setSfPresent(27, presentFence1);
auto displayFrame1 = getDisplayFrame(0);
auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
presentFence1->signalForTest(30);
@@ -1420,12 +1416,13 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
- mFrameTimeline->setSfPresent(56, presentFence2);
+ mFrameTimeline->setSfPresent(57, presentFence2);
auto displayFrame2 = getDisplayFrame(1);
auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
@@ -1473,13 +1470,14 @@
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1498,12 +1496,13 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
- mFrameTimeline->setSfPresent(56, presentFence2);
+ mFrameTimeline->setSfPresent(57, presentFence2);
auto displayFrame2 = getDisplayFrame(1);
auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
@@ -1552,11 +1551,12 @@
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 50, 50});
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1593,13 +1593,14 @@
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 40, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 50, 50});
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1618,7 +1619,8 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1667,11 +1669,12 @@
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 56, 60});
- int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 120, 120});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1690,7 +1693,8 @@
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
- sUidOne, sLayerNameOne, sLayerNameOne);
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1735,4 +1739,229 @@
EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
JankType::AppDeadlineMissed | JankType::BufferStuffing);
}
+
+TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffing) {
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(60);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
+ sUidOne, sLayerIdOne, sLayerNameOne,
+ sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(80);
+ mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
+ // Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(86, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+ presentFence2->signalForTest(90);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.endTime, 50);
+ EXPECT_EQ(actuals1.presentTime, 60);
+
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 90);
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 90);
+
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::BufferStuffing);
+}
+
+TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
+ EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_singleDisplayFrame_returnsZero) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 10.0);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne, sLayerIdTwo}), 10.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) {
+ const auto oneHundredMs = std::chrono::nanoseconds(100ms).count();
+ const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
+ const auto threeHundredMs = std::chrono::nanoseconds(300ms).count();
+ const auto fiveHundredMs = std::chrono::nanoseconds(500ms).count();
+ const auto sixHundredMs = std::chrono::nanoseconds(600ms).count();
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ presentFence1->signalForTest(oneHundredMs);
+ mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ presentFence2->signalForTest(twoHundredMs);
+ mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+ auto surfaceFrame3 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3);
+ presentFence3->signalForTest(threeHundredMs);
+ mFrameTimeline->setSfPresent(threeHundredMs, presentFence3);
+
+ auto surfaceFrame4 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame4);
+ presentFence4->signalForTest(fiveHundredMs);
+ mFrameTimeline->setSfPresent(fiveHundredMs, presentFence4);
+
+ auto surfaceFrame5 =
+ mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+ sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ // Dropped frames will be excluded from fps computation
+ surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame5);
+ presentFence5->signalForTest(sixHundredMs);
+ mFrameTimeline->setSfPresent(sixHundredMs, presentFence5);
+
+ EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
+}
+
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index eb2c1ba..7c431a0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -127,7 +127,6 @@
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -163,8 +162,6 @@
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
nsecs_t time = systemTime();
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .WillOnce(Return(time + nsecs_t(5 * 1e8)));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
/*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
@@ -177,7 +174,11 @@
transaction.id);
nsecs_t returnedTime = systemTime();
- EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
+ if ((flags & ISurfaceComposer::eSynchronous) || syncInputWindows) {
+ EXPECT_GE(systemTime(), applicationSentTime + s2ns(5));
+ } else {
+ EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
+ }
// This transaction should have been placed on the transaction queue
auto transactionQueue = mFlinger.getTransactionQueue();
EXPECT_EQ(1u, transactionQueue.size());
@@ -187,9 +188,11 @@
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
// called in SurfaceFlinger::signalTransaction
nsecs_t time = systemTime();
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .WillOnce(Return(time + nsecs_t(5 * 1e8)));
+ if (!syncInputWindows) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(2);
+ } else {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ }
// transaction that should go on the pending thread
TransactionInfo transactionA;
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
@@ -229,7 +232,8 @@
// if this is an animation, this thread should be blocked for 5s
// in setTransactionState waiting for transactionA to flush. Otherwise,
// the transaction should be placed on the pending queue
- if (flags & ISurfaceComposer::eAnimation) {
+ if (flags & (ISurfaceComposer::eAnimation | ISurfaceComposer::eSynchronous) ||
+ syncInputWindows) {
EXPECT_GE(systemTime(), applicationSentTime + s2ns(5));
} else {
EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
@@ -238,18 +242,9 @@
// transaction that would goes to pending transaciton queue.
mFlinger.flushTransactionQueues();
- // check that there is one binder on the pending queue.
+ // check that the transaction was applied.
auto transactionQueue = mFlinger.getPendingTransactionQueue();
- EXPECT_EQ(1u, transactionQueue.size());
-
- auto& [applyToken, transactionStates] = *(transactionQueue.begin());
- EXPECT_EQ(2u, transactionStates.size());
-
- auto& transactionStateA = transactionStates.front();
- transactionStates.pop();
- checkEqual(transactionA, transactionStateA);
- auto& transactionStateB = transactionStates.front();
- checkEqual(transactionB, transactionStateB);
+ EXPECT_EQ(0u, transactionQueue.size());
}
bool mHasListenerCallbacks = false;
@@ -262,10 +257,6 @@
// called in SurfaceFlinger::signalTransaction
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
- // nsecs_t time = systemTime();
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
- .WillOnce(Return(nsecs_t(5 * 1e8)))
- .WillOnce(Return(s2ns(2)));
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
/*desiredPresentTime*/ s2ns(1), false, FrameTimelineInfo{});
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 44b9b73..b9d1794 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -33,6 +33,7 @@
MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
};
} // namespace android::mock
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4f89353..2002bdf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
cc_library_shared {
name: "libvibratorservice",
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 7b4cc19..a468146 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
cc_benchmark {
name: "libvibratorservice_benchmarks",
srcs: [
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index ad85990..3294724 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -12,6 +12,15 @@
// 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"],
+}
+
cc_test {
name: "libvibratorservice_test",
test_suites: ["device-tests"],
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 8523bb2..f5491cf 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -18,8 +18,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp
index 98afdec..fa71ed7 100644
--- a/services/vr/hardware_composer/aidl/Android.bp
+++ b/services/vr/hardware_composer/aidl/Android.bp
@@ -4,8 +4,6 @@
// all of the 'license_kinds' from "frameworks_native_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-MIT
- // SPDX-license-identifier-Unicode-DFS
default_applicable_licenses: ["frameworks_native_license"],
}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 2d4690a..d1cd397 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -33,6 +33,7 @@
#include <unordered_set>
#include <utility>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <cutils/properties.h>
#include <log/log.h>
@@ -134,7 +135,7 @@
// If no layers specified via Settings, check legacy properties
if (implicit_layers_.count <= 0) {
ParseDebugVulkanLayers();
- property_list(ParseDebugVulkanLayer, this);
+ ParseDebugVulkanLayer();
// sort by priorities
auto& arr = implicit_layers_;
@@ -181,30 +182,39 @@
AddImplicitLayer(prio, p, strlen(p));
}
- static void ParseDebugVulkanLayer(const char* key,
- const char* val,
- void* user_data) {
+ void ParseDebugVulkanLayer() {
+ // Checks for consecutive debug.vulkan.layer.<priority> system
+ // properties after always checking an initial fixed range.
static const char prefix[] = "debug.vulkan.layer.";
- const size_t prefix_len = sizeof(prefix) - 1;
+ static constexpr int kFixedRangeBeginInclusive = 0;
+ static constexpr int kFixedRangeEndInclusive = 9;
- if (strncmp(key, prefix, prefix_len) || val[0] == '\0')
- return;
- key += prefix_len;
+ bool logged = false;
- // debug.vulkan.layer.<priority>
- int priority = -1;
- if (key[0] >= '0' && key[0] <= '9')
- priority = atoi(key);
+ int priority = kFixedRangeBeginInclusive;
+ while (true) {
+ const std::string prop_key =
+ std::string(prefix) + std::to_string(priority);
+ const std::string prop_val =
+ android::base::GetProperty(prop_key, "");
- if (priority < 0) {
- ALOGW("Ignored implicit layer %s with invalid priority %s", val,
- key);
- return;
+ if (!prop_val.empty()) {
+ if (!logged) {
+ ALOGI(
+ "Detected Vulkan layers configured with "
+ "debug.vulkan.layer.<priority>. Checking for "
+ "debug.vulkan.layer.<priority> in the range [%d, %d] "
+ "followed by a consecutive scan.",
+ kFixedRangeBeginInclusive, kFixedRangeEndInclusive);
+ logged = true;
+ }
+ AddImplicitLayer(priority, prop_val.c_str(), prop_val.length());
+ } else if (priority >= kFixedRangeEndInclusive) {
+ return;
+ }
+
+ ++priority;
}
-
- OverrideLayerNames& override_layers =
- *reinterpret_cast<OverrideLayerNames*>(user_data);
- override_layers.AddImplicitLayer(priority, val, strlen(val));
}
void AddImplicitLayer(int priority, const char* name, size_t len) {