Merge "Fix the missing extension EGL_ANDROID_image_native_buffer" into udc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8ca927e..5dbf7ac 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3362,23 +3362,25 @@
return;
}
- // Include the proto logging from WMShell.
- RunCommand(
- // Empty name because it's not intended to be classified as a bugreport section.
- // Actual logging files can be found as "/data/misc/wmtrace/shell_log.winscope"
- // in the bugreport.
- "", {"dumpsys", "activity", "service", "SystemUIService",
- "WMShell", "protolog", "save-for-bugreport"},
- CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
+ const std::vector<std::vector<std::string>> dumpTracesForBugReportCommands = {
+ {"dumpsys", "activity", "service", "SystemUIService", "WMShell", "protolog",
+ "save-for-bugreport"},
+ {"dumpsys", "activity", "service", "SystemUIService", "WMShell", "transitions", "tracing",
+ "save-for-bugreport"},
+ {"cmd", "input_method", "tracing", "save-for-bugreport"},
+ {"cmd", "window", "tracing", "save-for-bugreport"},
+ {"cmd", "window", "shell", "tracing", "save-for-bugreport"},
+ };
- for (const auto& service : {"input_method", "window", "window shell"}) {
+ for (const auto& command : dumpTracesForBugReportCommands) {
RunCommand(
// Empty name because it's not intended to be classified as a bugreport section.
// Actual tracing files can be found in "/data/misc/wmtrace/" in the bugreport.
- "", {"cmd", service, "tracing", "save-for-bugreport"},
+ "", command,
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
}
+ // This command needs to be run as root
static const auto SURFACEFLINGER_COMMAND_SAVE_ALL_TRACES = std::vector<std::string> {
"service", "call", "SurfaceFlinger", "1042"
};
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 93d8cdf..5cbcf9f 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -997,17 +997,22 @@
EXPECT_FALSE(ds.dump_pool_);
}
-TEST_F(DumpstateBaseTest, PreDumpUiData) {
- // SurfaceFlinger's transactions trace is always enabled, i.e. it is always pre-dumped
- static const auto kTransactionsTrace =
- std::filesystem::path {"/data/misc/wmtrace/transactions_trace.winscope"};
+TEST_F(DumpstateTest, PreDumpUiData) {
+ // These traces are always enabled, i.e. they are always pre-dumped
+ const std::vector<std::filesystem::path> uiTraces = {
+ std::filesystem::path{"/data/misc/wmtrace/transactions_trace.winscope"},
+ std::filesystem::path{"/data/misc/wmtrace/transition_trace.winscope"},
+ std::filesystem::path{"/data/misc/wmtrace/shell_transition_trace.winscope"},
+ };
- std::system(("rm " + kTransactionsTrace.string()).c_str());
- EXPECT_FALSE(std::filesystem::exists(kTransactionsTrace));
+ for (const auto traceFile : uiTraces) {
+ std::system(("rm -f " + traceFile.string()).c_str());
+ EXPECT_FALSE(std::filesystem::exists(traceFile)) << traceFile << " was not deleted.";
- Dumpstate& ds_ = Dumpstate::GetInstance();
- ds_.PreDumpUiData();
- EXPECT_TRUE(std::filesystem::exists(kTransactionsTrace));
+ Dumpstate& ds_ = Dumpstate::GetInstance();
+ ds_.PreDumpUiData();
+ EXPECT_TRUE(std::filesystem::exists(traceFile)) << traceFile << " was not created.";
+ }
}
class ZippedBugReportStreamTest : public DumpstateBaseTest {
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp
index a6025e6..85ce450 100644
--- a/cmds/installd/utils_default.cpp
+++ b/cmds/installd/utils_default.cpp
@@ -23,7 +23,7 @@
// platform dependent logic.
int rm_package_dir(const std::string& package_dir) {
- return delete_dir_contents_and_dir(package_dir);
+ return rename_delete_dir_contents_and_dir(package_dir);
}
} // namespace installd
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index d73a30b..fb69513 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -93,9 +93,22 @@
libfuzzer_options: [
"max_len=50000",
],
- cc: [
- "smoreland@google.com",
- "waghpawan@google.com",
+ },
+}
+
+// Adding this new fuzzer to test the corpus generated by record_binder
+cc_fuzz {
+ name: "servicemanager_test_fuzzer",
+ defaults: [
+ "servicemanager_defaults",
+ "service_fuzzer_defaults",
+ ],
+ host_supported: true,
+ srcs: ["fuzzers/ServiceManagerTestFuzzer.cpp"],
+ fuzz_config: {
+ libfuzzer_options: [
+ "max_len=50000",
],
},
+ corpus: ["fuzzers/servicemamanager_fuzzer_corpus/*"],
}
diff --git a/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
new file mode 100644
index 0000000..e19b6eb
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/ServiceManagerTestFuzzer.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzbinder/libbinder_driver.h>
+#include <utils/StrongPointer.h>
+
+#include "Access.h"
+#include "ServiceManager.h"
+
+using ::android::Access;
+using ::android::Parcel;
+using ::android::ServiceManager;
+using ::android::sp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider provider(data, size);
+ auto accessPtr = std::make_unique<Access>();
+ auto serviceManager = sp<ServiceManager>::make(std::move(accessPtr));
+
+ // Reserved bytes
+ provider.ConsumeBytes<uint8_t>(8);
+ uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ uint32_t flag = provider.ConsumeIntegral<uint32_t>();
+ std::vector<uint8_t> parcelData = provider.ConsumeRemainingBytes<uint8_t>();
+
+ Parcel inputParcel;
+ inputParcel.setData(parcelData.data(), parcelData.size());
+
+ Parcel reply;
+ serviceManager->transact(code, inputParcel, &reply, flag);
+
+ serviceManager->clear();
+
+ return 0;
+}
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_1
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_10
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_11
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_12
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_13
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_14
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_15
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_16
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_17
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_18
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_19
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
new file mode 100644
index 0000000..e69ab49
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_2
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_20
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_21
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_22
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_23
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_24
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_25
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_26
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_27
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_28
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_29
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_3
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_30
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_31
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_32
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_33
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_34
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_35
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
new file mode 100644
index 0000000..88ad474
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_36
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
new file mode 100644
index 0000000..fae15a2
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_37
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_38
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
new file mode 100644
index 0000000..b326907
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_39
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
new file mode 100644
index 0000000..05b27bf
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_4
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_40
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
new file mode 100644
index 0000000..b326907
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_41
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
new file mode 100644
index 0000000..cdaa1f0
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_42
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
new file mode 100644
index 0000000..ff0941b
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_43
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
new file mode 100644
index 0000000..cdaa1f0
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_44
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_45
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
new file mode 100644
index 0000000..7e5f948
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_46
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_5
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_6
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_7
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
new file mode 100644
index 0000000..07319f8
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_8
Binary files differ
diff --git a/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9 b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
new file mode 100644
index 0000000..39e5104
--- /dev/null
+++ b/cmds/servicemanager/fuzzers/servicemanager_fuzzer_corpus/Transaction_9
Binary files differ
diff --git a/headers/media_plugin/media/hardware/VideoAPI.h b/headers/media_plugin/media/hardware/VideoAPI.h
index a090876..5466680 100644
--- a/headers/media_plugin/media/hardware/VideoAPI.h
+++ b/headers/media_plugin/media/hardware/VideoAPI.h
@@ -127,6 +127,8 @@
PrimariesBT601_6_525, // Rec.ITU-R BT.601-6 525 or equivalent
PrimariesGenericFilm, // Generic Film
PrimariesBT2020, // Rec.ITU-R BT.2020 or equivalent
+ PrimariesRP431, // SMPTE RP 431-2 (DCI P3)
+ PrimariesEG432, // SMPTE EG 432-1 (Display P3)
PrimariesOther = 0xff,
};
@@ -173,6 +175,8 @@
StandardBT2020Constant, // PrimariesBT2020 and MatrixBT2020Constant
StandardBT470M, // PrimariesBT470_6M and MatrixBT470_6M
StandardFilm, // PrimariesGenericFilm and KR=0.253, KB=0.068
+ StandardDisplayP3, // PrimariesEG432 and MatrixBT601_6
+ // StandardAdobeRGB, // for placeholder only (not used by media)
StandardOther = 0xff,
};
@@ -282,6 +286,8 @@
case ColorAspects::PrimariesBT601_6_525: return "BT601_6_525";
case ColorAspects::PrimariesGenericFilm: return "GenericFilm";
case ColorAspects::PrimariesBT2020: return "BT2020";
+ case ColorAspects::PrimariesRP431: return "RP431";
+ case ColorAspects::PrimariesEG432: return "EG432";
case ColorAspects::PrimariesOther: return "Other";
default: return def;
}
@@ -332,6 +338,8 @@
case ColorAspects::StandardBT2020Constant: return "BT2020Constant";
case ColorAspects::StandardBT470M: return "BT470M";
case ColorAspects::StandardFilm: return "Film";
+ case ColorAspects::StandardDisplayP3: return "DisplayP3";
+ // case ColorAspects::StandardAdobeRGB: return "AdobeRGB";
case ColorAspects::StandardOther: return "Other";
default: return def;
}
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index daeebec..cce2e46 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -54,6 +54,12 @@
* The caller takes ownership of the ASurfaceControl returned and must release it
* using ASurfaceControl_release below.
*
+ * By default the \a ASurfaceControl will be visible and display any buffer submitted. In
+ * addition, the default buffer submission control may release and not display all buffers
+ * that are submitted before receiving a callback for the previous buffer. See
+ * \a ASurfaceTransaction_setVisibility and \a ASurfaceTransaction_setEnableBackPressure to
+ * change the default behaviors after creation.
+ *
* Available since API level 29.
*/
ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
@@ -548,11 +554,15 @@
* to the max display brightness. The system may not be able to, or may choose
* not to, deliver the requested range.
*
- * If unspecified, the system will attempt to provide the best range it can
- * for the given ambient conditions & device state. However, voluntarily
- * reducing the requested range can help improve battery life as well as can
- * improve quality by ensuring greater bit depth is allocated to the luminance
- * range in use.
+ * While requesting a large desired ratio will result in the most
+ * dynamic range, voluntarily reducing the requested range can help
+ * improve battery life as well as can improve quality by ensuring
+ * greater bit depth is allocated to the luminance range in use.
+ *
+ * Default value is 1.0f and indicates that extended range brightness
+ * is not being used, so the resulting SDR or HDR behavior will be
+ * determined entirely by the dataspace being used (ie, typically SDR
+ * however PQ or HLG transfer functions will still result in HDR)
*
* Must be finite && >= 1.0f
*
diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h
index c5ff03b..c0f6768 100644
--- a/include/ftl/algorithm.h
+++ b/include/ftl/algorithm.h
@@ -68,4 +68,29 @@
return std::cref(pair.second);
}
+// Combinator for ftl::Optional<T>::or_else when T is std::reference_wrapper<const V>. Given a
+// lambda argument that returns a `constexpr` value, ftl::static_ref<T> binds a reference to a
+// static T initialized to that constant.
+//
+// const ftl::SmallMap map = ftl::init::map(13, "tiramisu"sv)(14, "upside-down cake"sv);
+// assert("???"sv ==
+// map.get(20).or_else(ftl::static_ref<std::string_view>([] { return "???"sv; }))->get());
+//
+// using Map = decltype(map);
+//
+// assert("snow cone"sv ==
+// ftl::find_if(map, [](const auto& pair) { return pair.second.front() == 's'; })
+// .transform(ftl::to_mapped_ref<Map>)
+// .or_else(ftl::static_ref<std::string_view>([] { return "snow cone"sv; }))
+// ->get());
+//
+template <typename T, typename F>
+constexpr auto static_ref(F&& f) {
+ return [f = std::forward<F>(f)] {
+ constexpr auto kInitializer = f();
+ static const T kValue = kInitializer;
+ return Optional(std::cref(kValue));
+ };
+}
+
} // namespace android::ftl
diff --git a/include/ftl/flags.h b/include/ftl/flags.h
index cdb4e84..dbe3148 100644
--- a/include/ftl/flags.h
+++ b/include/ftl/flags.h
@@ -120,7 +120,7 @@
}
/* Tests whether any of the given flags are set */
- bool any(Flags<F> f) const { return (mFlags & f.mFlags) != 0; }
+ bool any(Flags<F> f = ~Flags<F>()) const { return (mFlags & f.mFlags) != 0; }
/* Tests whether all of the given flags are set */
bool all(Flags<F> f) const { return (mFlags & f.mFlags) == f.mFlags; }
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 66d3435..1a40fdb 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -212,6 +212,11 @@
std::string languageTag;
// The layout type such as QWERTY or AZERTY.
std::string layoutType;
+
+ inline bool operator==(const KeyboardLayoutInfo& other) const {
+ return languageTag == other.languageTag && layoutType == other.layoutType;
+ }
+ inline bool operator!=(const KeyboardLayoutInfo& other) const { return !(*this == other); }
};
// The version of the Universal Stylus Initiative (USI) protocol supported by the input device.
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 9423041..b2e8baa 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -26,9 +26,9 @@
#include <android-base/result.h>
#include <input/Input.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
#include <utils/Tokenizer.h>
#include <utils/Unicode.h>
+#include <map>
// Maximum number of keys supported by KeyCharacterMaps
#define MAX_KEYS 8192
@@ -152,13 +152,9 @@
void writeToParcel(Parcel* parcel) const;
#endif
- bool operator==(const KeyCharacterMap& other) const;
+ bool operator==(const KeyCharacterMap& other) const = default;
- bool operator!=(const KeyCharacterMap& other) const;
-
- KeyCharacterMap(const KeyCharacterMap& other);
-
- virtual ~KeyCharacterMap();
+ KeyCharacterMap(const KeyCharacterMap& other) = default;
private:
struct Behavior {
@@ -173,17 +169,18 @@
/* The replacement keycode if the key has to be replaced outright. */
int32_t replacementKeyCode = 0;
+
+ bool operator==(const Behavior&) const = default;
};
struct Key {
- Key();
- Key(const Key& other);
+ bool operator==(const Key&) const = default;
/* The single character label printed on the key, or 0 if none. */
- char16_t label;
+ char16_t label = 0;
/* The number or symbol character generated by the key, or 0 if none. */
- char16_t number;
+ char16_t number = 0;
/* The list of key behaviors sorted from most specific to least specific
* meta key binding. */
@@ -218,7 +215,6 @@
public:
Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
- ~Parser();
status_t parse();
private:
@@ -232,8 +228,8 @@
status_t parseCharacterLiteral(char16_t* outCharacter);
};
- KeyedVector<int32_t, Key*> mKeys;
- KeyboardType mType;
+ std::map<int32_t, Key> mKeys;
+ KeyboardType mType = KeyboardType::UNKNOWN;
std::string mLoadFileName;
bool mLayoutOverlayApplied = false;
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index 13ffb58..21a2877 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -34,10 +34,12 @@
protected:
const android::base::unique_fd mFd;
- bool writeInputEvent(uint16_t type, uint16_t code, int32_t value);
+ bool writeInputEvent(uint16_t type, uint16_t code, int32_t value,
+ std::chrono::nanoseconds eventTime);
bool writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
const std::map<int, int>& evKeyCodeMapping,
- const std::map<int, UinputAction>& actionMapping);
+ const std::map<int, UinputAction>& actionMapping,
+ std::chrono::nanoseconds eventTime);
};
class VirtualKeyboard : public VirtualInputDevice {
@@ -47,7 +49,8 @@
static const std::map<int, UinputAction> KEY_ACTION_MAPPING;
VirtualKeyboard(android::base::unique_fd fd);
virtual ~VirtualKeyboard() override;
- bool writeKeyEvent(int32_t androidKeyCode, int32_t androidAction);
+ bool writeKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime);
};
class VirtualDpad : public VirtualInputDevice {
@@ -55,17 +58,20 @@
static const std::map<int, int> DPAD_KEY_CODE_MAPPING;
VirtualDpad(android::base::unique_fd fd);
virtual ~VirtualDpad() override;
- bool writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction);
+ bool writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime);
};
class VirtualMouse : public VirtualInputDevice {
public:
VirtualMouse(android::base::unique_fd fd);
virtual ~VirtualMouse() override;
- bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction);
+ bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime);
// TODO(b/259554911): changing float parameters to int32_t.
- bool writeRelativeEvent(float relativeX, float relativeY);
- bool writeScrollEvent(float xAxisMovement, float yAxisMovement);
+ bool writeRelativeEvent(float relativeX, float relativeY, std::chrono::nanoseconds eventTime);
+ bool writeScrollEvent(float xAxisMovement, float yAxisMovement,
+ std::chrono::nanoseconds eventTime);
private:
static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING;
@@ -78,7 +84,8 @@
virtual ~VirtualTouchscreen() override;
// TODO(b/259554911): changing float parameters to int32_t.
bool writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action, float locationX,
- float locationY, float pressure, float majorAxisSize);
+ float locationY, float pressure, float majorAxisSize,
+ std::chrono::nanoseconds eventTime);
private:
static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING;
@@ -91,7 +98,7 @@
*/
std::bitset<MAX_POINTERS> mActivePointers{};
bool isValidPointerId(int32_t pointerId, UinputAction uinputAction);
- bool handleTouchDown(int32_t pointerId);
- bool handleTouchUp(int32_t pointerId);
+ bool handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime);
+ bool handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime);
};
} // namespace android
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index e45a656..aca5009 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -75,6 +75,20 @@
return DEAD_OBJECT;
}
+status_t ActivityManager::registerUidObserverForUids(const sp<IUidObserver>& observer,
+ const int32_t event, const int32_t cutpoint,
+ const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->registerUidObserverForUids(observer, event, cutpoint, callingPackage, uids,
+ nUids, observerToken);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
status_t ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer)
{
sp<IActivityManager> service = getService();
@@ -85,6 +99,26 @@
return DEAD_OBJECT;
}
+status_t ActivityManager::addUidToObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->addUidToObserver(observerToken, callingPackage, uid);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
+status_t ActivityManager::removeUidFromObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->removeUidFromObserver(observerToken, callingPackage, uid);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage)
{
sp<IActivityManager> service = getService();
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 53852d8..8d9955d 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -47,7 +47,7 @@
binder_proxy_limit_callback BpBinder::sLimitCallback;
bool BpBinder::sBinderProxyThrottleCreate = false;
-static StaticString16 kDescriptorUninit(u"<uninit descriptor>");
+static StaticString16 kDescriptorUninit(u"");
// Arbitrarily high value that probably distinguishes a bad behaving app
uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500;
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index ebdaa4c..84900a7 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -77,6 +77,30 @@
return OK;
}
+ virtual status_t registerUidObserverForUids(const sp<IUidObserver>& observer,
+ const int32_t event, const int32_t cutpoint,
+ const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(observer));
+ data.writeInt32(event);
+ data.writeInt32(cutpoint);
+ data.writeString16(callingPackage);
+ data.writeInt32Array(nUids, uids);
+ status_t err =
+ remote()->transact(REGISTER_UID_OBSERVER_FOR_UIDS_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ err = reply.readStrongBinder(&observerToken);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
+ }
+
virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer)
{
Parcel data, reply;
@@ -89,6 +113,34 @@
return OK;
}
+ virtual status_t addUidToObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeStrongBinder(observerToken);
+ data.writeString16(callingPackage);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(ADD_UID_TO_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
+ }
+
+ virtual status_t removeUidFromObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeStrongBinder(observerToken);
+ data.writeString16(callingPackage);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(REMOVE_UID_FROM_OBSERVER_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ return OK;
+ }
+
virtual bool isUidActive(const uid_t uid, const String16& callingPackage)
{
Parcel data, reply;
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index d952dc7..1c35f53 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -67,9 +67,10 @@
remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual void onUidProcAdjChanged(uid_t uid) {
+ virtual void onUidProcAdjChanged(uid_t uid, int32_t adj) {
Parcel data, reply;
data.writeInt32((int32_t)uid);
+ data.writeInt32((int32_t)adj);
remote()->transact(ON_UID_PROC_ADJ_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -121,7 +122,8 @@
case ON_UID_PROC_ADJ_CHANGED_TRANSACTION: {
CHECK_INTERFACE(IUidObserver, data, reply);
uid_t uid = data.readInt32();
- onUidProcAdjChanged(uid);
+ int32_t adj = data.readInt32();
+ onUidProcAdjChanged(uid, adj);
return NO_ERROR;
} break;
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index ed3ce24..03fa699 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -928,7 +928,7 @@
transactionData.size() -
offsetof(RpcWireTransaction, data)};
Span<const uint32_t> objectTableSpan;
- if (session->getProtocolVersion().value() >
+ if (session->getProtocolVersion().value() >=
RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
std::optional<Span<const uint8_t>> objectTableBytes =
parcelSpan.splitOff(transaction->parcelDataSize);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 41707d4..0e8e187 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -92,12 +92,6 @@
"name": "binderRpcTest"
},
{
- "name": "CtsRootRollbackManagerHostTestCases"
- },
- {
- "name": "StagedRollbackTest"
- },
- {
"name": "binderRpcTestNoKernel"
},
{
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index a323feb..cb64603 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -37,9 +37,9 @@
class RpcTransport;
class FdTrigger;
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 2;
constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000;
-constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL;
+constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 1;
// Starting with this version:
//
diff --git a/libs/binder/include_activitymanager/binder/ActivityManager.h b/libs/binder/include_activitymanager/binder/ActivityManager.h
index 5dfbd44..9c634c7 100644
--- a/libs/binder/include_activitymanager/binder/ActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/ActivityManager.h
@@ -82,7 +82,15 @@
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage);
+ status_t registerUidObserverForUids(const sp<IUidObserver>& observer, const int32_t event,
+ const int32_t cutpoint, const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken);
status_t unregisterUidObserver(const sp<IUidObserver>& observer);
+ status_t addUidToObserver(const sp<IBinder>& observerToken, const String16& callingPackage,
+ int32_t uid);
+ status_t removeUidFromObserver(const sp<IBinder>& observerToken, const String16& callingPackage,
+ int32_t uid);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
diff --git a/libs/binder/include_activitymanager/binder/IActivityManager.h b/libs/binder/include_activitymanager/binder/IActivityManager.h
index 20d12ae..07450c6 100644
--- a/libs/binder/include_activitymanager/binder/IActivityManager.h
+++ b/libs/binder/include_activitymanager/binder/IActivityManager.h
@@ -35,7 +35,16 @@
const int32_t event,
const int32_t cutpoint,
const String16& callingPackage) = 0;
+ virtual status_t registerUidObserverForUids(const sp<IUidObserver>& observer,
+ const int32_t event, const int32_t cutpoint,
+ const String16& callingPackage,
+ const int32_t uids[], size_t nUids,
+ /*out*/ sp<IBinder>& observerToken) = 0;
virtual status_t unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
+ virtual status_t addUidToObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) = 0;
+ virtual status_t removeUidFromObserver(const sp<IBinder>& observerToken,
+ const String16& callingPackage, int32_t uid) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
virtual status_t checkPermission(const String16& permission,
@@ -51,6 +60,9 @@
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
+ REGISTER_UID_OBSERVER_FOR_UIDS_TRANSACTION,
+ ADD_UID_TO_OBSERVER_TRANSACTION,
+ REMOVE_UID_FROM_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
GET_UID_PROCESS_STATE_TRANSACTION,
CHECK_PERMISSION_TRANSACTION,
diff --git a/libs/binder/include_activitymanager/binder/IUidObserver.h b/libs/binder/include_activitymanager/binder/IUidObserver.h
index 17f03a9..5ea7447 100644
--- a/libs/binder/include_activitymanager/binder/IUidObserver.h
+++ b/libs/binder/include_activitymanager/binder/IUidObserver.h
@@ -34,7 +34,7 @@
virtual void onUidIdle(uid_t uid, bool disabled) = 0;
virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
int32_t capability) = 0;
- virtual void onUidProcAdjChanged(uid_t uid) = 0;
+ virtual void onUidProcAdjChanged(uid_t uid, int32_t adj) = 0;
enum {
ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp
index 3dab2c7..642cea4 100644
--- a/libs/binder/tests/binderRpcWireProtocolTest.cpp
+++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp
@@ -237,14 +237,25 @@
checkRepr(kCurrentRepr, 0);
}
+TEST(RpcWire, V1) {
+ checkRepr(kCurrentRepr, 1);
+}
+
TEST(RpcWire, CurrentVersion) {
checkRepr(kCurrentRepr, RPC_WIRE_PROTOCOL_VERSION);
}
-static_assert(RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL,
+static_assert(RPC_WIRE_PROTOCOL_VERSION == 1,
"If the binder wire protocol is updated, this test should test additional versions. "
"The binder wire protocol should only be updated on upstream AOSP.");
+TEST(RpcWire, NextIsPlusOneReminder) {
+ if (RPC_WIRE_PROTOCOL_VERSION != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+ EXPECT_EQ(RPC_WIRE_PROTOCOL_VERSION + 1, RPC_WIRE_PROTOCOL_VERSION_NEXT)
+ << "Make sure to note what the next version should be.";
+ }
+}
+
TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) {
if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
EXPECT_FALSE(base::GetProperty("ro.build.version.codename", "") == "REL")
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 97cb810..5eb3308 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -62,7 +62,10 @@
"android.hardware.audio@7.0::IDevicesFactory",
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
"android.hardware.automotive.audiocontrol@2.0::IAudioControl",
+ "android.hardware.automotive.can@1.0::ICanBus",
+ "android.hardware.automotive.can@1.0::ICanController",
"android.hardware.automotive.evs@1.0::IEvsCamera",
+ "android.hardware.automotive.sv@1.0::ISurroundViewService",
"android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
@@ -87,7 +90,12 @@
/* list of hal interface to dump containing process during native dumps */
static const std::vector<std::string> aidl_interfaces_to_dump {
"android.hardware.automotive.audiocontrol.IAudioControl",
+ "android.hardware.automotive.can.ICanController",
"android.hardware.automotive.evs.IEvsEnumerator",
+ "android.hardware.automotive.ivn.IIvnAndroidDevice",
+ "android.hardware.automotive.occupant_awareness.IOccupantAwareness",
+ "android.hardware.automotive.remoteaccess.IRemoteAccess",
+ "android.hardware.automotive.vehicle.IVehicle",
"android.hardware.biometrics.face.IBiometricsFace",
"android.hardware.biometrics.fingerprint.IBiometricsFingerprint",
"android.hardware.camera.provider.ICameraProvider",
diff --git a/libs/ftl/algorithm_test.cpp b/libs/ftl/algorithm_test.cpp
index 8052caf..487b1b8 100644
--- a/libs/ftl/algorithm_test.cpp
+++ b/libs/ftl/algorithm_test.cpp
@@ -47,4 +47,20 @@
EXPECT_EQ(opt->get(), ftl::StaticVector("tiramisu"sv));
}
+TEST(Algorithm, StaticRef) {
+ using namespace std::string_view_literals;
+
+ const ftl::SmallMap map = ftl::init::map(13, "tiramisu"sv)(14, "upside-down cake"sv);
+ ASSERT_EQ("???"sv,
+ map.get(20).or_else(ftl::static_ref<std::string_view>([] { return "???"sv; }))->get());
+
+ using Map = decltype(map);
+
+ ASSERT_EQ("snow cone"sv,
+ ftl::find_if(map, [](const auto& pair) { return pair.second.front() == 's'; })
+ .transform(ftl::to_mapped_ref<Map>)
+ .or_else(ftl::static_ref<std::string_view>([] { return "snow cone"sv; }))
+ ->get());
+}
+
} // namespace android::test
diff --git a/libs/ftl/flags_test.cpp b/libs/ftl/flags_test.cpp
index eea052b..1279d11 100644
--- a/libs/ftl/flags_test.cpp
+++ b/libs/ftl/flags_test.cpp
@@ -35,6 +35,7 @@
TEST(Flags, Any) {
Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO;
+ ASSERT_TRUE(flags.any());
ASSERT_TRUE(flags.any(TestFlags::ONE));
ASSERT_TRUE(flags.any(TestFlags::TWO));
ASSERT_FALSE(flags.any(TestFlags::THREE));
@@ -42,6 +43,9 @@
ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE));
ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE));
ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE));
+
+ Flags<TestFlags> emptyFlags;
+ ASSERT_FALSE(emptyFlags.any());
}
TEST(Flags, All) {
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index c480056..5fbae3c 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -433,61 +433,24 @@
return (mUseAngle == YES) ? true : false;
}
-bool GraphicsEnv::angleIsSystemDriver() {
- // Make sure we are init'ed
- if (mAngleAppName.empty()) {
- ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
- return false;
- }
-
- return (mAngleIsSystemDriver == YES) ? true : false;
-}
-
-bool GraphicsEnv::shouldForceLegacyDriver() {
- // Make sure we are init'ed
- if (mAngleAppName.empty()) {
- ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE.");
- return false;
- }
-
- return (mAngleIsSystemDriver == YES && mUseAngle == NO) ? true : false;
-}
-
-std::string GraphicsEnv::getLegacySuffix() {
- return mLegacyDriverSuffix;
-}
-
void GraphicsEnv::updateUseAngle() {
- mUseAngle = NO;
-
const char* ANGLE_PREFER_ANGLE = "angle";
- const char* ANGLE_PREFER_LEGACY = "legacy";
- // The following is a deprecated version of "legacy"
const char* ANGLE_PREFER_NATIVE = "native";
mUseAngle = NO;
if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
- ALOGI("Using ANGLE, the %s GLES driver for package '%s'",
- mAngleIsSystemDriver == YES ? "system" : "optional", mAngleAppName.c_str());
+ ALOGV("User set \"Developer Options\" to force the use of ANGLE");
mUseAngle = YES;
- } else if (mAngleDeveloperOptIn == ANGLE_PREFER_LEGACY ||
- mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
- ALOGI("Using the (%s) Legacy GLES driver for package '%s'",
- mAngleIsSystemDriver == YES ? "optional" : "system", mAngleAppName.c_str());
+ } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
+ ALOGV("User set \"Developer Options\" to force the use of Native");
} else {
ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
}
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const bool angleIsSystemDriver, const std::string developerOptIn,
+ const std::string developerOptIn,
const std::vector<std::string> eglFeatures) {
- // Set whether ANGLE is the system driver:
- mAngleIsSystemDriver = angleIsSystemDriver ? YES : NO;
-
- // Note: Given the current logic and lack of the old rules file processing,
- // there seems to be little chance that mUseAngle != UNKNOWN. Leave this
- // for now, even though it seems outdated.
if (mUseAngle != UNKNOWN) {
// We've already figured out an answer for this app, so just return.
ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -508,25 +471,6 @@
updateUseAngle();
}
-void GraphicsEnv::setLegacyDriverInfo(const std::string appName, const bool angleIsSystemDriver,
- const std::string legacyDriverName) {
- ALOGV("setting legacy app name to '%s'", appName.c_str());
- mAngleAppName = appName;
-
- // Force the use of the legacy driver instead of ANGLE
- const char* ANGLE_PREFER_LEGACY = "legacy";
- mAngleDeveloperOptIn = ANGLE_PREFER_LEGACY;
- ALOGV("setting ANGLE application opt-in to 'legacy'");
-
- // Set whether ANGLE is the system driver:
- mAngleIsSystemDriver = angleIsSystemDriver ? YES : NO;
-
- mLegacyDriverSuffix = legacyDriverName;
-
- // Update the current status of whether we should use ANGLE or not
- updateUseAngle();
-}
-
void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
if (mLayerPaths.empty()) {
mLayerPaths = layerPaths;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 1274c46..f9b234a 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -100,28 +100,17 @@
bool shouldUseAngle(std::string appName);
// Check if this app process should use ANGLE.
bool shouldUseAngle();
- // If ANGLE is the system GLES driver
- bool angleIsSystemDriver();
- // If should use legacy driver instead of a system ANGLE driver
- bool shouldForceLegacyDriver();
// Set a search path for loading ANGLE libraries. The path is a list of
// directories separated by ':'. A directory can be contained in a zip file
// (libraries must be stored uncompressed and page aligned); such elements
// in the search path must have a '!' after the zip filename, e.g.
// /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
- void setAngleInfo(const std::string path, const std::string appName,
- const bool angleIsSystemDriver, std::string devOptIn,
+ void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
const std::vector<std::string> eglFeatures);
- // Set the state so that the legacy driver will be used, and in case ANGLE
- // is the system driver, provide the name of the legacy driver.
- void setLegacyDriverInfo(const std::string appName, const bool angleIsSystemDriver,
- const std::string legacyDriverName);
// Get the ANGLE driver namespace.
android_namespace_t* getAngleNamespace();
// Get the app name for ANGLE debug message.
std::string& getAngleAppName();
- // Get the legacy driver's suffix name.
- std::string getLegacySuffix();
const std::vector<std::string>& getAngleEglFeatures();
@@ -178,10 +167,6 @@
std::string mAngleDeveloperOptIn;
// ANGLE EGL features;
std::vector<std::string> mAngleEglFeatures;
- // ANGLE is System Driver flag.
- UseAngle mAngleIsSystemDriver = UNKNOWN;
- // Legacy driver name to use when ANGLE is the system driver.
- std::string mLegacyDriverSuffix;
// Use ANGLE flag.
UseAngle mUseAngle = UNKNOWN;
// Vulkan debug layers libs.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 21900a0..33bb343 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -66,6 +66,19 @@
],
}
+filegroup {
+ name: "android_gui_aidl",
+ srcs: [
+ "android/gui/DisplayInfo.aidl",
+ "android/gui/FocusRequest.aidl",
+ "android/gui/InputApplicationInfo.aidl",
+ "android/gui/IWindowInfosListener.aidl",
+ "android/gui/IWindowInfosReportedListener.aidl",
+ "android/gui/WindowInfo.aidl",
+ "android/gui/WindowInfosUpdate.aidl",
+ ],
+}
+
cc_library_static {
name: "libgui_window_info_static",
vendor_available: true,
@@ -78,9 +91,11 @@
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
+ "android/gui/WindowInfosUpdate.aidl",
"android/gui/WindowInfo.aidl",
"DisplayInfo.cpp",
"WindowInfo.cpp",
+ "WindowInfosUpdate.cpp",
],
shared_libs: [
@@ -118,6 +133,9 @@
name: "libgui_aidl",
srcs: ["aidl/**/*.aidl"],
path: "aidl/",
+ aidl: {
+ deps: [":android_gui_aidl"],
+ },
}
filegroup {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 7f7a043..5217209 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -33,6 +33,7 @@
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
+#include <gui/TraceUtils.h>
#include <private/gui/BufferQueueThreadState.h>
#ifndef __ANDROID_VNDK__
@@ -646,7 +647,7 @@
status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
int maxAcquiredBuffers) {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers);
if (maxAcquiredBuffers < 1 ||
maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9eb1a9f..9a2343b 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -35,6 +35,7 @@
#include <gui/GLConsumer.h>
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
+#include <gui/TraceUtils.h>
#include <private/gui/BufferQueueThreadState.h>
#include <utils/Log.h>
@@ -125,7 +126,7 @@
status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers,
int* maxBufferCount) {
- ATRACE_CALL();
+ ATRACE_FORMAT("%s(%d)", __func__, maxDequeuedBuffers);
BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d",
maxDequeuedBuffers);
@@ -502,6 +503,20 @@
if ((buffer == nullptr) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
{
+ if (CC_UNLIKELY(ATRACE_ENABLED())) {
+ if (buffer == nullptr) {
+ ATRACE_FORMAT_INSTANT("%s buffer reallocation: null", mConsumerName.string());
+ } else {
+ ATRACE_FORMAT_INSTANT("%s buffer reallocation actual %dx%d format:%d "
+ "layerCount:%d "
+ "usage:%d requested: %dx%d format:%d layerCount:%d "
+ "usage:%d ",
+ mConsumerName.string(), width, height, format,
+ BQ_LAYER_COUNT, usage, buffer->getWidth(),
+ buffer->getHeight(), buffer->getPixelFormat(),
+ buffer->getLayerCount(), buffer->getUsage());
+ }
+ }
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = nullptr;
mSlots[found].mRequestBufferCalled = false;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index fee91a4..2322b70 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -897,11 +897,11 @@
SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace));
SAFE_PARCEL(output->writeBool, allowProtected);
SAFE_PARCEL(output->writeBool, grayscale);
-
SAFE_PARCEL(output->writeInt32, excludeHandles.size());
for (auto& excludeHandle : excludeHandles) {
SAFE_PARCEL(output->writeStrongBinder, excludeHandle);
}
+ SAFE_PARCEL(output->writeBool, hintForSeamlessTransition);
return NO_ERROR;
}
@@ -918,7 +918,6 @@
dataspace = static_cast<ui::Dataspace>(value);
SAFE_PARCEL(input->readBool, &allowProtected);
SAFE_PARCEL(input->readBool, &grayscale);
-
int32_t numExcludeHandles = 0;
SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize());
excludeHandles.reserve(numExcludeHandles);
@@ -927,6 +926,7 @@
SAFE_PARCEL(input->readStrongBinder, &binder);
excludeHandles.emplace(binder);
}
+ SAFE_PARCEL(input->readBool, &hintForSeamlessTransition);
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7700aa4..eb5cc4f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -898,7 +898,7 @@
}
void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) {
- if (!(state.what & layer_state_t::eBufferChanged)) {
+ if (!(state.what & layer_state_t::eBufferChanged) || !state.bufferData->hasBuffer()) {
return;
}
@@ -1642,28 +1642,25 @@
releaseBufferIfOverwriting(*s);
- if (buffer == nullptr) {
- s->what &= ~layer_state_t::eBufferChanged;
- s->bufferData = nullptr;
- return *this;
- }
-
std::shared_ptr<BufferData> bufferData = std::make_shared<BufferData>();
bufferData->buffer = buffer;
- uint64_t frameNumber = sc->resolveFrameNumber(optFrameNumber);
- bufferData->frameNumber = frameNumber;
- bufferData->producerId = producerId;
- bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
- if (fence) {
- bufferData->acquireFence = *fence;
- bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
+ if (buffer) {
+ uint64_t frameNumber = sc->resolveFrameNumber(optFrameNumber);
+ bufferData->frameNumber = frameNumber;
+ bufferData->producerId = producerId;
+ bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
+ if (fence) {
+ bufferData->acquireFence = *fence;
+ bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
+ }
+ bufferData->releaseBufferEndpoint =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ setReleaseBufferCallback(bufferData.get(), callback);
}
- bufferData->releaseBufferEndpoint =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
if (mIsAutoTimestamp) {
mDesiredPresentTime = systemTime();
}
- setReleaseBufferCallback(bufferData.get(), callback);
s->what |= layer_state_t::eBufferChanged;
s->bufferData = std::move(bufferData);
registerSurfaceControlForCallback(sc);
@@ -1684,6 +1681,25 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::unsetBuffer(
+ const sp<SurfaceControl>& sc) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ if (!(s->what & layer_state_t::eBufferChanged)) {
+ return *this;
+ }
+
+ releaseBufferIfOverwriting(*s);
+
+ s->what &= ~layer_state_t::eBufferChanged;
+ s->bufferData = nullptr;
+ return *this;
+}
+
void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
ReleaseBufferCallback callback) {
if (!callback) {
diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp
index 76c60c2..8e00c2f 100644
--- a/libs/gui/VsyncEventData.cpp
+++ b/libs/gui/VsyncEventData.cpp
@@ -23,8 +23,8 @@
namespace android::gui {
-static_assert(VsyncEventData::kFrameTimelinesLength == 7,
- "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_LENGTH (and here)");
+static_assert(VsyncEventData::kFrameTimelinesCapacity == 7,
+ "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_CAPACITY (and here)");
int64_t VsyncEventData::preferredVsyncId() const {
return frameTimelines[preferredFrameTimelineIndex].vsyncId;
@@ -46,11 +46,15 @@
SAFE_PARCEL(parcel->readInt64, &vsync.frameInterval);
- uint64_t uintPreferredFrameTimelineIndex;
- SAFE_PARCEL(parcel->readUint64, &uintPreferredFrameTimelineIndex);
+ uint32_t uintPreferredFrameTimelineIndex;
+ SAFE_PARCEL(parcel->readUint32, &uintPreferredFrameTimelineIndex);
vsync.preferredFrameTimelineIndex = static_cast<size_t>(uintPreferredFrameTimelineIndex);
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ uint32_t uintFrameTimelinesLength;
+ SAFE_PARCEL(parcel->readUint32, &uintFrameTimelinesLength);
+ vsync.frameTimelinesLength = static_cast<size_t>(uintFrameTimelinesLength);
+
+ for (size_t i = 0; i < vsync.frameTimelinesLength; i++) {
SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].vsyncId);
SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].deadlineTimestamp);
SAFE_PARCEL(parcel->readInt64, &vsync.frameTimelines[i].expectedPresentationTime);
@@ -60,8 +64,9 @@
}
status_t ParcelableVsyncEventData::writeToParcel(Parcel* parcel) const {
SAFE_PARCEL(parcel->writeInt64, vsync.frameInterval);
- SAFE_PARCEL(parcel->writeUint64, vsync.preferredFrameTimelineIndex);
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ SAFE_PARCEL(parcel->writeUint32, vsync.preferredFrameTimelineIndex);
+ SAFE_PARCEL(parcel->writeUint32, vsync.frameTimelinesLength);
+ for (size_t i = 0; i < vsync.frameTimelinesLength; i++) {
SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].vsyncId);
SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].deadlineTimestamp);
SAFE_PARCEL(parcel->writeInt64, vsync.frameTimelines[i].expectedPresentationTime);
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 2b34a0f..76e7b6e 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -17,6 +17,7 @@
#include <android/gui/ISurfaceComposer.h>
#include <gui/AidlStatusUtil.h>
#include <gui/WindowInfosListenerReporter.h>
+#include "gui/WindowInfosUpdate.h"
namespace android {
@@ -84,7 +85,7 @@
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
+ const gui::WindowInfosUpdate& update,
const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
std::unordered_set<sp<WindowInfosListener>, gui::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -95,12 +96,12 @@
windowInfosListeners.insert(listener);
}
- mLastWindowInfos = windowInfos;
- mLastDisplayInfos = displayInfos;
+ mLastWindowInfos = update.windowInfos;
+ mLastDisplayInfos = update.displayInfos;
}
for (auto listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos, displayInfos);
+ listener->onWindowInfosChanged(update);
}
if (windowInfosReportedListener) {
diff --git a/libs/gui/WindowInfosUpdate.cpp b/libs/gui/WindowInfosUpdate.cpp
new file mode 100644
index 0000000..38ae5ef
--- /dev/null
+++ b/libs/gui/WindowInfosUpdate.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/WindowInfosUpdate.h>
+#include <private/gui/ParcelUtils.h>
+
+namespace android::gui {
+
+status_t WindowInfosUpdate::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ uint32_t size;
+
+ SAFE_PARCEL(parcel->readUint32, &size);
+ windowInfos.reserve(size);
+ for (uint32_t i = 0; i < size; i++) {
+ windowInfos.push_back({});
+ SAFE_PARCEL(windowInfos.back().readFromParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->readUint32, &size);
+ displayInfos.reserve(size);
+ for (uint32_t i = 0; i < size; i++) {
+ displayInfos.push_back({});
+ SAFE_PARCEL(displayInfos.back().readFromParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->readInt64, &vsyncId);
+ SAFE_PARCEL(parcel->readInt64, ×tamp);
+
+ return OK;
+}
+
+status_t WindowInfosUpdate::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(windowInfos.size()));
+ for (auto& windowInfo : windowInfos) {
+ SAFE_PARCEL(windowInfo.writeToParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(displayInfos.size()));
+ for (auto& displayInfo : displayInfos) {
+ SAFE_PARCEL(displayInfo.writeToParcel, parcel);
+ }
+
+ SAFE_PARCEL(parcel->writeInt64, vsyncId);
+ SAFE_PARCEL(parcel->writeInt64, timestamp);
+
+ return OK;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index aa58e2e..ec3266c 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -230,6 +230,10 @@
*/
void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener);
+ /**
+ * Capture the specified screen. This requires the READ_FRAME_BUFFER
+ * permission.
+ */
void captureDisplayById(long displayId, IScreenCaptureListener listener);
/**
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index a5b2762..400229d 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,12 +16,11 @@
package android.gui;
-import android.gui.DisplayInfo;
import android.gui.IWindowInfosReportedListener;
-import android.gui.WindowInfo;
+import android.gui.WindowInfosUpdate;
/** @hide */
-oneway interface IWindowInfosListener
-{
- void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+oneway interface IWindowInfosListener {
+ void onWindowInfosChanged(
+ in WindowInfosUpdate update, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
}
diff --git a/libs/gui/android/gui/WindowInfosUpdate.aidl b/libs/gui/android/gui/WindowInfosUpdate.aidl
new file mode 100644
index 0000000..0c6109d
--- /dev/null
+++ b/libs/gui/android/gui/WindowInfosUpdate.aidl
@@ -0,0 +1,22 @@
+/*
+** Copyright 2023, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.gui;
+
+import android.gui.DisplayInfo;
+import android.gui.WindowInfo;
+
+parcelable WindowInfosUpdate cpp_header "gui/WindowInfosUpdate.h";
diff --git a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
index 6d5ae49..6e4f074 100644
--- a/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_displayEvent_fuzzer.cpp
@@ -51,7 +51,7 @@
event.vsync.count = fdp->ConsumeIntegral<uint32_t>();
event.vsync.vsyncData.frameInterval = fdp->ConsumeIntegral<uint64_t>();
event.vsync.vsyncData.preferredFrameTimelineIndex = fdp->ConsumeIntegral<uint32_t>();
- for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesLength; ++idx) {
+ for (size_t idx = 0; idx < gui::VsyncEventData::kFrameTimelinesCapacity; ++idx) {
event.vsync.vsyncData.frameTimelines[idx].vsyncId = fdp->ConsumeIntegral<int64_t>();
event.vsync.vsyncData.frameTimelines[idx].deadlineTimestamp =
fdp->ConsumeIntegral<uint64_t>();
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
index 5c794ae..2676e0a 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -41,7 +41,7 @@
bool captureSecureLayers{false};
int32_t uid{UNSET_UID};
// Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
- // result will be in the display's colorspace.
+ // result will be in a colorspace appropriate for capturing the display contents
// The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
// different from SRGB (byte per color), and failed when checking colors in tests.
// NOTE: In normal cases, we want the screen to be captured in display's colorspace.
@@ -59,6 +59,15 @@
std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
+ // Hint that the caller will use the screenshot animation as part of a transition animation.
+ // The canonical example would be screen rotation - in such a case any color shift in the
+ // screenshot is a detractor so composition in the display's colorspace is required.
+ // Otherwise, the system may choose a colorspace that is more appropriate for use-cases
+ // such as file encoding or for blending HDR content into an ap's UI, where the display's
+ // exact colorspace is not an appropriate intermediate result.
+ // Note that if the caller is requesting a specific dataspace, this hint does nothing.
+ bool hintForSeamlessTransition = false;
+
virtual status_t writeToParcel(Parcel* output) const;
virtual status_t readFromParcel(const Parcel* input);
};
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 5c88a07..a6f503e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -233,9 +233,10 @@
// Geometry updates.
static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged |
- layer_state_t::eBufferTransformChanged | layer_state_t::eCropChanged |
- layer_state_t::eDestinationFrameChanged | layer_state_t::eMatrixChanged |
- layer_state_t::ePositionChanged | layer_state_t::eTransformToDisplayInverseChanged |
+ layer_state_t::eBufferTransformChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eCropChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::ePositionChanged |
+ layer_state_t::eTransformToDisplayInverseChanged |
layer_state_t::eTransparentRegionChanged;
// Buffer and related updates.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d431b43..945b164 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -541,6 +541,7 @@
const std::optional<sp<Fence>>& fence = std::nullopt,
const std::optional<uint64_t>& frameNumber = std::nullopt,
uint32_t producerId = 0, ReleaseBufferCallback callback = nullptr);
+ Transaction& unsetBuffer(const sp<SurfaceControl>& sc);
std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
/**
diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h
index dfdae21..b40a840 100644
--- a/libs/gui/include/gui/VsyncEventData.h
+++ b/libs/gui/include/gui/VsyncEventData.h
@@ -24,8 +24,8 @@
// Plain Old Data (POD) vsync data structure. For example, it can be easily used in the
// DisplayEventReceiver::Event union.
struct VsyncEventData {
- // Max amount of frame timelines is arbitrarily set to be reasonable.
- static constexpr int64_t kFrameTimelinesLength = 7;
+ // Max capacity of frame timelines is arbitrarily set to be reasonable.
+ static constexpr int64_t kFrameTimelinesCapacity = 7;
// The current frame interval in ns when this frame was scheduled.
int64_t frameInterval;
@@ -33,6 +33,9 @@
// Index into the frameTimelines that represents the platform's preferred frame timeline.
uint32_t preferredFrameTimelineIndex;
+ // Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity.
+ uint32_t frameTimelinesLength;
+
struct alignas(8) FrameTimeline {
// The Vsync Id corresponsing to this vsync event. This will be used to
// populate ISurfaceComposer::setFrameTimelineVsync and
@@ -45,7 +48,7 @@
// The anticipated Vsync presentation time in nanos.
int64_t expectedPresentationTime;
- } frameTimelines[kFrameTimelinesLength]; // Sorted possible frame timelines.
+ } frameTimelines[kFrameTimelinesCapacity]; // Sorted possible frame timelines.
// Gets the preferred frame timeline's vsync ID.
int64_t preferredVsyncId() const;
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
index a18a498..02c8eb5 100644
--- a/libs/gui/include/gui/WindowInfosListener.h
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -16,15 +16,13 @@
#pragma once
-#include <gui/DisplayInfo.h>
-#include <gui/WindowInfo.h>
+#include <gui/WindowInfosUpdate.h>
#include <utils/RefBase.h>
namespace android::gui {
class WindowInfosListener : public virtual RefBase {
public:
- virtual void onWindowInfosChanged(const std::vector<WindowInfo>&,
- const std::vector<DisplayInfo>&) = 0;
+ virtual void onWindowInfosChanged(const WindowInfosUpdate& update) = 0;
};
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 2754442..38cb108 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -22,6 +22,7 @@
#include <binder/IBinder.h>
#include <gui/SpHash.h>
#include <gui/WindowInfosListener.h>
+#include <gui/WindowInfosUpdate.h>
#include <unordered_set>
namespace android {
@@ -29,8 +30,7 @@
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
- const std::vector<gui::DisplayInfo>&,
+ binder::Status onWindowInfosChanged(const gui::WindowInfosUpdate& update,
const sp<gui::IWindowInfosReportedListener>&) override;
status_t addWindowInfosListener(
const sp<gui::WindowInfosListener>& windowInfosListener,
diff --git a/libs/gui/include/gui/WindowInfosUpdate.h b/libs/gui/include/gui/WindowInfosUpdate.h
new file mode 100644
index 0000000..2ca59fb
--- /dev/null
+++ b/libs/gui/include/gui/WindowInfosUpdate.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+#include <gui/DisplayInfo.h>
+#include <gui/WindowInfo.h>
+
+namespace android::gui {
+
+struct WindowInfosUpdate : public Parcelable {
+ WindowInfosUpdate() {}
+
+ WindowInfosUpdate(std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
+ int64_t vsyncId, int64_t timestamp)
+ : windowInfos(std::move(windowInfos)),
+ displayInfos(std::move(displayInfos)),
+ vsyncId(vsyncId),
+ timestamp(timestamp) {}
+
+ std::vector<WindowInfo> windowInfos;
+ std::vector<DisplayInfo> displayInfos;
+ int64_t vsyncId;
+ int64_t timestamp;
+
+ status_t writeToParcel(android::Parcel*) const override;
+ status_t readFromParcel(const android::Parcel*) override;
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h
index 08785b4..1c900e9 100644
--- a/libs/gui/include/gui/test/CallbackUtils.h
+++ b/libs/gui/include/gui/test/CallbackUtils.h
@@ -51,6 +51,7 @@
enum Buffer {
NOT_ACQUIRED = 0,
ACQUIRED,
+ ACQUIRED_NULL,
};
enum PreviousBuffer {
@@ -133,17 +134,28 @@
: mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
- nsecs_t latchTime) const {
+ nsecs_t /* latchTime */) const {
const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence,
previousReleaseFence, transformHint, frameEvents, ignore] =
- surfaceControlStats;
+ surfaceControlStats;
- ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence));
- ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0,
- mBufferResult == ExpectedResult::Buffer::ACQUIRED)
- << "bad acquire time";
- ASSERT_LE(std::get<nsecs_t>(acquireTimeOrFence), latchTime)
- << "acquire time should be <= latch time";
+ nsecs_t acquireTime = -1;
+ if (std::holds_alternative<nsecs_t>(acquireTimeOrFence)) {
+ acquireTime = std::get<nsecs_t>(acquireTimeOrFence);
+ } else {
+ auto fence = std::get<sp<Fence>>(acquireTimeOrFence);
+ if (fence) {
+ ASSERT_EQ(fence->wait(3000), NO_ERROR);
+ acquireTime = fence->getSignalTime();
+ }
+ }
+
+ if (mBufferResult == ExpectedResult::Buffer::ACQUIRED) {
+ ASSERT_GT(acquireTime, 0) << "acquire time should be valid";
+ } else {
+ ASSERT_LE(acquireTime, 0) << "acquire time should not be valid";
+ }
+ ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED);
if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
ASSERT_NE(previousReleaseFence, nullptr)
diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp
index da88463..3949d70 100644
--- a/libs/gui/tests/DisplayEventStructLayout_test.cpp
+++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp
@@ -35,6 +35,7 @@
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16);
+ CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp,
@@ -44,16 +45,16 @@
// Also test the offsets of the last frame timeline. A loop is not used because the non-const
// index cannot be used in static_assert.
const int lastFrameTimelineOffset = /* Start of array */ 24 +
- (VsyncEventData::kFrameTimelinesLength - 1) * /* Size of FrameTimeline */ 24;
+ (VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24;
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1].vsyncId,
+ vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId,
lastFrameTimelineOffset);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1]
+ vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1]
.deadlineTimestamp,
lastFrameTimelineOffset + 8);
CHECK_OFFSET(DisplayEventReceiver::Event::VSync,
- vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesLength - 1]
+ vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1]
.expectedPresentationTime,
lastFrameTimelineOffset + 16);
diff --git a/libs/gui/tests/VsyncEventData_test.cpp b/libs/gui/tests/VsyncEventData_test.cpp
index f114522..a2138f2 100644
--- a/libs/gui/tests/VsyncEventData_test.cpp
+++ b/libs/gui/tests/VsyncEventData_test.cpp
@@ -36,6 +36,7 @@
FrameTimeline timeline1 = FrameTimeline{4, 5, 6};
data.vsync.frameTimelines[0] = timeline0;
data.vsync.frameTimelines[1] = timeline1;
+ data.vsync.frameTimelinesLength = 2;
Parcel p;
data.writeToParcel(&p);
@@ -45,7 +46,8 @@
data2.readFromParcel(&p);
ASSERT_EQ(data.vsync.frameInterval, data2.vsync.frameInterval);
ASSERT_EQ(data.vsync.preferredFrameTimelineIndex, data2.vsync.preferredFrameTimelineIndex);
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ ASSERT_EQ(data.vsync.frameTimelinesLength, data2.vsync.frameTimelinesLength);
+ for (int i = 0; i < VsyncEventData::kFrameTimelinesCapacity; i++) {
ASSERT_EQ(data.vsync.frameTimelines[i].vsyncId, data2.vsync.frameTimelines[i].vsyncId);
ASSERT_EQ(data.vsync.frameTimelines[i].deadlineTimestamp,
data2.vsync.frameTimelines[i].deadlineTimestamp);
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 136a560..12c9e53 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -85,63 +85,7 @@
// --- KeyCharacterMap ---
-KeyCharacterMap::KeyCharacterMap(const std::string& filename)
- : mType(KeyboardType::UNKNOWN), mLoadFileName(filename) {}
-
-KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
- : mType(other.mType),
- mLoadFileName(other.mLoadFileName),
- mLayoutOverlayApplied(other.mLayoutOverlayApplied),
- mKeyRemapping(other.mKeyRemapping),
- mKeysByScanCode(other.mKeysByScanCode),
- mKeysByUsageCode(other.mKeysByUsageCode) {
- for (size_t i = 0; i < other.mKeys.size(); i++) {
- mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
- }
-}
-
-KeyCharacterMap::~KeyCharacterMap() {
- clear();
-}
-
-bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const {
- if (mType != other.mType) {
- return false;
- }
- if (mLoadFileName != other.mLoadFileName) {
- return false;
- }
- if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
- return false;
- }
- if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() ||
- mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
- mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
- return false;
- }
-
- for (size_t i = 0; i < mKeys.size(); i++) {
- if (mKeys.keyAt(i) != other.mKeys.keyAt(i)) {
- return false;
- }
- const Key* key = mKeys.valueAt(i);
- const Key* otherKey = other.mKeys.valueAt(i);
- if (key->label != otherKey->label || key->number != otherKey->number) {
- return false;
- }
- }
-
- if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode ||
- mKeysByUsageCode != other.mKeysByUsageCode) {
- return false;
- }
-
- return true;
-}
-
-bool KeyCharacterMap::operator!=(const KeyCharacterMap& other) const {
- return !(*this == other);
-}
+KeyCharacterMap::KeyCharacterMap(const std::string& filename) : mLoadFileName(filename) {}
base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
Format format) {
@@ -207,10 +151,6 @@
void KeyCharacterMap::clear() {
mKeysByScanCode.clear();
mKeysByUsageCode.clear();
- for (size_t i = 0; i < mKeys.size(); i++) {
- Key* key = mKeys.editValueAt(i);
- delete key;
- }
mKeys.clear();
mLayoutOverlayApplied = false;
mType = KeyboardType::UNKNOWN;
@@ -233,24 +173,16 @@
if (mLayoutOverlayApplied) {
reloadBaseFromFile();
}
- for (size_t i = 0; i < overlay.mKeys.size(); i++) {
- int32_t keyCode = overlay.mKeys.keyAt(i);
- Key* key = overlay.mKeys.valueAt(i);
- ssize_t oldIndex = mKeys.indexOfKey(keyCode);
- if (oldIndex >= 0) {
- delete mKeys.valueAt(oldIndex);
- mKeys.editValueAt(oldIndex) = new Key(*key);
- } else {
- mKeys.add(keyCode, new Key(*key));
- }
+ for (const auto& [keyCode, key] : overlay.mKeys) {
+ mKeys.insert_or_assign(keyCode, key);
}
- for (auto const& it : overlay.mKeysByScanCode) {
- mKeysByScanCode.insert_or_assign(it.first, it.second);
+ for (const auto& [fromScanCode, toAndroidKeyCode] : overlay.mKeysByScanCode) {
+ mKeysByScanCode.insert_or_assign(fromScanCode, toAndroidKeyCode);
}
- for (auto const& it : overlay.mKeysByUsageCode) {
- mKeysByUsageCode.insert_or_assign(it.first, it.second);
+ for (const auto& [fromHidUsageCode, toAndroidKeyCode] : overlay.mKeysByUsageCode) {
+ mKeysByUsageCode.insert_or_assign(fromHidUsageCode, toAndroidKeyCode);
}
mLayoutOverlayApplied = true;
}
@@ -343,19 +275,15 @@
if (behavior.character == chars[i]) {
result = behavior.character;
if ((behavior.metaState & metaState) == behavior.metaState) {
- goto ExactMatch;
+ // Found exact match!
+ return result;
}
break;
}
}
}
}
- ExactMatch: ;
}
-#if DEBUG_MAPPING
- ALOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.",
- keyCode, toString(chars, numChars).string(), metaState, result);
-#endif
return result;
}
@@ -494,9 +422,9 @@
}
const KeyCharacterMap::Key* KeyCharacterMap::getKey(int32_t keyCode) const {
- ssize_t index = mKeys.indexOfKey(keyCode);
- if (index >= 0) {
- return mKeys.valueAt(index);
+ auto it = mKeys.find(keyCode);
+ if (it != mKeys.end()) {
+ return &it->second;
}
return nullptr;
}
@@ -550,19 +478,17 @@
return false;
}
- for (size_t i = 0; i < mKeys.size(); i++) {
- const Key* key = mKeys.valueAt(i);
-
+ for (const auto& [keyCode, key] : mKeys) {
// Try to find the most general behavior that maps to this character.
// For example, the base key behavior will usually be last in the list.
const Behavior* found = nullptr;
- for (const Behavior& behavior : key->behaviors) {
+ for (const Behavior& behavior : key.behaviors) {
if (behavior.character == ch) {
found = &behavior;
}
}
if (found != nullptr) {
- *outKeyCode = mKeys.keyAt(i);
+ *outKeyCode = keyCode;
*outMetaState = found->metaState;
return true;
}
@@ -714,11 +640,7 @@
return nullptr;
}
- Key* key = new Key();
- key->label = label;
- key->number = number;
- map->mKeys.add(keyCode, key);
-
+ Key key{.label = label, .number = number};
while (parcel->readInt32()) {
int32_t metaState = parcel->readInt32();
char16_t character = parcel->readInt32();
@@ -728,13 +650,14 @@
return nullptr;
}
- key->behaviors.push_back({
+ key.behaviors.push_back({
.metaState = metaState,
.character = character,
.fallbackKeyCode = fallbackKeyCode,
.replacementKeyCode = replacementKeyCode,
});
}
+ map->mKeys.emplace(keyCode, std::move(key));
if (parcel->errorCheck()) {
return nullptr;
@@ -790,13 +713,11 @@
size_t numKeys = mKeys.size();
parcel->writeInt32(numKeys);
- for (size_t i = 0; i < numKeys; i++) {
- int32_t keyCode = mKeys.keyAt(i);
- const Key* key = mKeys.valueAt(i);
+ for (const auto& [keyCode, key] : mKeys) {
parcel->writeInt32(keyCode);
- parcel->writeInt32(key->label);
- parcel->writeInt32(key->number);
- for (const Behavior& behavior : key->behaviors) {
+ parcel->writeInt32(key.label);
+ parcel->writeInt32(key.number);
+ for (const Behavior& behavior : key.behaviors) {
parcel->writeInt32(1);
parcel->writeInt32(behavior.metaState);
parcel->writeInt32(behavior.character);
@@ -826,22 +747,12 @@
}
#endif // __linux__
-// --- KeyCharacterMap::Key ---
-
-KeyCharacterMap::Key::Key() : label(0), number(0) {}
-
-KeyCharacterMap::Key::Key(const Key& other)
- : label(other.label), number(other.number), behaviors(other.behaviors) {}
-
// --- KeyCharacterMap::Parser ---
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
}
-KeyCharacterMap::Parser::~Parser() {
-}
-
status_t KeyCharacterMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
@@ -1021,7 +932,7 @@
keyCodeToken.string());
return BAD_VALUE;
}
- if (mMap->mKeys.indexOfKey(*keyCode) >= 0) {
+ if (mMap->mKeys.find(*keyCode) != mMap->mKeys.end()) {
ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
return BAD_VALUE;
@@ -1037,27 +948,27 @@
ALOGD_IF(DEBUG_PARSER, "Parsed beginning of key: keyCode=%d.", *keyCode);
mKeyCode = *keyCode;
- mMap->mKeys.add(*keyCode, new Key());
+ mMap->mKeys.emplace(*keyCode, Key{});
mState = STATE_KEY;
return NO_ERROR;
}
status_t KeyCharacterMap::Parser::parseKeyProperty() {
- Key* key = mMap->mKeys.valueFor(mKeyCode);
+ Key& key = mMap->mKeys[mKeyCode];
String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
if (token == "}") {
mState = STATE_TOP;
- return finishKey(*key);
+ return finishKey(key);
}
- Vector<Property> properties;
+ std::vector<Property> properties;
// Parse all comma-delimited property names up to the first colon.
for (;;) {
if (token == "label") {
- properties.add(Property(PROPERTY_LABEL));
+ properties.emplace_back(PROPERTY_LABEL);
} else if (token == "number") {
- properties.add(Property(PROPERTY_NUMBER));
+ properties.emplace_back(PROPERTY_NUMBER);
} else {
int32_t metaState;
status_t status = parseModifier(token.string(), &metaState);
@@ -1066,7 +977,7 @@
mTokenizer->getLocation().string(), token.string());
return status;
}
- properties.add(Property(PROPERTY_META, metaState));
+ properties.emplace_back(PROPERTY_META, metaState);
}
mTokenizer->skipDelimiters(WHITESPACE);
@@ -1181,47 +1092,44 @@
} while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#');
// Add the behavior.
- for (size_t i = 0; i < properties.size(); i++) {
- const Property& property = properties.itemAt(i);
+ for (const Property& property : properties) {
switch (property.property) {
case PROPERTY_LABEL:
- if (key->label) {
- ALOGE("%s: Duplicate label for key.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
- key->label = behavior.character;
+ if (key.label) {
+ ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
+ }
+ key.label = behavior.character;
#if DEBUG_PARSER
- ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label);
+ ALOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key.label);
#endif
break;
case PROPERTY_NUMBER:
- if (key->number) {
- ALOGE("%s: Duplicate number for key.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
+ if (key.number) {
+ ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().string());
+ return BAD_VALUE;
}
- key->number = behavior.character;
+ key.number = behavior.character;
#if DEBUG_PARSER
- ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number);
+ ALOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key.number);
#endif
break;
case PROPERTY_META: {
- for (const Behavior& b : key->behaviors) {
- if (b.metaState == property.metaState) {
+ for (const Behavior& b : key.behaviors) {
+ if (b.metaState == property.metaState) {
ALOGE("%s: Duplicate key behavior for modifier.",
mTokenizer->getLocation().string());
return BAD_VALUE;
- }
+ }
}
Behavior newBehavior = behavior;
newBehavior.metaState = property.metaState;
- key->behaviors.push_front(newBehavior);
+ key.behaviors.push_front(newBehavior);
ALOGD_IF(DEBUG_PARSER,
"Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d replace=%d.",
- mKeyCode, key->behaviors.front().metaState, key->behaviors.front().character,
- key->behaviors.front().fallbackKeyCode,
- key->behaviors.front().replacementKeyCode);
+ mKeyCode, key.behaviors.front().metaState, key.behaviors.front().character,
+ key.behaviors.front().fallbackKeyCode,
+ key.behaviors.front().replacementKeyCode);
break;
}
}
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index 3c1f2b6..af9ce3a 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -44,15 +44,24 @@
ioctl(mFd, UI_DEV_DESTROY);
}
-bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value) {
- struct input_event ev = {.type = type, .code = code, .value = value};
+bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value,
+ std::chrono::nanoseconds eventTime) {
+ std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(eventTime);
+ std::chrono::microseconds microseconds =
+ std::chrono::duration_cast<std::chrono::microseconds>(eventTime - seconds);
+ struct input_event ev = {.type = type,
+ .code = code,
+ .value = value,
+ .input_event_sec = static_cast<time_t>(seconds.count()),
+ .input_event_usec = static_cast<suseconds_t>(microseconds.count())};
return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev);
}
/** Utility method to write keyboard key events or mouse button events. */
bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction,
const std::map<int, int>& evKeyCodeMapping,
- const std::map<int, UinputAction>& actionMapping) {
+ const std::map<int, UinputAction>& actionMapping,
+ std::chrono::nanoseconds eventTime) {
auto evKeyCodeIterator = evKeyCodeMapping.find(androidCode);
if (evKeyCodeIterator == evKeyCodeMapping.end()) {
ALOGE("Unsupported native EV keycode for android code %d", androidCode);
@@ -63,10 +72,10 @@
return false;
}
if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second),
- static_cast<int32_t>(actionIterator->second))) {
+ static_cast<int32_t>(actionIterator->second), eventTime)) {
return false;
}
- if (!writeInputEvent(EV_SYN, SYN_REPORT, 0)) {
+ if (!writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime)) {
return false;
}
return true;
@@ -189,8 +198,10 @@
VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {}
VirtualKeyboard::~VirtualKeyboard() {}
-bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction) {
- return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING);
+bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime) {
+ return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING,
+ eventTime);
}
// --- VirtualDpad ---
@@ -210,9 +221,10 @@
VirtualDpad::~VirtualDpad() {}
-bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction) {
+bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime) {
return writeEvKeyEvent(androidKeyCode, androidAction, DPAD_KEY_CODE_MAPPING,
- VirtualKeyboard::KEY_ACTION_MAPPING);
+ VirtualKeyboard::KEY_ACTION_MAPPING, eventTime);
}
// --- VirtualMouse ---
@@ -236,20 +248,24 @@
VirtualMouse::~VirtualMouse() {}
-bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction) {
+bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction,
+ std::chrono::nanoseconds eventTime) {
return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING,
- BUTTON_ACTION_MAPPING);
+ BUTTON_ACTION_MAPPING, eventTime);
}
-bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY) {
- return writeInputEvent(EV_REL, REL_X, relativeX) && writeInputEvent(EV_REL, REL_Y, relativeY) &&
- writeInputEvent(EV_SYN, SYN_REPORT, 0);
+bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY,
+ std::chrono::nanoseconds eventTime) {
+ return writeInputEvent(EV_REL, REL_X, relativeX, eventTime) &&
+ writeInputEvent(EV_REL, REL_Y, relativeY, eventTime) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
}
-bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement) {
- return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement) &&
- writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement) &&
- writeInputEvent(EV_SYN, SYN_REPORT, 0);
+bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement,
+ std::chrono::nanoseconds eventTime) {
+ return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement, eventTime) &&
+ writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement, eventTime) &&
+ writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
}
// --- VirtualTouchscreen ---
@@ -291,7 +307,7 @@
bool VirtualTouchscreen::writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action,
float locationX, float locationY, float pressure,
- float majorAxisSize) {
+ float majorAxisSize, std::chrono::nanoseconds eventTime) {
auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
return false;
@@ -300,44 +316,44 @@
if (!isValidPointerId(pointerId, uinputAction)) {
return false;
}
- if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId, eventTime)) {
return false;
}
auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
return false;
}
- if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE,
- static_cast<int32_t>(toolTypeIterator->second))) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE, static_cast<int32_t>(toolTypeIterator->second),
+ eventTime)) {
return false;
}
- if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId)) {
+ if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId, eventTime)) {
return false;
}
- if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId)) {
+ if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId, eventTime)) {
return false;
}
- if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX, eventTime)) {
return false;
}
- if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY, eventTime)) {
return false;
}
if (!isnan(pressure)) {
- if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure, eventTime)) {
return false;
}
}
if (!isnan(majorAxisSize)) {
- if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize, eventTime)) {
return false;
}
}
- return writeInputEvent(EV_SYN, SYN_REPORT, 0);
+ return writeInputEvent(EV_SYN, SYN_REPORT, 0, eventTime);
}
-bool VirtualTouchscreen::handleTouchUp(int32_t pointerId) {
- if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1))) {
+bool VirtualTouchscreen::handleTouchUp(int32_t pointerId, std::chrono::nanoseconds eventTime) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1), eventTime)) {
return false;
}
// When a pointer is no longer in touch, remove the pointer id from the corresponding
@@ -347,7 +363,8 @@
// Only sends the BTN UP event when there's no pointers on the touchscreen.
if (mActivePointers.none()) {
- if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE))) {
+ if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE),
+ eventTime)) {
return false;
}
ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", mFd.get());
@@ -355,12 +372,13 @@
return true;
}
-bool VirtualTouchscreen::handleTouchDown(int32_t pointerId) {
+bool VirtualTouchscreen::handleTouchDown(int32_t pointerId, std::chrono::nanoseconds eventTime) {
// When a new pointer is down on the touchscreen, add the pointer id in the corresponding
// entry in the unreleased touches map.
if (mActivePointers.none()) {
// Only sends the BTN Down event when the first pointer on the touchscreen is down.
- if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS))) {
+ if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS),
+ eventTime)) {
return false;
}
ALOGD_IF(isDebug(), "First pointer %d down under touchscreen %d, BTN DOWN event sent",
@@ -369,7 +387,7 @@
mActivePointers.set(pointerId);
ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, mFd.get());
- if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId))) {
+ if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId), eventTime)) {
return false;
}
return true;
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 66a40f1..8f005a5 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -197,7 +197,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- return VsyncEventData::kFrameTimelinesLength;
+ return frameCallbackData->vsyncEventData.frameTimelinesLength;
}
size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) {
@@ -213,7 +213,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+ LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesCapacity, "Index out of bounds");
return frameCallbackData->vsyncEventData.frameTimelines[index].vsyncId;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
@@ -222,7 +222,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+ LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesCapacity, "Index out of bounds");
return frameCallbackData->vsyncEventData.frameTimelines[index].expectedPresentationTime;
}
int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
@@ -231,7 +231,7 @@
AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
"Data is only valid in callback");
- LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesLength, "Index out of bounds");
+ LOG_ALWAYS_FATAL_IF(index >= VsyncEventData::kFrameTimelinesCapacity, "Index out of bounds");
return frameCallbackData->vsyncEventData.frameTimelines[index].deadlineTimestamp;
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 932be56..c412c9c 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -43,10 +43,12 @@
createProtectedImage, backendFormat,
isOutputBuffer);
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
- ALOGE_IF(!mBackendTexture.isValid(),
- "Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d isWriteable:%d "
- "format:%d",
- this, desc.width, desc.height, createProtectedImage, isOutputBuffer, desc.format);
+ if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
+ LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
+ "isWriteable:%d format:%d",
+ this, desc.width, desc.height, createProtectedImage, isOutputBuffer,
+ desc.format);
+ }
}
AutoBackendTexture::~AutoBackendTexture() {
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
index 37ff5df..92b01e0 100644
--- a/libs/renderengine/skia/ColorSpaces.cpp
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -21,6 +21,8 @@
namespace skia {
// please keep in sync with hwui/utils/Color.cpp
+// TODO: Scale by the dimming ratio here instead of in a generic 3x3 transform
+// Otherwise there may be luminance shift for e.g., HLG.
sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
skcms_Matrix3x3 gamut;
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -61,13 +63,14 @@
case HAL_DATASPACE_TRANSFER_GAMMA2_8:
return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
case HAL_DATASPACE_TRANSFER_ST2084:
- return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ return SkColorSpace::MakeRGB({-2.f, -1.55522297832f, 1.86045365631f, 32 / 2523.0f,
+ 2413 / 128.0f, -2392 / 128.0f, 8192 / 1305.0f},
+ gamut);
case HAL_DATASPACE_TRANSFER_SMPTE_170M:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
case HAL_DATASPACE_TRANSFER_HLG:
- // return HLG transfer but scale by 1/12
skcms_TransferFunction hlgFn;
- if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 1.f / 12.f, 2.f, 2.f,
+ if (skcms_TransferFunction_makeScaledHLGish(&hlgFn, 0.314509843, 2.f, 2.f,
1.f / 0.17883277f, 0.28466892f,
0.55991073f)) {
return SkColorSpace::MakeRGB(hlgFn, gamut);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 8256dd8..cfea85f 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -517,16 +517,18 @@
} else {
runtimeEffect = effectIter->second;
}
+
mat4 colorTransform = parameters.layer.colorTransform;
colorTransform *=
mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
parameters.layerDimmingRatio, 1.f));
+
const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr;
- return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
- parameters.display.maxLuminance,
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect,
+ std::move(colorTransform), parameters.display.maxLuminance,
parameters.display.currentLuminanceNits,
parameters.layer.source.buffer.maxLuminanceNits,
hardwareBuffer, parameters.display.renderIntent);
@@ -911,12 +913,10 @@
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)
- ? display.outputDataspace
- : layer.sourceDataspace;
+ // If 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 ? display.outputDataspace : layer.sourceDataspace;
SkPaint paint;
if (layer.source.buffer.buffer) {
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index ba190e0..980f8d1 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -176,11 +176,8 @@
mSensors = mSensorServer->getSensorList(mOpPackageName);
size_t count = mSensors.size();
- if (count == 0) {
- ALOGE("Failed to get Sensor list");
- mSensorServer.clear();
- return UNKNOWN_ERROR;
- }
+ // If count is 0, mSensorList will be non-null. This is old
+ // existing behavior and callers expect this.
mSensorList =
static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
index 42b0cc1..5a4aaab 100644
--- a/libs/shaders/include/shaders/shaders.h
+++ b/libs/shaders/include/shaders/shaders.h
@@ -51,23 +51,20 @@
// Input dataspace of the source colors.
const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
- // Working dataspace for the output surface, for conversion from linear space.
+ // Working dataspace for the output surface.
const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
// Sets whether alpha premultiplication must be undone.
// This is required if the source colors use premultiplied alpha and is not opaque.
const bool undoPremultipliedAlpha = false;
- // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear
- // RGB. This is used when Skia is expected to color manage the input image based on the
- // dataspace of the provided source image and destination surface. SkRuntimeEffects use the
- // destination color space as the working color space. RenderEngine deliberately sets the color
- // space for input images and destination surfaces to be the same whenever LinearEffects are
- // expected to be used so that color-management is controlled by RenderEngine, but other users
- // of a LinearEffect may not be able to control the color space of the images and surfaces. So
- // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
- // dataspace for correct conversion to linear colors.
- ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+ // "Fake" dataspace of the destination colors. This is used for applying an OETF to compute
+ // non-linear RGB. This is used when Skia is expected to color manage the input image based on
+ // the dataspace of the provided source image and destination surface. Some use-cases in
+ // RenderEngine expect to apply a different OETF than what is expected by Skia. As in,
+ // RenderEngine will color manage to a custom destination and "cast" the result to Skia's
+ // working space.
+ ui::Dataspace fakeOutputDataspace = ui::Dataspace::UNKNOWN;
enum SkSLType { Shader, ColorFilter };
SkSLType type = Shader;
@@ -76,7 +73,7 @@
static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
- lhs.fakeInputDataspace == rhs.fakeInputDataspace;
+ lhs.fakeOutputDataspace == rhs.fakeOutputDataspace;
}
struct LinearEffectHasher {
@@ -89,7 +86,7 @@
size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
- return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace));
+ return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeOutputDataspace));
}
};
@@ -99,10 +96,6 @@
// 2. Apply color transform matrices in linear space
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
-// Generates a shader string that applies color transforms in linear space.
-// This is intended to be plugged into an SkColorFilter
-std::string buildLinearEffectSkSLForColorFilter(const LinearEffect& linearEffect);
-
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index a3c403e..c85517a 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -33,212 +33,111 @@
return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
}
-void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 EOTF(float3 color) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
- tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
- return pow(tmp, 1.0 / float3(m1));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float EOTF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 0.5 ? channel * channel / 3.0 :
- (exp((channel - c) / a) + b) / 12.0;
- }
-
- float3 EOTF(float3 color) {
- return float3(EOTF_channel(color.r), EOTF_channel(color.g),
- EOTF_channel(color.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 EOTF(float3 color) {
- return color;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SMPTE_170M:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 1 / 0.45);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_GAMMA2_2:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return pow(srgb, 2.2);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_GAMMA2_6:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return pow(srgb, 2.6);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_GAMMA2_8:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return pow(srgb, 2.8);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- }
-}
-
void generateXYZTransforms(std::string& shader) {
shader.append(R"(
- uniform float4x4 in_rgbToXyz;
- uniform float4x4 in_xyzToRgb;
+ uniform float3x3 in_rgbToXyz;
+ uniform float3x3 in_xyzToSrcRgb;
+ uniform float4x4 in_colorTransform;
float3 ToXYZ(float3 rgb) {
- return (in_rgbToXyz * float4(rgb, 1.0)).rgb;
+ return in_rgbToXyz * rgb;
}
- float3 ToRGB(float3 xyz) {
- return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+ float3 ToSrcRGB(float3 xyz) {
+ return in_xyzToSrcRgb * xyz;
+ }
+
+ float3 ApplyColorTransform(float3 rgb) {
+ return (in_colorTransform * float4(rgb, 1.0)).rgb;
}
)");
}
-// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
-void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
- std::string& shader) {
+// Conversion from relative light to absolute light
+// Note that 1.0 == 203 nits.
+void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, std::string& shader) {
switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 10000.0;
- }
- )");
- break;
case HAL_DATASPACE_TRANSFER_HLG:
+ // BT. 2408 says that a signal level of 0.75 == 203 nits for HLG, but that's after
+ // applying OOTF. But we haven't applied OOTF yet, so we need to scale by a different
+ // constant instead.
shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 1000.0;
- }
- )");
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 264.96;
+ }
+ )");
break;
default:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // SDR -> HDR tonemap
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_inputMaxLuminance;
- }
- )");
- break;
- default:
- // Input and output are both SDR, so no tone-mapping is expected so
- // no-op the luminance normalization.
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_displayMaxLuminance;
- }
- )");
- break;
- }
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 203.0;
+ }
+ )");
+ break;
}
}
// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace,
std::string& shader) {
switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_ST2084:
shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 10000.0;
- }
- )");
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 203.0;
+ }
+ )");
break;
case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0;
- }
- )");
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 264.96;
+ }
+ )");
+ break;
+ default:
+ // Transcoding to HLG requires applying the inverse OOTF
+ // with the expectation that the OOTF is then applied during
+ // tonemapping downstream.
+ // BT. 2100-2 operates on normalized luminances, so renormalize to the input to
+ // correctly adjust gamma.
+ // Note that following BT. 2408 for HLG OETF actually maps 0.75 == ~264.96 nits,
+ // rather than 203 nits, because 203 nits == OOTF(invOETF(0.75)), so even though
+ // we originally scaled by 203 nits we need to re-normalize to 264.96 nits when
+ // converting to the correct brightness range.
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ float ootfGain = pow(xyz.y / 1000.0, -0.2 / 1.2);
+ return xyz * ootfGain / 264.96;
+ }
+ )");
+ break;
+ }
break;
default:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / in_libtonemap_displayMaxLuminance;
- }
- )");
- break;
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_HLG:
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ // libtonemap outputs a range [0, in_libtonemap_displayMaxLuminance], so
+ // normalize back to [0, 1] when the output is SDR.
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ default:
+ // Otherwise normalize back down to the range [0, 1]
+ // TODO: get this working for extended range outputs
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 203.0;
+ }
+ )");
+ break;
+ }
}
}
@@ -249,145 +148,34 @@
toAidlDataspace(outputDataspace))
.c_str());
- generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+ generateLuminanceScalesForOOTF(inputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(inputDataspace, outputDataspace, shader);
+ // Some tonemappers operate on CIE luminance, other tonemappers operate on linear rgb
+ // luminance in the source gamut.
shader.append(R"(
- float3 OOTF(float3 linearRGB, float3 xyz) {
+ float3 OOTF(float3 linearRGB) {
float3 scaledLinearRGB = ScaleLuminance(linearRGB);
- float3 scaledXYZ = ScaleLuminance(xyz);
+ float3 scaledXYZ = ToXYZ(scaledLinearRGB);
- float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
+ float gain = libtonemap_LookupTonemapGain(ToSrcRGB(scaledXYZ), scaledXYZ);
return NormalizeLuminance(scaledXYZ * gain);
}
)");
}
-void generateOETF(ui::Dataspace dataspace, std::string& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 OETF(float3 xyz) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(xyz, float3(m1));
- tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
- return pow(tmp, float3(m2));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float OETF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
- a * log(12.0 * channel - b) + c;
- }
-
- float3 OETF(float3 linear) {
- return float3(OETF_channel(linear.r), OETF_channel(linear.g),
- OETF_channel(linear.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 OETF(float3 linear) {
- return linear;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SMPTE_170M:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return linear <= 0.018 ?
- linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099;
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_GAMMA2_2:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return pow(linear, (1.0 / 2.2));
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_GAMMA2_6:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return pow(linear, (1.0 / 2.6));
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_GAMMA2_8:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return pow(linear, (1.0 / 2.8));
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- }
+void generateOETF(std::string& shader) {
+ // Only support gamma 2.2 for now
+ shader.append(R"(
+ float OETF(float3 linear) {
+ return sign(linear) * pow(abs(linear), (1.0 / 2.2));
+ }
+ )");
}
void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type,
- std::string& shader) {
+ bool needsCustomOETF, std::string& shader) {
switch (type) {
case LinearEffect::SkSLType::ColorFilter:
shader.append(R"(
@@ -408,11 +196,19 @@
c.rgb = c.rgb / (c.a + 0.0019);
)");
}
+ // We are using linear sRGB as a working space, with 1.0 == 203 nits
shader.append(R"(
- float3 linearRGB = EOTF(c.rgb);
- float3 xyz = ToXYZ(linearRGB);
- c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
+ c.rgb = ApplyColorTransform(OOTF(toLinearSrgb(c.rgb)));
)");
+ if (needsCustomOETF) {
+ shader.append(R"(
+ c.rgb = OETF(c.rgb);
+ )");
+ } else {
+ shader.append(R"(
+ c.rgb = fromLinearSrgb(c.rgb);
+ )");
+ }
if (undoPremultipliedAlpha) {
shader.append(R"(
c.rgb = c.rgb * (c.a + 0.0019);
@@ -424,33 +220,6 @@
)");
}
-// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
-ColorSpace toColorSpace(ui::Dataspace dataspace) {
- switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
- case HAL_DATASPACE_STANDARD_BT709:
- return ColorSpace::sRGB();
- case HAL_DATASPACE_STANDARD_DCI_P3:
- return ColorSpace::DisplayP3();
- case HAL_DATASPACE_STANDARD_BT2020:
- case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
- return ColorSpace::BT2020();
- case HAL_DATASPACE_STANDARD_ADOBE_RGB:
- return ColorSpace::AdobeRGB();
- // TODO(b/208290320): BT601 format and variants return different primaries
- case HAL_DATASPACE_STANDARD_BT601_625:
- case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
- case HAL_DATASPACE_STANDARD_BT601_525:
- case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
- // TODO(b/208290329): BT407M format returns different primaries
- case HAL_DATASPACE_STANDARD_BT470M:
- // TODO(b/208290904): FILM format returns different primaries
- case HAL_DATASPACE_STANDARD_FILM:
- case HAL_DATASPACE_STANDARD_UNSPECIFIED:
- default:
- return ColorSpace::sRGB();
- }
-}
-
template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
std::vector<uint8_t> buildUniformValue(T value) {
std::vector<uint8_t> result;
@@ -463,17 +232,45 @@
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
std::string shaderString;
- generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
- ? linearEffect.inputDataspace
- : linearEffect.fakeInputDataspace,
- shaderString);
generateXYZTransforms(shaderString);
generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
- generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, shaderString);
+
+ const bool needsCustomOETF = (linearEffect.fakeOutputDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
+ HAL_DATASPACE_TRANSFER_GAMMA2_2;
+ if (needsCustomOETF) {
+ generateOETF(shaderString);
+ }
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, needsCustomOETF,
+ shaderString);
return shaderString;
}
+ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return ColorSpace::BT2020();
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return ColorSpace::AdobeRGB();
+ // TODO(b/208290320): BT601 format and variants return different primaries
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ // TODO(b/208290329): BT407M format returns different primaries
+ case HAL_DATASPACE_STANDARD_BT470M:
+ // TODO(b/208290904): FILM format returns different primaries
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ default:
+ return ColorSpace::sRGB();
+ }
+}
+
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
@@ -481,29 +278,29 @@
aidl::android::hardware::graphics::composer3::RenderIntent renderIntent) {
std::vector<tonemap::ShaderUniform> uniforms;
- const ui::Dataspace inputDataspace = linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
- ? linearEffect.inputDataspace
- : linearEffect.fakeInputDataspace;
+ auto inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ auto outputColorSpace = toColorSpace(linearEffect.outputDataspace);
- if (inputDataspace == linearEffect.outputDataspace) {
- uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
- uniforms.push_back(
- {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)});
- } else {
- ColorSpace inputColorSpace = toColorSpace(inputDataspace);
- ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
- uniforms.push_back({.name = "in_rgbToXyz",
- .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))});
- uniforms.push_back({.name = "in_xyzToRgb",
- .value = buildUniformValue<mat4>(
- colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
- }
+ uniforms.push_back(
+ {.name = "in_rgbToXyz",
+ .value = buildUniformValue<mat3>(ColorSpace::linearExtendedSRGB().getRGBtoXYZ())});
+ uniforms.push_back({.name = "in_xyzToSrcRgb",
+ .value = buildUniformValue<mat3>(inputColorSpace.getXYZtoRGB())});
+ // Transforms xyz colors to linear source colors, then applies the color transform, then
+ // transforms to linear extended RGB for skia to color manage.
+ uniforms.push_back({.name = "in_colorTransform",
+ .value = buildUniformValue<mat4>(
+ mat4(ColorSpace::linearExtendedSRGB().getXYZtoRGB()) *
+ // TODO: the color transform ideally should be applied
+ // in the source colorspace, but doing that breaks
+ // renderengine tests
+ mat4(outputColorSpace.getRGBtoXYZ()) * colorTransform *
+ mat4(outputColorSpace.getXYZtoRGB()))});
tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
// If the input luminance is unknown, use display luminance (aka,
- // no-op any luminance changes)
- // This will be the case for eg screenshots in addition to
- // uncalibrated displays
+ // no-op any luminance changes).
+ // This is expected to only be meaningful for PQ content
.contentMaxLuminance =
maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
.currentDisplayLuminance = currentDisplayLuminanceNits > 0
diff --git a/libs/shaders/tests/shaders_test.cpp b/libs/shaders/tests/shaders_test.cpp
index d45fb24..ba8bed2 100644
--- a/libs/shaders/tests/shaders_test.cpp
+++ b/libs/shaders/tests/shaders_test.cpp
@@ -35,6 +35,10 @@
return arg.name == name && arg.value == value;
}
+MATCHER_P(UniformNameEq, name, "") {
+ return arg.name == name;
+}
+
template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
std::vector<uint8_t> buildUniformValue(T value) {
std::vector<uint8_t> result;
@@ -49,50 +53,44 @@
shaders::LinearEffect effect =
shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
- .fakeInputDataspace = ui::Dataspace::UNKNOWN};
+ .fakeOutputDataspace = ui::Dataspace::UNKNOWN};
mat4 colorTransform = mat4::scale(vec4(.9, .9, .9, 1.));
auto uniforms =
shaders::buildLinearEffectUniforms(effect, colorTransform, 1.f, 1.f, 1.f, nullptr,
aidl::android::hardware::graphics::composer3::
RenderIntent::COLORIMETRIC);
- EXPECT_THAT(uniforms, Contains(UniformEq("in_rgbToXyz", buildUniformValue<mat4>(mat4()))));
EXPECT_THAT(uniforms,
- Contains(UniformEq("in_xyzToRgb", buildUniformValue<mat4>(colorTransform))));
+ Contains(UniformEq("in_rgbToXyz",
+ buildUniformValue<mat3>(
+ ColorSpace::linearExtendedSRGB().getRGBtoXYZ()))));
+ EXPECT_THAT(uniforms,
+ Contains(UniformEq("in_xyzToSrcRgb",
+ buildUniformValue<mat3>(
+ ColorSpace::linearSRGB().getXYZtoRGB()))));
+ // color transforms are already tested in renderengine's tests
+ EXPECT_THAT(uniforms, Contains(UniformNameEq("in_colorTransform")));
}
TEST_F(ShadersTest, buildLinearEffectUniforms_selectsGamutTransformMatrices) {
shaders::LinearEffect effect =
shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB,
.outputDataspace = ui::Dataspace::DISPLAY_P3,
- .fakeInputDataspace = ui::Dataspace::UNKNOWN};
+ .fakeOutputDataspace = ui::Dataspace::UNKNOWN};
ColorSpace inputColorSpace = ColorSpace::sRGB();
- ColorSpace outputColorSpace = ColorSpace::DisplayP3();
auto uniforms =
shaders::buildLinearEffectUniforms(effect, mat4(), 1.f, 1.f, 1.f, nullptr,
aidl::android::hardware::graphics::composer3::
RenderIntent::COLORIMETRIC);
EXPECT_THAT(uniforms,
Contains(UniformEq("in_rgbToXyz",
- buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ())))));
+ buildUniformValue<mat3>(
+ ColorSpace::linearExtendedSRGB().getRGBtoXYZ()))));
EXPECT_THAT(uniforms,
- Contains(UniformEq("in_xyzToRgb",
- buildUniformValue<mat4>(mat4(outputColorSpace.getXYZtoRGB())))));
-}
-
-TEST_F(ShadersTest, buildLinearEffectUniforms_respectsFakeInputDataspace) {
- shaders::LinearEffect effect =
- shaders::LinearEffect{.inputDataspace = ui::Dataspace::V0_SRGB,
- .outputDataspace = ui::Dataspace::DISPLAY_P3,
- .fakeInputDataspace = ui::Dataspace::DISPLAY_P3};
-
- auto uniforms =
- shaders::buildLinearEffectUniforms(effect, mat4(), 1.f, 1.f, 1.f, nullptr,
- aidl::android::hardware::graphics::composer3::
- RenderIntent::COLORIMETRIC);
- EXPECT_THAT(uniforms, Contains(UniformEq("in_rgbToXyz", buildUniformValue<mat4>(mat4()))));
- EXPECT_THAT(uniforms, Contains(UniformEq("in_xyzToRgb", buildUniformValue<mat4>(mat4()))));
+ Contains(UniformEq("in_xyzToSrcRgb",
+ buildUniformValue<mat3>(inputColorSpace.getXYZtoRGB()))));
+ EXPECT_THAT(uniforms, Contains(UniformNameEq("in_colorTransform")));
}
} // namespace android
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrutils.h b/libs/ultrahdr/include/ultrahdr/jpegrutils.h
index ed38e07..4ab664e 100644
--- a/libs/ultrahdr/include/ultrahdr/jpegrutils.h
+++ b/libs/ultrahdr/include/ultrahdr/jpegrutils.h
@@ -102,7 +102,9 @@
* xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
* <rdf:Description
* xmlns:Container="http://ns.google.com/photos/1.0/container/"
- * xmlns:Item="http://ns.google.com/photos/1.0/container/item/">
+ * xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
+ * xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
+ * hdrgm:Version="1">
* <Container:Directory>
* <rdf:Seq>
* <rdf:li
@@ -127,7 +129,8 @@
* @param secondary_image_length length of secondary image
* @return XMP metadata in type of string
*/
-std::string generateXmpForPrimaryImage(int secondary_image_length);
+std::string generateXmpForPrimaryImage(int secondary_image_length,
+ ultrahdr_metadata_struct& metadata);
/*
* This method generates XMP metadata for the recovery map image.
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
index 302aeee..f970936 100644
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
@@ -48,7 +48,7 @@
*/
struct ultrahdr_metadata_struct {
// Ultra HDR library version
- uint32_t version;
+ const char* version;
// Max Content Boost for the map
float maxContentBoost;
// Min Content Boost for the map
@@ -58,4 +58,4 @@
} // namespace android::ultrahdr
-#endif //ANDROID_ULTRAHDR_ULTRAHDR_H
\ No newline at end of file
+#endif //ANDROID_ULTRAHDR_ULTRAHDR_H
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 5f55d1b..0f7aa27 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -61,7 +61,7 @@
}
// The current JPEGR version that we encode to
-static const uint32_t kJpegrVersion = 1;
+static const char* const kJpegrVersion = "1.0";
// Map is quarter res / sixteenth size
static const size_t kMapDimensionScaleFactor = 4;
@@ -943,7 +943,7 @@
+ xmp_secondary_length
+ compressed_gain_map->length;
// primary image
- const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size);
+ const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
// same as primary
const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size();
diff --git a/libs/ultrahdr/jpegrutils.cpp b/libs/ultrahdr/jpegrutils.cpp
index 9d07a6f..6430af1 100644
--- a/libs/ultrahdr/jpegrutils.cpp
+++ b/libs/ultrahdr/jpegrutils.cpp
@@ -302,7 +302,7 @@
return true;
}
-string generateXmpForPrimaryImage(int secondary_image_length) {
+string generateXmpForPrimaryImage(int secondary_image_length, ultrahdr_metadata_struct& metadata) {
const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
const vector<string> kLiItem({string("rdf:li"), kConItem});
@@ -316,6 +316,8 @@
writer.StartWritingElement("rdf:Description");
writer.WriteXmlns(kContainerPrefix, kContainerUri);
writer.WriteXmlns(kItemPrefix, kItemUri);
+ writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
+ writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
writer.StartWritingElements(kConDirSeq);
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
index ba3b4d0..58cd8f4 100644
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ b/libs/ultrahdr/tests/jpegr_test.cpp
@@ -180,6 +180,7 @@
TEST_F(JpegRTest, writeXmpThenRead) {
ultrahdr_metadata_struct metadata_expected;
+ metadata_expected.version = "1.0";
metadata_expected.maxContentBoost = 1.25;
metadata_expected.minContentBoost = 0.75;
const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -538,7 +539,7 @@
JpegRBenchmark benchmark;
- ultrahdr_metadata_struct metadata = { .version = 1,
+ ultrahdr_metadata_struct metadata = { .version = "1.0",
.maxContentBoost = 8.0f,
.minContentBoost = 1.0f / 8.0f };
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 501bf58..32c21f6 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -697,6 +697,11 @@
#define EGL_EXT_device_query 1
#endif /* EGL_EXT_device_query */
+#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
+#define EGL_EXT_gl_colorspace_bt2020_hlg 1
+#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x333E
+#endif /* EGL_EXT_gl_colorspace_bt2020_hlg */
+
#ifndef EGL_EXT_gl_colorspace_bt2020_linear
#define EGL_EXT_gl_colorspace_bt2020_linear 1
#define EGL_GL_COLORSPACE_BT2020_LINEAR_EXT 0x333F
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index aecfc6b..b3a4bc1 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -15,6 +15,7 @@
*/
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "BlobCache.h"
@@ -22,6 +23,7 @@
#include <errno.h>
#include <inttypes.h>
#include <log/log.h>
+#include <utils/Trace.h>
#include <chrono>
@@ -230,6 +232,8 @@
}
int BlobCache::unflatten(void const* buffer, size_t size) {
+ ATRACE_NAME("BlobCache::unflatten");
+
// All errors should result in the BlobCache being in an empty state.
clear();
@@ -293,6 +297,8 @@
}
void BlobCache::clean() {
+ ATRACE_NAME("BlobCache::clean");
+
// Remove a random cache entry until the total cache size gets below half
// the maximum total cache size.
while (mTotalSize > mMaxTotalSize / 2) {
diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp
index 1026842..4a0fac4 100644
--- a/opengl/libs/EGL/FileBlobCache.cpp
+++ b/opengl/libs/EGL/FileBlobCache.cpp
@@ -14,6 +14,8 @@
** limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include "FileBlobCache.h"
#include <errno.h>
@@ -24,6 +26,7 @@
#include <unistd.h>
#include <log/log.h>
+#include <utils/Trace.h>
// Cache file header
static const char* cacheFileMagic = "EGL$";
@@ -51,6 +54,8 @@
const std::string& filename)
: BlobCache(maxKeySize, maxValueSize, maxTotalSize)
, mFilename(filename) {
+ ATRACE_CALL();
+
if (mFilename.length() > 0) {
size_t headerSize = cacheFileHeaderSize;
@@ -117,6 +122,8 @@
}
void FileBlobCache::writeToFile() {
+ ATRACE_CALL();
+
if (mFilename.length() > 0) {
size_t cacheSize = getFlattenedSize();
size_t headerSize = cacheFileHeaderSize;
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 415e8ea..2c3ce16 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -139,9 +139,10 @@
static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
-static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
- DRIVER_SUFFIX_PROPERTY,
- "ro.board.platform",
+static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = {
+ "persist.graphics.egl",
+ DRIVER_SUFFIX_PROPERTY,
+ "ro.board.platform",
};
static bool should_unload_system_driver(egl_connection_t* cnx) {
@@ -208,8 +209,7 @@
ATRACE_CALL();
const nsecs_t openTime = systemTime();
- if (!android::GraphicsEnv::getInstance().angleIsSystemDriver() &&
- should_unload_system_driver(cnx)) {
+ if (should_unload_system_driver(cnx)) {
unload_system_driver(cnx);
}
@@ -218,12 +218,8 @@
return cnx->dso;
}
- // Firstly, try to load ANGLE driver, unless we know that we shouldn't.
- bool shouldForceLegacyDriver = android::GraphicsEnv::getInstance().shouldForceLegacyDriver();
- driver_t* hnd = nullptr;
- if (!shouldForceLegacyDriver) {
- hnd = attempt_to_load_angle(cnx);
- }
+ // Firstly, try to load ANGLE driver.
+ driver_t* hnd = attempt_to_load_angle(cnx);
if (!hnd) {
// Secondly, try to load from driver apk.
@@ -285,8 +281,10 @@
}
LOG_ALWAYS_FATAL_IF(!hnd,
- "couldn't find an OpenGL ES implementation, make sure you set %s or %s",
- HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1]);
+ "couldn't find an OpenGL ES implementation, make sure one of %s, %s and %s "
+ "is set",
+ HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1],
+ HAL_SUBNAME_KEY_PROPERTIES[2]);
if (!cnx->libEgl) {
cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 6593c1b..525fed1 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -353,8 +353,9 @@
// Typically that means there is an HDR capable display attached, but could be
// support for attaching an HDR display. In either case, advertise support for
// HDR color spaces.
- mExtensionString.append(
- "EGL_EXT_gl_colorspace_bt2020_linear EGL_EXT_gl_colorspace_bt2020_pq ");
+ mExtensionString.append("EGL_EXT_gl_colorspace_bt2020_hlg "
+ "EGL_EXT_gl_colorspace_bt2020_linear "
+ "EGL_EXT_gl_colorspace_bt2020_pq ");
}
char const* start = gExtensionString;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 2bca14d..48718bb 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -18,6 +18,7 @@
#include "egl_platform_entries.h"
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware_buffer.h>
@@ -29,7 +30,6 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <stdlib.h>
#include <string.h>
-#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <condition_variable>
#include <deque>
@@ -421,11 +421,14 @@
return HAL_DATASPACE_V0_SCRGB;
} else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
return HAL_DATASPACE_V0_SCRGB_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_HLG_EXT) {
+ return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
return HAL_DATASPACE_BT2020_LINEAR;
} else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
return HAL_DATASPACE_BT2020_PQ;
}
+
return HAL_DATASPACE_UNKNOWN;
}
@@ -452,6 +455,9 @@
if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
}
+ if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_hlg")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_HLG_EXT);
+ }
if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) {
colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
}
@@ -485,6 +491,7 @@
case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
case EGL_GL_COLORSPACE_SCRGB_EXT:
+ case EGL_GL_COLORSPACE_BT2020_HLG_EXT:
case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
case EGL_GL_COLORSPACE_BT2020_PQ_EXT:
case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT:
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 614a78e..72bd292 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -53,6 +53,4 @@
vintf_fragments: [
"manifest_android.frameworks.automotive.display@1.0.xml",
],
-
- system_ext_specific: true,
}
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index ea1077a..5c7f344 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -1,4 +1,4 @@
-service automotive_display /system_ext/bin/android.frameworks.automotive.display@1.0-service
+service automotive_display /system/bin/android.frameworks.automotive.display@1.0-service
class hal
user graphics
group automotive_evs
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index e04481c..69df45b 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -224,6 +224,9 @@
"libinputservice_test",
"Bug-115739809",
"StructLayout_test",
+ // currently unused, but still must build correctly
+ "inputflinger",
+ "libinputflingerhost",
// native fuzzers
"inputflinger_latencytracker_fuzzer",
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 472d7a1..ddebcad 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -57,9 +57,8 @@
* The event flow is via the "InputListener" interface, as follows:
* InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher
*/
-InputManager::InputManager(
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
+InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
+ InputDispatcherPolicyInterface& dispatcherPolicy) {
mDispatcher = createInputDispatcher(dispatcherPolicy);
mProcessor = std::make_unique<InputProcessor>(*mDispatcher);
mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor);
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 793757d..b6ad419 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,9 +100,8 @@
~InputManager() override;
public:
- InputManager(
- const sp<InputReaderPolicyInterface>& readerPolicy,
- const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
+ InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
+ InputDispatcherPolicyInterface& dispatcherPolicy);
status_t start() override;
status_t stop() override;
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 2450235..4ec5b89 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -38,50 +38,6 @@
// --- InputReaderConfiguration ---
-std::string InputReaderConfiguration::changesToString(uint32_t changes) {
- if (changes == 0) {
- return "<none>";
- }
- std::string result;
- if (changes & CHANGE_POINTER_SPEED) {
- result += "POINTER_SPEED | ";
- }
- if (changes & CHANGE_POINTER_GESTURE_ENABLEMENT) {
- result += "POINTER_GESTURE_ENABLEMENT | ";
- }
- if (changes & CHANGE_DISPLAY_INFO) {
- result += "DISPLAY_INFO | ";
- }
- if (changes & CHANGE_SHOW_TOUCHES) {
- result += "SHOW_TOUCHES | ";
- }
- if (changes & CHANGE_KEYBOARD_LAYOUTS) {
- result += "KEYBOARD_LAYOUTS | ";
- }
- if (changes & CHANGE_DEVICE_ALIAS) {
- result += "DEVICE_ALIAS | ";
- }
- if (changes & CHANGE_TOUCH_AFFINE_TRANSFORMATION) {
- result += "TOUCH_AFFINE_TRANSFORMATION | ";
- }
- if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
- result += "EXTERNAL_STYLUS_PRESENCE | ";
- }
- if (changes & CHANGE_POINTER_CAPTURE) {
- result += "POINTER_CAPTURE | ";
- }
- if (changes & CHANGE_ENABLED_STATE) {
- result += "ENABLED_STATE | ";
- }
- if (changes & CHANGE_TOUCHPAD_SETTINGS) {
- result += "TOUCHPAD_SETTINGS | ";
- }
- if (changes & CHANGE_MUST_REOPEN) {
- result += "MUST_REOPEN | ";
- }
- return result;
-}
-
std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
const std::string& uniqueDisplayId) const {
if (uniqueDisplayId.empty()) {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index f852001..f65533e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -48,10 +48,8 @@
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
public:
- FakeInputDispatcherPolicy() {}
-
-protected:
- virtual ~FakeInputDispatcherPolicy() {}
+ FakeInputDispatcherPolicy() = default;
+ virtual ~FakeInputDispatcherPolicy() = default;
private:
void notifyConfigurationChanged(nsecs_t) override {}
@@ -82,24 +80,23 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
- *outConfig = mConfig;
+ InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
+
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
+ return true; // dispatch event normally
}
- bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
- return true;
- }
-
- void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
return 0;
}
- bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
- return false;
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) override {
+ return {};
}
void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
@@ -258,7 +255,7 @@
static void benchmarkNotifyMotion(benchmark::State& state) {
// Create dispatcher
- sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(fakePolicy);
dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher.start();
@@ -293,7 +290,7 @@
static void benchmarkInjectMotion(benchmark::State& state) {
// Create dispatcher
- sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(fakePolicy);
dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher.start();
@@ -327,7 +324,7 @@
static void benchmarkOnWindowInfosChanged(benchmark::State& state) {
// Create dispatcher
- sp<FakeInputDispatcherPolicy> fakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(fakePolicy);
dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher.start();
@@ -343,8 +340,10 @@
std::vector<gui::DisplayInfo> displayInfos{info};
for (auto _ : state) {
- dispatcher.onWindowInfosChanged(windowInfos, displayInfos);
- dispatcher.onWindowInfosChanged(/*windowInfos=*/{}, /*displayInfos=*/{});
+ dispatcher.onWindowInfosChanged(
+ {windowInfos, displayInfos, /*vsyncId=*/0, /*timestamp=*/0});
+ dispatcher.onWindowInfosChanged(
+ {/*windowInfos=*/{}, /*displayInfos=*/{}, /*vsyncId=*/{}, /*timestamp=*/0});
}
dispatcher.stop();
}
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index b4497fd..ed95de7 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -28,8 +28,6 @@
inputPublisher(inputChannel),
inputState(idGenerator) {}
-Connection::~Connection() {}
-
const std::string Connection::getWindowName() const {
if (inputChannel != nullptr) {
return inputChannel->getName();
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index 6040e9b..2929d61 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -27,10 +27,7 @@
struct DispatchEntry;
/* Manages the dispatch state associated with a single input channel. */
-class Connection : public RefBase {
-protected:
- virtual ~Connection();
-
+class Connection {
public:
enum class Status {
// Everything is peachy.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e8434be..326ca87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -54,6 +54,7 @@
#define INDENT4 " "
using namespace android::ftl::flag_operators;
+using android::base::Error;
using android::base::HwTimeoutMultiplier;
using android::base::Result;
using android::base::StringPrintf;
@@ -129,48 +130,68 @@
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
-bool isValidKeyAction(int32_t action) {
+Result<void> checkKeyAction(int32_t action) {
switch (action) {
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
- return true;
+ return {};
default:
- return false;
+ return Error() << "Key event has invalid action code " << action;
}
}
-bool validateKeyEvent(int32_t action) {
- if (!isValidKeyAction(action)) {
- ALOGE("Key event has invalid action code 0x%x", action);
- return false;
- }
- return true;
+Result<void> validateKeyEvent(int32_t action) {
+ return checkKeyAction(action);
}
-bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
+Result<void> checkMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
switch (MotionEvent::getActionMasked(action)) {
case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_UP:
- return pointerCount == 1;
+ case AMOTION_EVENT_ACTION_UP: {
+ if (pointerCount != 1) {
+ return Error() << "invalid pointer count " << pointerCount;
+ }
+ return {};
+ }
case AMOTION_EVENT_ACTION_MOVE:
case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
- case AMOTION_EVENT_ACTION_HOVER_EXIT:
- return pointerCount >= 1;
+ case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+ if (pointerCount < 1) {
+ return Error() << "invalid pointer count " << pointerCount;
+ }
+ return {};
+ }
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_SCROLL:
- return true;
+ return {};
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
const int32_t index = MotionEvent::getActionIndex(action);
- return index >= 0 && index < pointerCount && pointerCount > 1;
+ if (index < 0) {
+ return Error() << "invalid index " << index << " for "
+ << MotionEvent::actionToString(action);
+ }
+ if (index >= pointerCount) {
+ return Error() << "invalid index " << index << " for pointerCount " << pointerCount;
+ }
+ if (pointerCount <= 1) {
+ return Error() << "invalid pointer count " << pointerCount << " for "
+ << MotionEvent::actionToString(action);
+ }
+ return {};
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
- case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
- return actionButton != 0;
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ if (actionButton == 0) {
+ return Error() << "action button should be nonzero for "
+ << MotionEvent::actionToString(action);
+ }
+ return {};
+ }
default:
- return false;
+ return Error() << "invalid action " << action;
}
}
@@ -178,32 +199,50 @@
return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
}
-bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
- const PointerProperties* pointerProperties) {
- if (!isValidMotionAction(action, actionButton, pointerCount)) {
- ALOGE("Motion event has invalid action code 0x%x", action);
- return false;
+Result<void> validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
+ const PointerProperties* pointerProperties) {
+ Result<void> actionCheck = checkMotionAction(action, actionButton, pointerCount);
+ if (!actionCheck.ok()) {
+ return actionCheck;
}
if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
- ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %zu.",
- pointerCount, MAX_POINTERS);
- return false;
+ return Error() << "Motion event has invalid pointer count " << pointerCount
+ << "; value must be between 1 and " << MAX_POINTERS << ".";
}
std::bitset<MAX_POINTER_ID + 1> pointerIdBits;
for (size_t i = 0; i < pointerCount; i++) {
int32_t id = pointerProperties[i].id;
if (id < 0 || id > MAX_POINTER_ID) {
- ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", id,
- MAX_POINTER_ID);
- return false;
+ return Error() << "Motion event has invalid pointer id " << id
+ << "; value must be between 0 and " << MAX_POINTER_ID;
}
if (pointerIdBits.test(id)) {
- ALOGE("Motion event has duplicate pointer id %d", id);
- return false;
+ return Error() << "Motion event has duplicate pointer id " << id;
}
pointerIdBits.set(id);
}
- return true;
+ return {};
+}
+
+Result<void> validateInputEvent(const InputEvent& event) {
+ switch (event.getType()) {
+ case InputEventType::KEY: {
+ const KeyEvent& key = static_cast<const KeyEvent&>(event);
+ const int32_t action = key.getAction();
+ return validateKeyEvent(action);
+ }
+ case InputEventType::MOTION: {
+ const MotionEvent& motion = static_cast<const MotionEvent&>(event);
+ const int32_t action = motion.getAction();
+ const size_t pointerCount = motion.getPointerCount();
+ const PointerProperties* pointerProperties = motion.getPointerProperties();
+ const int32_t actionButton = motion.getActionButton();
+ return validateMotionEvent(action, actionButton, pointerCount, pointerProperties);
+ }
+ default: {
+ return {};
+ }
+ }
}
std::string dumpRegion(const Region& region) {
@@ -459,14 +498,14 @@
// Returns true if the event type passed as argument represents a user activity.
bool isUserActivityEvent(const EventEntry& eventEntry) {
switch (eventEntry.type) {
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::DRAG:
case EventEntry::Type::FOCUS:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::DRAG:
- case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
return false;
- case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::KEY:
case EventEntry::Type::MOTION:
return true;
@@ -646,10 +685,10 @@
// --- InputDispatcher ---
-InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
+InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
: InputDispatcher(policy, STALE_EVENT_TIMEOUT) {}
-InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::chrono::nanoseconds staleEventTimeout)
: mPolicy(policy),
mPendingEvent(nullptr),
@@ -676,7 +715,6 @@
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
#endif
mKeyRepeatState.lastKeyEntry = nullptr;
- policy->getDispatcherConfiguration(&mConfig);
}
InputDispatcher::~InputDispatcher() {
@@ -688,7 +726,7 @@
mCommandQueue.clear();
while (!mConnectionsByToken.empty()) {
- sp<Connection> connection = mConnectionsByToken.begin()->second;
+ std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
removeInputChannelLocked(connection->inputChannel->getConnectionToken(), /*notify=*/false);
}
}
@@ -802,7 +840,7 @@
}
// If we reached here, we have an unresponsive connection.
- sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+ std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
if (connection == nullptr) {
ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
return nextAnrCheck;
@@ -815,7 +853,7 @@
}
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
- const sp<Connection>& connection) {
+ const std::shared_ptr<Connection>& connection) {
if (connection->monitor) {
return mMonitorDispatchingTimeout;
}
@@ -1058,7 +1096,8 @@
const std::vector<sp<WindowInfoHandle>> touchedSpies =
findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
for (const auto& windowHandle : touchedSpies) {
- const sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
+ const std::shared_ptr<Connection> connection =
+ getConnectionLocked(windowHandle->getToken());
if (connection != nullptr && connection->responsive) {
// This spy window could take more input. Drop all events preceding this
// event, so that the spy window can get a chance to receive the stream.
@@ -1297,7 +1336,7 @@
(keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER);
}
-bool InputDispatcher::isAppSwitchPendingLocked() {
+bool InputDispatcher::isAppSwitchPendingLocked() const {
return mAppSwitchDueTime != LLONG_MAX;
}
@@ -1401,7 +1440,7 @@
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyConfigurationChanged(eventTime);
+ mPolicy.notifyConfigurationChanged(eventTime);
};
postCommandLocked(std::move(command));
return true;
@@ -1422,6 +1461,11 @@
CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset");
options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
+
+ // Remove all active pointers from this device
+ for (auto& [_, touchState] : mTouchStatesByDisplay) {
+ touchState.removeAllPointersForDevice(entry.deviceId);
+ }
return true;
}
@@ -1645,6 +1689,8 @@
doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
};
postCommandLocked(std::move(command));
+ // Poke user activity for keys not passed to user
+ pokeUserActivityLocked(*entry);
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
@@ -1661,6 +1707,8 @@
*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
: InputEventInjectionResult::FAILED);
mReporter->reportDroppedKey(entry->id);
+ // Poke user activity for undispatched keys
+ pokeUserActivityLocked(*entry);
return true;
}
@@ -1716,10 +1764,10 @@
scoped_unlock unlock(mLock);
if (entry->accuracyChanged) {
- mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+ mPolicy.notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
}
- mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
- entry->hwTimestamp, entry->values);
+ mPolicy.notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+ entry->hwTimestamp, entry->values);
};
postCommandLocked(std::move(command));
}
@@ -1895,7 +1943,7 @@
pokeUserActivityLocked(*eventEntry);
for (const InputTarget& inputTarget : inputTargets) {
- sp<Connection> connection =
+ std::shared_ptr<Connection> connection =
getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
@@ -1909,7 +1957,7 @@
}
}
-void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection>& connection) {
// We will not be breaking any connections here, even if the policy wants us to abort dispatch.
// If the policy decides to close the app, we will get a channel removal event via
// unregisterInputChannel, and will clean up the connection that way. We are already not
@@ -2101,7 +2149,7 @@
std::vector<Monitor> responsiveMonitors;
std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
[this](const Monitor& monitor) REQUIRES(mLock) {
- sp<Connection> connection =
+ std::shared_ptr<Connection> connection =
getConnectionLocked(monitor.inputChannel->getConnectionToken());
if (connection == nullptr) {
ALOGE("Could not find connection for monitor %s",
@@ -2164,7 +2212,7 @@
// event injection will be allowed.
const int32_t displayId = entry.displayId;
const int32_t action = entry.action;
- const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ const int32_t maskedAction = MotionEvent::getActionMasked(action);
// Update the touch state as needed based on the properties of the touch event.
outInjectionResult = InputEventInjectionResult::PENDING;
@@ -2191,7 +2239,9 @@
const bool wasDown = oldState != nullptr && oldState->isDown();
const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
(maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
- const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction;
+ const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
// If pointers are already down, let's finish the current gesture and ignore the new events
@@ -2291,16 +2341,11 @@
continue;
}
- if (isHoverAction) {
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
const int32_t pointerId = entry.pointerProperties[0].id;
- if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
- // Pointer left. Remove it
- tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
- } else {
- // The "windowHandle" is the target of this hovering pointer.
- tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId,
- pointerId);
- }
+ // The "windowHandle" is the target of this hovering pointer.
+ tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointerId);
}
// Set target flags.
@@ -2384,7 +2429,7 @@
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (!tempTouchState.isDown()) {
+ if (!tempTouchState.isDown() && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
LOG(INFO) << "Dropping event because the pointer is not down or we previously "
"dropped the pointer down event in display "
<< displayId << ": " << entry.getDescription();
@@ -2392,6 +2437,20 @@
return {};
}
+ // If the pointer is not currently hovering, then ignore the event.
+ if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+ const int32_t pointerId = entry.pointerProperties[0].id;
+ if (oldState == nullptr ||
+ oldState->getWindowsWithHoveringPointer(entry.deviceId, pointerId).empty()) {
+ LOG(INFO) << "Dropping event because the hovering pointer is not in any windows in "
+ "display "
+ << displayId << ": " << entry.getDescription();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
+ tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
+ }
+
addDragEventLocked(entry);
// Check whether touches should slip outside of the current foreground window.
@@ -2401,6 +2460,7 @@
const bool isStylus = isPointerFromStylus(entry, /*pointerIndex=*/0);
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
+ LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
auto [newTouchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
// Verify targeted injection.
@@ -2416,13 +2476,11 @@
newTouchedWindowHandle = nullptr;
}
- if (oldTouchedWindowHandle != newTouchedWindowHandle &&
- oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
- oldTouchedWindowHandle->getName().c_str(),
- newTouchedWindowHandle->getName().c_str(), displayId);
- }
+ if (!haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) {
+ ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
+ oldTouchedWindowHandle->getName().c_str(),
+ newTouchedWindowHandle->getName().c_str(), displayId);
+
// Make a slippery exit from the old window.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
const int32_t pointerId = entry.pointerProperties[0].id;
@@ -2488,21 +2546,6 @@
targets);
}
}
- // Ensure that we have at least one foreground window or at least one window that cannot be a
- // foreground target. If we only have windows that are not receiving foreground touches (e.g. we
- // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window
- // that is actually receiving the entire gesture.
- if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(),
- [](const TouchedWindow& touchedWindow) {
- return !canReceiveForegroundTouches(
- *touchedWindow.windowHandle->getInfo()) ||
- touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND);
- })) {
- ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
- displayId, entry.getDescription().c_str());
- outInjectionResult = InputEventInjectionResult::FAILED;
- return {};
- }
// Ensure that all touched windows are valid for injection.
if (entry.injectionState != nullptr) {
@@ -2544,7 +2587,7 @@
}
}
- // Success! Output targets from the touch state.
+ // Output targets from the touch state.
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
// Windows with hovering pointers are getting persisted inside TouchState.
@@ -2556,6 +2599,23 @@
targets);
}
+ if (targets.empty()) {
+ LOG(INFO) << "Dropping event because no targets were found: " << entry.getDescription();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
+
+ // If we only have windows getting ACTION_OUTSIDE, then drop the event, because there is no
+ // window that is actually receiving the entire gesture.
+ if (std::all_of(targets.begin(), targets.end(), [](const InputTarget& target) {
+ return target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE);
+ })) {
+ LOG(INFO) << "Dropping event because all windows would just receive ACTION_OUTSIDE: "
+ << entry.getDescription();
+ outInjectionResult = InputEventInjectionResult::FAILED;
+ return {};
+ }
+
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
// Drop the outside or hover touch windows since we will not care about them
// in the next iteration.
@@ -2666,9 +2726,6 @@
if (uint32_t(pointerIndex) == entry.pointerCount) {
LOG_ALWAYS_FATAL("Should find a valid pointer index by id %d", mDragState->pointerId);
- sendDropWindowCommandLocked(nullptr, 0, 0);
- mDragState.reset();
- return;
}
const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
@@ -2980,13 +3037,11 @@
}
int32_t displayId = getTargetDisplayId(eventEntry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
+ const WindowInfo* windowDisablingUserActivityInfo = nullptr;
if (focusedWindowHandle != nullptr) {
const WindowInfo* info = focusedWindowHandle->getInfo();
if (info->inputConfig.test(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY)) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
- }
- return;
+ windowDisablingUserActivityInfo = info;
}
}
@@ -2997,7 +3052,13 @@
if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
return;
}
-
+ if (windowDisablingUserActivityInfo != nullptr) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("Not poking user activity: disabled by window '%s'.",
+ windowDisablingUserActivityInfo->name.c_str());
+ }
+ return;
+ }
if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
eventType = USER_ACTIVITY_EVENT_TOUCH;
}
@@ -3008,6 +3069,22 @@
if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
return;
}
+ // If the key code is unknown, we don't consider it user activity
+ if (keyEntry.keyCode == AKEYCODE_UNKNOWN) {
+ return;
+ }
+ // Don't inhibit events that were intercepted or are not passed to
+ // the apps, like system shortcuts
+ if (windowDisablingUserActivityInfo != nullptr &&
+ keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP &&
+ keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("Not poking user activity: disabled by window '%s'.",
+ windowDisablingUserActivityInfo->name.c_str());
+ }
+ return;
+ }
+
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
@@ -3021,13 +3098,13 @@
auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+ mPolicy.pokeUserActivity(eventTime, eventType, displayId);
};
postCommandLocked(std::move(command));
}
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection,
+ const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
@@ -3097,7 +3174,7 @@
}
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
- const sp<Connection>& connection,
+ const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
@@ -3131,7 +3208,7 @@
}
}
-void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
+void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget,
ftl::Flags<InputTarget::Flags> dispatchMode) {
@@ -3316,14 +3393,14 @@
}
std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens;
- std::vector<sp<Connection>> newConnections;
+ std::vector<std::shared_ptr<Connection>> newConnections;
for (const InputTarget& target : targets) {
if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
continue; // Skip windows that receive ACTION_OUTSIDE
}
sp<IBinder> token = target.inputChannel->getConnectionToken();
- sp<Connection> connection = getConnectionLocked(token);
+ std::shared_ptr<Connection> connection = getConnectionLocked(token);
if (connection == nullptr) {
continue;
}
@@ -3336,7 +3413,7 @@
mInteractionConnectionTokens = newConnectionTokens;
std::string targetList;
- for (const sp<Connection>& connection : newConnections) {
+ for (const std::shared_ptr<Connection>& connection : newConnections) {
targetList += connection->getWindowName() + ", ";
}
std::string message = "Interaction with: " + targetList;
@@ -3362,7 +3439,7 @@
auto command = [this, token]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->onPointerDownOutsideFocus(token);
+ mPolicy.onPointerDownOutsideFocus(token);
};
postCommandLocked(std::move(command));
}
@@ -3416,7 +3493,7 @@
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
+ const std::shared_ptr<Connection>& connection) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
connection->getInputChannelName().c_str());
@@ -3573,7 +3650,7 @@
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
- const int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
+ const int32_t actionMasked = MotionEvent::getActionMasked(dispatchEntry.resolvedAction);
if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) {
// Only sign events up and down events as the purely move events
// are tied to their up/down counterparts so signing would be redundant.
@@ -3596,8 +3673,8 @@
}
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection, uint32_t seq,
- bool handled, nsecs_t consumeTime) {
+ const std::shared_ptr<Connection>& connection,
+ uint32_t seq, bool handled, nsecs_t consumeTime) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
connection->getInputChannelName().c_str(), seq, toString(handled));
@@ -3616,7 +3693,7 @@
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection,
+ const std::shared_ptr<Connection>& connection,
bool notify) {
if (DEBUG_DISPATCH_CYCLE) {
LOG(DEBUG) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
@@ -3641,7 +3718,7 @@
auto command = [this, connection]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+ mPolicy.notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
};
postCommandLocked(std::move(command));
}
@@ -3665,7 +3742,7 @@
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
std::scoped_lock _l(mLock);
- sp<Connection> connection = getConnectionLocked(connectionToken);
+ std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",
connectionToken.get(), events);
@@ -3757,7 +3834,7 @@
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) {
- sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
+ std::shared_ptr<Connection> connection = getConnectionLocked(channel->getConnectionToken());
if (connection == nullptr) {
return;
}
@@ -3766,7 +3843,7 @@
}
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
- const sp<Connection>& connection, const CancelationOptions& options) {
+ const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3844,7 +3921,7 @@
}
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
- const nsecs_t downTime, const sp<Connection>& connection,
+ const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
ftl::Flags<InputTarget::Flags> targetFlags) {
if (connection->status == Connection::Status::BROKEN) {
return;
@@ -3909,7 +3986,8 @@
void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) {
if (windowHandle != nullptr) {
- sp<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken());
+ std::shared_ptr<Connection> wallpaperConnection =
+ getConnectionLocked(windowHandle->getToken());
if (wallpaperConnection != nullptr) {
synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options);
}
@@ -4092,7 +4170,9 @@
args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
args.displayId, args.policyFlags, KeyEvent::actionToString(args.action), args.flags,
KeyEvent::getLabel(args.keyCode), args.scanCode, args.metaState, args.downTime);
- if (!validateKeyEvent(args.action)) {
+ Result<void> keyCheck = validateKeyEvent(args.action);
+ if (!keyCheck.ok()) {
+ LOG(ERROR) << "invalid key event: " << keyCheck.error();
return;
}
@@ -4121,7 +4201,7 @@
args.eventTime);
android::base::Timer t;
- mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
+ mPolicy.interceptKeyBeforeQueueing(event, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4135,7 +4215,7 @@
mLock.unlock();
policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ if (!mPolicy.filterInputEvent(event, policyFlags)) {
return; // event was consumed by the filter
}
@@ -4189,9 +4269,10 @@
}
}
- if (!validateMotionEvent(args.action, args.actionButton, args.pointerCount,
- args.pointerProperties)) {
- LOG(ERROR) << "Invalid event: " << args.dump();
+ Result<void> motionCheck = validateMotionEvent(args.action, args.actionButton,
+ args.pointerCount, args.pointerProperties);
+ if (!motionCheck.ok()) {
+ LOG(ERROR) << "Invalid event: " << args.dump() << "; reason: " << motionCheck.error();
return;
}
@@ -4199,7 +4280,7 @@
policyFlags |= POLICY_FLAG_TRUSTED;
android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4238,7 +4319,7 @@
args.pointerProperties, args.pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
- if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+ if (!mPolicy.filterInputEvent(event, policyFlags)) {
return; // event was consumed by the filter
}
@@ -4304,7 +4385,7 @@
ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args.eventTime,
args.deviceId, args.isOn);
}
- mPolicy->notifyVibratorState(args.deviceId, args.isOn);
+ mPolicy.notifyVibratorState(args.deviceId, args.isOn);
}
bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs& args) {
@@ -4320,7 +4401,7 @@
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->notifySwitch(args.eventTime, args.switchValues, args.switchMask, policyFlags);
+ mPolicy.notifySwitch(args.eventTime, args.switchValues, args.switchMask, policyFlags);
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
@@ -4367,6 +4448,12 @@
InputEventInjectionSync syncMode,
std::chrono::milliseconds timeout,
uint32_t policyFlags) {
+ Result<void> eventValidation = validateInputEvent(*event);
+ if (!eventValidation.ok()) {
+ LOG(INFO) << "Injection failed: invalid event: " << eventValidation.error();
+ return InputEventInjectionResult::FAILED;
+ }
+
if (debugInboundEventDetails()) {
LOG(DEBUG) << __func__ << ": targetUid=" << toString(targetUid)
<< ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
@@ -4392,11 +4479,7 @@
switch (event->getType()) {
case InputEventType::KEY: {
const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
- int32_t action = incomingKey.getAction();
- if (!validateKeyEvent(action)) {
- return InputEventInjectionResult::FAILED;
- }
-
+ const int32_t action = incomingKey.getAction();
int32_t flags = incomingKey.getFlags();
if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
flags |= AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
@@ -4417,7 +4500,7 @@
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
android::base::Timer t;
- mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
+ mPolicy.interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4438,25 +4521,18 @@
case InputEventType::MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- const int32_t action = motionEvent.getAction();
const bool isPointerEvent =
isFromSource(event->getSource(), AINPUT_SOURCE_CLASS_POINTER);
// If a pointer event has no displayId specified, inject it to the default display.
const uint32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE)
? ADISPLAY_ID_DEFAULT
: event->getDisplayId();
- const size_t pointerCount = motionEvent.getPointerCount();
- const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
- const int32_t actionButton = motionEvent.getActionButton();
int32_t flags = motionEvent.getFlags();
- if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
- return InputEventInjectionResult::FAILED;
- }
if (!(policyFlags & POLICY_FLAG_FILTERED)) {
nsecs_t eventTime = motionEvent.getEventTime();
android::base::Timer t;
- mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
+ mPolicy.interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -4473,8 +4549,9 @@
std::unique_ptr<MotionEntry> injectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags, action, actionButton,
- flags, motionEvent.getMetaState(),
+ displayId, policyFlags, motionEvent.getAction(),
+ motionEvent.getActionButton(), flags,
+ motionEvent.getMetaState(),
motionEvent.getButtonState(),
motionEvent.getClassification(),
motionEvent.getEdgeFlags(),
@@ -4482,18 +4559,22 @@
motionEvent.getYPrecision(),
motionEvent.getRawXCursorPosition(),
motionEvent.getRawYCursorPosition(),
- motionEvent.getDownTime(), uint32_t(pointerCount),
- pointerProperties, samplePointerCoords);
+ motionEvent.getDownTime(),
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
+ samplePointerCoords);
transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
- samplePointerCoords += pointerCount;
+ samplePointerCoords += motionEvent.getPointerCount();
std::unique_ptr<MotionEntry> nextInjectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
resolvedDeviceId, motionEvent.getSource(),
- displayId, policyFlags, action, actionButton,
- flags, motionEvent.getMetaState(),
+ displayId, policyFlags,
+ motionEvent.getAction(),
+ motionEvent.getActionButton(), flags,
+ motionEvent.getMetaState(),
motionEvent.getButtonState(),
motionEvent.getClassification(),
motionEvent.getEdgeFlags(),
@@ -4502,7 +4583,8 @@
motionEvent.getRawXCursorPosition(),
motionEvent.getRawYCursorPosition(),
motionEvent.getDownTime(),
- uint32_t(pointerCount), pointerProperties,
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
samplePointerCoords);
transformMotionEntryForInjectionLocked(*nextInjectedEntry,
motionEvent.getTransform());
@@ -4796,7 +4878,7 @@
return false;
}
- sp<Connection> connection = getConnectionLocked(window->getToken());
+ std::shared_ptr<Connection> connection = getConnectionLocked(window->getToken());
if (connection == nullptr) {
ALOGW("Not sending touch to %s because there's no corresponding connection",
window->getName().c_str());
@@ -5318,8 +5400,8 @@
}
// Synthesize cancel for old window and down for new window.
- sp<Connection> fromConnection = getConnectionLocked(fromToken);
- sp<Connection> toConnection = getConnectionLocked(toToken);
+ std::shared_ptr<Connection> fromConnection = getConnectionLocked(fromToken);
+ std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions
@@ -5411,7 +5493,7 @@
mReplacedKeys.clear();
}
-void InputDispatcher::logDispatchStateLocked() {
+void InputDispatcher::logDispatchStateLocked() const {
std::string dump;
dumpDispatchStateLocked(dump);
@@ -5419,11 +5501,11 @@
std::string line;
while (std::getline(stream, line, '\n')) {
- ALOGD("%s", line.c_str());
+ ALOGI("%s", line.c_str());
}
}
-std::string InputDispatcher::dumpPointerCaptureStateLocked() {
+std::string InputDispatcher::dumpPointerCaptureStateLocked() const {
std::string dump;
dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
@@ -5441,7 +5523,7 @@
return dump;
}
-void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
+void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const {
dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled));
@@ -5529,6 +5611,14 @@
} else {
dump += INDENT "Displays: <none>\n";
}
+ dump += INDENT "Window Infos:\n";
+ dump += StringPrintf(INDENT2 "vsync id: %" PRId64 "\n", mWindowInfosVsyncId);
+ dump += StringPrintf(INDENT2 "timestamp (ns): %" PRId64 "\n", mWindowInfosTimestamp);
+ dump += "\n";
+ dump += StringPrintf(INDENT2 "max update delay (ns): %" PRId64 "\n", mMaxWindowInfosDelay);
+ dump += StringPrintf(INDENT2 "max update delay vsync id: %" PRId64 "\n",
+ mMaxWindowInfosDelayVsyncId);
+ dump += "\n";
if (!mGlobalMonitorsByDisplay.empty()) {
for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) {
@@ -5544,7 +5634,7 @@
// Dump recently dispatched or dropped events from oldest to newest.
if (!mRecentQueue.empty()) {
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
- for (std::shared_ptr<EventEntry>& entry : mRecentQueue) {
+ for (const std::shared_ptr<EventEntry>& entry : mRecentQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5567,7 +5657,7 @@
// Dump inbound events from oldest to newest.
if (!mInboundQueue.empty()) {
dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
- for (std::shared_ptr<EventEntry>& entry : mInboundQueue) {
+ for (const std::shared_ptr<EventEntry>& entry : mInboundQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -5649,7 +5739,7 @@
dump += mLatencyAggregator.dump(INDENT2);
}
-void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
+void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const {
const size_t numMonitors = monitors.size();
for (size_t i = 0; i < numMonitors; i++) {
const Monitor& monitor = monitors[i];
@@ -5685,8 +5775,9 @@
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
- sp<Connection> connection =
- sp<Connection>::make(std::move(serverChannel), /*monitor=*/false, mIdGenerator);
+ std::shared_ptr<Connection> connection =
+ std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false,
+ mIdGenerator);
if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
ALOGE("Created a new connection, but the token %p is already known", token.get());
@@ -5723,8 +5814,8 @@
<< " without a specified display.";
}
- sp<Connection> connection =
- sp<Connection>::make(serverChannel, /*monitor=*/true, mIdGenerator);
+ std::shared_ptr<Connection> connection =
+ std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator);
const sp<IBinder>& token = serverChannel->getConnectionToken();
const int fd = serverChannel->getFd();
@@ -5764,7 +5855,7 @@
status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
bool notify) {
- sp<Connection> connection = getConnectionLocked(connectionToken);
+ std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
// Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
return BAD_VALUE;
@@ -5910,7 +6001,8 @@
return std::nullopt;
}
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
+std::shared_ptr<Connection> InputDispatcher::getConnectionLocked(
+ const sp<IBinder>& inputConnectionToken) const {
if (inputConnectionToken == nullptr) {
return nullptr;
}
@@ -5925,21 +6017,22 @@
}
std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
- sp<Connection> connection = getConnectionLocked(connectionToken);
+ std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
if (connection == nullptr) {
return "<nullptr>";
}
return connection->getInputChannelName();
}
-void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
}
void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
- const sp<Connection>& connection, uint32_t seq,
- bool handled, nsecs_t consumeTime) {
+ const std::shared_ptr<Connection>& connection,
+ uint32_t seq, bool handled,
+ nsecs_t consumeTime) {
// Handle post-event policy actions.
std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
if (dispatchEntryIt == connection->waitQueue.end()) {
@@ -6004,7 +6097,7 @@
const sp<IBinder>& newToken) {
auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyFocusChanged(oldToken, newToken);
+ mPolicy.notifyFocusChanged(oldToken, newToken);
};
postCommandLocked(std::move(command));
}
@@ -6012,12 +6105,12 @@
void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) {
auto command = [this, token, x, y]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyDropWindow(token, x, y);
+ mPolicy.notifyDropWindow(token, x, y);
};
postCommandLocked(std::move(command));
}
-void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) {
if (connection == nullptr) {
LOG_ALWAYS_FATAL("Caller must check for nullness");
}
@@ -6059,7 +6152,7 @@
auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyNoFocusedWindowAnr(application);
+ mPolicy.notifyNoFocusedWindowAnr(application);
};
postCommandLocked(std::move(command));
}
@@ -6099,8 +6192,7 @@
{ // release lock
scoped_unlock unlock(mLock);
android::base::Timer t;
- delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &event,
- entry.policyFlags);
+ delay = mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -6122,7 +6214,7 @@
std::string reason) {
auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyWindowUnresponsive(token, pid, reason);
+ mPolicy.notifyWindowUnresponsive(token, pid, reason);
};
postCommandLocked(std::move(command));
}
@@ -6131,7 +6223,7 @@
std::optional<int32_t> pid) {
auto command = [this, token, pid]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->notifyWindowResponsive(token, pid);
+ mPolicy.notifyWindowResponsive(token, pid);
};
postCommandLocked(std::move(command));
}
@@ -6179,9 +6271,9 @@
sendWindowResponsiveCommandLocked(connectionToken, pid);
}
-bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry,
- KeyEntry& keyEntry, bool handled) {
+bool InputDispatcher::afterKeyEventLockedInterruptable(
+ const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+ KeyEntry& keyEntry, bool handled) {
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
@@ -6215,8 +6307,12 @@
mLock.unlock();
- mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
- keyEntry.policyFlags, &event);
+ if (const auto unhandledKeyFallback =
+ mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+ event, keyEntry.policyFlags);
+ unhandledKeyFallback) {
+ event = *unhandledKeyFallback;
+ }
mLock.lock();
@@ -6256,9 +6352,13 @@
mLock.unlock();
- bool fallback =
- mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
- &event, keyEntry.policyFlags, &event);
+ bool fallback = false;
+ if (auto fb = mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+ event, keyEntry.policyFlags);
+ fb) {
+ fallback = true;
+ event = *fb;
+ }
mLock.lock();
@@ -6356,9 +6456,9 @@
return false;
}
-bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry,
- MotionEntry& motionEntry, bool handled) {
+bool InputDispatcher::afterMotionEventLockedInterruptable(
+ const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+ MotionEntry& motionEntry, bool handled) {
return false;
}
@@ -6510,7 +6610,7 @@
mCurrentPointerCaptureRequest.seq++;
auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy->setPointerCapture(request);
+ mPolicy.setPointerCapture(request);
};
postCommandLocked(std::move(command));
}
@@ -6534,12 +6634,11 @@
mLooper->wake();
}
-void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>& displayInfos) {
+void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
- for (const auto& info : windowInfos) {
+ for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
}
@@ -6554,13 +6653,22 @@
}
mDisplayInfos.clear();
- for (const auto& displayInfo : displayInfos) {
+ for (const auto& displayInfo : update.displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
}
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
}
+
+ mWindowInfosVsyncId = update.vsyncId;
+ mWindowInfosTimestamp = update.timestamp;
+
+ int64_t delay = systemTime() - update.timestamp;
+ if (delay > mMaxWindowInfosDelay) {
+ mMaxWindowInfosDelay = delay;
+ mMaxWindowInfosDelayVsyncId = update.vsyncId;
+ }
}
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
@@ -6583,9 +6691,8 @@
}
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
- const std::vector<gui::WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>& displayInfos) {
- mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
+ const gui::WindowInfosUpdate& update) {
+ mDispatcher.onWindowInfosChanged(update);
}
void InputDispatcher::cancelCurrentTouch() {
@@ -6602,6 +6709,13 @@
mLooper->wake();
}
+void InputDispatcher::requestRefreshConfiguration() {
+ InputDispatcherConfiguration config = mPolicy.getDispatcherConfiguration();
+
+ std::scoped_lock _l(mLock);
+ mConfig = config;
+}
+
void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) {
std::scoped_lock _l(mLock);
mMonitorDispatchingTimeout = timeout;
@@ -6611,7 +6725,7 @@
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
TouchState& state, int32_t pointerId,
- std::vector<InputTarget>& targets) {
+ std::vector<InputTarget>& targets) const {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
@@ -6680,9 +6794,11 @@
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds, downTimeInTarget);
- sp<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken());
+ std::shared_ptr<Connection> wallpaperConnection =
+ getConnectionLocked(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
- sp<Connection> toConnection = getConnectionLocked(toWindowHandle->getToken());
+ std::shared_ptr<Connection> toConnection =
+ getConnectionLocked(toWindowHandle->getToken());
toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, wallpaperConnection,
wallpaperFlags);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4aba24c..9b12f2f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -36,7 +36,7 @@
#include <attestation/HmacKeyManager.h>
#include <gui/InputApplication.h>
-#include <gui/WindowInfo.h>
+#include <gui/WindowInfosUpdate.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <limits.h>
@@ -82,8 +82,8 @@
public:
static constexpr bool kDefaultInTouchMode = true;
- explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
- explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
+ explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
+ explicit InputDispatcher(InputDispatcherPolicyInterface& policy,
std::chrono::nanoseconds staleEventTimeout);
~InputDispatcher() override;
@@ -144,11 +144,12 @@
void displayRemoved(int32_t displayId) override;
// Public because it's also used by tests to simulate the WindowInfosListener callback
- void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos,
- const std::vector<android::gui::DisplayInfo>& displayInfos);
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&);
void cancelCurrentTouch() override;
+ void requestRefreshConfiguration() override;
+
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
@@ -165,8 +166,8 @@
std::unique_ptr<InputThread> mThread;
- sp<InputDispatcherPolicyInterface> mPolicy;
- android::InputDispatcherConfiguration mConfig;
+ InputDispatcherPolicyInterface& mPolicy;
+ android::InputDispatcherConfiguration mConfig GUARDED_BY(mLock);
std::mutex mLock;
@@ -203,6 +204,11 @@
const IdGenerator mIdGenerator;
+ int64_t mWindowInfosVsyncId GUARDED_BY(mLock);
+ int64_t mWindowInfosTimestamp GUARDED_BY(mLock);
+ int64_t mMaxWindowInfosDelay GUARDED_BY(mLock) = -1;
+ int64_t mMaxWindowInfosDelayVsyncId GUARDED_BY(mLock) = -1;
+
// With each iteration, InputDispatcher nominally processes one queued event,
// a timeout, or a response from an input consumer.
// This method should only be called on the input dispatcher's own thread.
@@ -232,7 +238,7 @@
nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
bool isAppSwitchKeyEvent(const KeyEntry& keyEntry);
- bool isAppSwitchPendingLocked() REQUIRES(mLock);
+ bool isAppSwitchPendingLocked() const REQUIRES(mLock);
void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
// Blocked event latency optimization. Drops old events when the user intends
@@ -249,12 +255,12 @@
sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
REQUIRES(mLock);
- sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+ std::shared_ptr<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock);
- void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void removeConnectionLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -264,8 +270,8 @@
};
// All registered connections mapped by input channel token.
- std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken
- GUARDED_BY(mLock);
+ std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>>
+ mConnectionsByToken GUARDED_BY(mLock);
// Find a monitor pid by the provided token.
std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -328,8 +334,8 @@
std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
- std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<Connection>& connection)
- REQUIRES(mLock);
+ std::chrono::nanoseconds getDispatchingTimeoutLocked(
+ const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
// Input filter processing.
bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs& args) REQUIRES(mLock);
@@ -354,9 +360,7 @@
class DispatcherWindowListener : public gui::WindowInfosListener {
public:
explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){};
- void onWindowInfosChanged(
- const std::vector<android::gui::WindowInfo>& windowInfos,
- const std::vector<android::gui::DisplayInfo>& displayInfos) override;
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
private:
InputDispatcher& mDispatcher;
@@ -533,7 +537,7 @@
// prevent unneeded wakeups.
AnrTracker mAnrTracker GUARDED_BY(mLock);
- void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void cancelEventsForAnrLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
// If a focused application changes, we should stop counting down the "no focused window" time,
// because we will have no way of knowing when the previous application actually added a window.
// This also means that we will miss cases like pulling down notification shade when the
@@ -594,22 +598,26 @@
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
// If needed, the methods post commands to run later once the critical bits are done.
- void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ void prepareDispatchCycleLocked(nsecs_t currentTime,
+ const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
- void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ void enqueueDispatchEntriesLocked(nsecs_t currentTime,
+ const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
- const InputTarget& inputTarget,
+ void enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
+ std::shared_ptr<EventEntry>, const InputTarget& inputTarget,
ftl::Flags<InputTarget::Flags> dispatchMode) REQUIRES(mLock);
status_t publishMotionEvent(Connection& connection, DispatchEntry& dispatchEntry) const;
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
+ void startDispatchCycleLocked(nsecs_t currentTime,
+ const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
+ void finishDispatchCycleLocked(nsecs_t currentTime,
+ const std::shared_ptr<Connection>& connection, uint32_t seq,
+ bool handled, nsecs_t consumeTime) REQUIRES(mLock);
+ void abortBrokenDispatchCycleLocked(nsecs_t currentTime,
+ const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
- void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock);
- void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- bool notify) REQUIRES(mLock);
void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
int handleReceiveCallback(int events, sp<IBinder> connectionToken);
@@ -624,14 +632,13 @@
void synthesizeCancelationEventsForInputChannelLocked(
const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options)
REQUIRES(mLock);
- void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection,
- const CancelationOptions& options)
+ void synthesizeCancelationEventsForConnectionLocked(
+ const std::shared_ptr<Connection>& connection, const CancelationOptions& options)
REQUIRES(mLock);
- void synthesizePointerDownEventsForConnectionLocked(const nsecs_t downTime,
- const sp<Connection>& connection,
- ftl::Flags<InputTarget::Flags> targetFlags)
- REQUIRES(mLock);
+ void synthesizePointerDownEventsForConnectionLocked(
+ const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
+ ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock);
void synthesizeCancelationEventsForWindowLocked(
const sp<android::gui::WindowInfoHandle>& windowHandle,
@@ -647,10 +654,10 @@
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
// Dump state.
- void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
- void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
- void logDispatchStateLocked() REQUIRES(mLock);
- std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
+ void dumpDispatchStateLocked(std::string& dump) const REQUIRES(mLock);
+ void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const;
+ void logDispatchStateLocked() const REQUIRES(mLock);
+ std::string dumpPointerCaptureStateLocked() const REQUIRES(mLock);
// Registration.
void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
@@ -658,16 +665,16 @@
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
- void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection,
- uint32_t seq, bool handled, nsecs_t consumeTime)
- REQUIRES(mLock);
+ void doDispatchCycleFinishedCommand(nsecs_t finishTime,
+ const std::shared_ptr<Connection>& connection, uint32_t seq,
+ bool handled, nsecs_t consumeTime) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
KeyEntry& entry) REQUIRES(mLock);
void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
REQUIRES(mLock);
void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
- void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ void onAnrLocked(const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
const std::string& reason) REQUIRES(mLock);
@@ -675,10 +682,10 @@
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
- bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
+ bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
- bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
+ bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection,
DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
bool handled) REQUIRES(mLock);
@@ -702,8 +709,8 @@
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t pointerId, std::vector<InputTarget>& targets)
- REQUIRES(mLock);
+ TouchState& state, int32_t pointerId,
+ std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
index bca1600..3ef8419 100644
--- a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
@@ -20,7 +20,7 @@
namespace android {
std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
- const sp<InputDispatcherPolicyInterface>& policy) {
+ InputDispatcherPolicyInterface& policy) {
return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
}
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp
index a5bfc25..96d78c3 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.cpp
+++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp
@@ -256,7 +256,7 @@
}
}
-std::string LatencyAggregator::dump(const char* prefix) {
+std::string LatencyAggregator::dump(const char* prefix) const {
std::string sketchDump = StringPrintf("%s Sketches:\n", prefix);
for (size_t i = 0; i < SketchIndex::SIZE; i++) {
const int64_t numDown = mDownSketches[i]->num_values();
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h
index accfc29..60b6813 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.h
+++ b/services/inputflinger/dispatcher/LatencyAggregator.h
@@ -56,7 +56,7 @@
*/
void processTimeline(const InputEventTimeline& timeline) override;
- std::string dump(const char* prefix);
+ std::string dump(const char* prefix) const;
~LatencyAggregator();
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 2dd7d3f..b7c36a8 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -165,7 +165,7 @@
}
}
-std::string LatencyTracker::dump(const char* prefix) {
+std::string LatencyTracker::dump(const char* prefix) const {
return StringPrintf("%sLatencyTracker:\n", prefix) +
StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) +
StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 64dfeef..4212da8 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -55,7 +55,7 @@
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
- std::string dump(const char* prefix);
+ std::string dump(const char* prefix) const;
private:
/**
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 9c443f1..0a61d48 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -242,6 +242,18 @@
clearWindowsWithoutPointers();
}
+void TouchState::removeAllPointersForDevice(int32_t removedDeviceId) {
+ for (TouchedWindow& window : windows) {
+ window.removeAllHoveringPointersForDevice(removedDeviceId);
+ }
+ if (deviceId == removedDeviceId) {
+ for (TouchedWindow& window : windows) {
+ window.removeAllTouchingPointers();
+ }
+ }
+ clearWindowsWithoutPointers();
+}
+
std::string TouchState::dump() const {
std::string out;
out += StringPrintf("deviceId=%d, source=%s\n", deviceId,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index a20080f..15b840f 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -54,6 +54,8 @@
int32_t deviceId, int32_t hoveringPointerId);
void removeHoveringPointer(int32_t deviceId, int32_t hoveringPointerId);
void clearHoveringPointers();
+
+ void removeAllPointersForDevice(int32_t removedDeviceId);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 99c4769..d55d657 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -58,6 +58,10 @@
}
}
+void TouchedWindow::removeAllTouchingPointers() {
+ pointerIds.reset();
+}
+
void TouchedWindow::removeHoveringPointer(int32_t deviceId, int32_t pointerId) {
const auto it = mHoveringPointerIdsByDevice.find(deviceId);
if (it == mHoveringPointerIdsByDevice.end()) {
@@ -70,6 +74,10 @@
}
}
+void TouchedWindow::removeAllHoveringPointersForDevice(int32_t deviceId) {
+ mHoveringPointerIdsByDevice.erase(deviceId);
+}
+
std::string TouchedWindow::dump() const {
std::string out;
std::string hoveringPointers =
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index aa2e9dd..43e7169 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -44,6 +44,9 @@
void addHoveringPointer(int32_t deviceId, int32_t pointerId);
void removeHoveringPointer(int32_t deviceId, int32_t pointerId);
void removeTouchingPointer(int32_t pointerId);
+
+ void removeAllTouchingPointers();
+ void removeAllHoveringPointersForDevice(int32_t deviceId);
void clearHoveringPointers();
std::string dump() const;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
index 5247d8e..6b298c2 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -25,6 +25,6 @@
// This factory method is used to encapsulate implementation details in internal header files.
std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
- const sp<InputDispatcherPolicyInterface>& policy);
+ InputDispatcherPolicyInterface& policy);
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 76dce63..c752ddd 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -225,6 +225,12 @@
* Abort the current touch stream.
*/
virtual void cancelCurrentTouch() = 0;
+
+ /**
+ * Request that the InputDispatcher's configuration, which can be obtained through the policy,
+ * be updated.
+ */
+ virtual void requestRefreshConfiguration() = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 7843923..5539915 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -35,12 +35,11 @@
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*/
-class InputDispatcherPolicyInterface : public virtual RefBase {
-protected:
- InputDispatcherPolicyInterface() {}
- virtual ~InputDispatcherPolicyInterface() {}
-
+class InputDispatcherPolicyInterface {
public:
+ InputDispatcherPolicyInterface() = default;
+ virtual ~InputDispatcherPolicyInterface() = default;
+
/* Notifies the system that a configuration change has occurred. */
virtual void notifyConfigurationChanged(nsecs_t when) = 0;
@@ -75,14 +74,14 @@
virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
/* Gets the input dispatcher configuration. */
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
+ virtual InputDispatcherConfiguration getDispatcherConfiguration() = 0;
/* Filters an input event.
* Return true to dispatch the event unmodified, false to consume the event.
* A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
* to injectInputEvent.
*/
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+ virtual bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) = 0;
/* Intercepts a key event immediately before queueing it.
* The policy can use this method as an opportunity to perform power management functions
@@ -91,7 +90,7 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0;
+ virtual void interceptKeyBeforeQueueing(const KeyEvent& keyEvent, uint32_t& policyFlags) = 0;
/* Intercepts a touch, trackball or other motion event before queueing it.
* The policy can use this method as an opportunity to perform power management functions
@@ -100,18 +99,19 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
+ virtual void interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent* keyEvent,
+ const KeyEvent& keyEvent,
uint32_t policyFlags) = 0;
/* Allows the policy a chance to perform default processing for an unhandled key.
- * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
- uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0;
+ * Returns an alternate key event to redispatch as a fallback, if needed. */
+ virtual std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>& token,
+ const KeyEvent& keyEvent,
+ uint32_t policyFlags) = 0;
/* Notifies the policy about switch events.
*/
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 743587c..4d2839f 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -23,7 +23,7 @@
cc_library_shared {
name: "libinputflingerhost",
-
+ cpp_std: "c++20",
srcs: [
"InputFlinger.cpp",
"InputDriver.cpp",
@@ -64,14 +64,17 @@
srcs: ["main.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"libbinder",
"libinputflingerhost",
"libutils",
- "libinput"
+ "libinput",
],
static_libs: [
"libarect",
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 1750c64..a93a2ea 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -42,118 +42,6 @@
namespace android {
-// --- InputReaderInterface ---
-
-/* The interface for the InputReader shared library.
- *
- * Manages one or more threads that process raw input events and sends cooked event data to an
- * input listener.
- *
- * The implementation must guarantee thread safety for this interface. However, since the input
- * listener is NOT thread safe, all calls to the listener must happen from the same thread.
- */
-class InputReaderInterface {
-public:
- InputReaderInterface() { }
- virtual ~InputReaderInterface() { }
-
- /* Dumps the state of the input reader.
- *
- * This method may be called on any thread (usually by the input manager). */
- virtual void dump(std::string& dump) = 0;
-
- /* Called by the heartbeat to ensures that the reader has not deadlocked. */
- virtual void monitor() = 0;
-
- /* Returns true if the input device is enabled. */
- virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
-
- /* Makes the reader start processing events from the kernel. */
- virtual status_t start() = 0;
-
- /* Makes the reader stop processing any more events. */
- virtual status_t stop() = 0;
-
- /* Gets information about all input devices.
- *
- * This method may be called on any thread (usually by the input manager).
- */
- virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
-
- /* Query current input state. */
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) = 0;
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw) = 0;
-
- virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const = 0;
-
- virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
-
- /* Toggle Caps Lock */
- virtual void toggleCapsLockState(int32_t deviceId) = 0;
-
- /* Determine whether physical keys exist for the given framework-domain key codes. */
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0;
-
- /* Requests that a reconfiguration of all input devices.
- * The changes flag is a bitfield that indicates what has changed and whether
- * the input devices must all be reopened. */
- virtual void requestRefreshConfiguration(uint32_t changes) = 0;
-
- /* Controls the vibrator of a particular input device. */
- virtual void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
- int32_t token) = 0;
- virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
-
- virtual bool isVibrating(int32_t deviceId) = 0;
-
- virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
- /* Get battery level of a particular input device. */
- virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
- /* Get battery status of a particular input device. */
- virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
- /* Get the device path for the battery of an input device. */
- virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0;
-
- virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
-
- virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
-
- /* Return true if the device can send input events to the specified display. */
- virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
-
- /* Enable sensor in input reader mapper. */
- virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
- std::chrono::microseconds samplingPeriod,
- std::chrono::microseconds maxBatchReportLatency) = 0;
-
- /* Disable sensor in input reader mapper. */
- virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
-
- /* Flush sensor data in input reader mapper. */
- virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
-
- /* Set color for the light */
- virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0;
- /* Set player ID for the light */
- virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0;
- /* Get light color */
- virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
- /* Get light player ID */
- virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
-
- /* Get the Bluetooth address of an input device, if known. */
- virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
-
- /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
- virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
-};
-
// --- InputReaderConfiguration ---
/*
@@ -163,51 +51,51 @@
*/
struct InputReaderConfiguration {
// Describes changes that have occurred.
- enum {
+ enum class Change : uint32_t {
// The mouse pointer speed changed.
- CHANGE_POINTER_SPEED = 1 << 0,
+ POINTER_SPEED = 1u << 0,
// The pointer gesture control changed.
- CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1,
+ POINTER_GESTURE_ENABLEMENT = 1u << 1,
// The display size or orientation changed.
- CHANGE_DISPLAY_INFO = 1 << 2,
+ DISPLAY_INFO = 1u << 2,
// The visible touches option changed.
- CHANGE_SHOW_TOUCHES = 1 << 3,
+ SHOW_TOUCHES = 1u << 3,
// The keyboard layouts must be reloaded.
- CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
+ KEYBOARD_LAYOUTS = 1u << 4,
// The device name alias supplied by the may have changed for some devices.
- CHANGE_DEVICE_ALIAS = 1 << 5,
+ DEVICE_ALIAS = 1u << 5,
// The location calibration matrix changed.
- CHANGE_TOUCH_AFFINE_TRANSFORMATION = 1 << 6,
+ TOUCH_AFFINE_TRANSFORMATION = 1u << 6,
// The presence of an external stylus has changed.
- CHANGE_EXTERNAL_STYLUS_PRESENCE = 1 << 7,
+ EXTERNAL_STYLUS_PRESENCE = 1u << 7,
// The pointer capture mode has changed.
- CHANGE_POINTER_CAPTURE = 1 << 8,
+ POINTER_CAPTURE = 1u << 8,
// The set of disabled input devices (disabledDevices) has changed.
- CHANGE_ENABLED_STATE = 1 << 9,
+ ENABLED_STATE = 1u << 9,
// The device type has been updated.
- CHANGE_DEVICE_TYPE = 1 << 10,
+ DEVICE_TYPE = 1u << 10,
// The keyboard layout association has changed.
- CHANGE_KEYBOARD_LAYOUT_ASSOCIATION = 1 << 11,
+ KEYBOARD_LAYOUT_ASSOCIATION = 1u << 11,
// The stylus button reporting configurations has changed.
- CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12,
+ STYLUS_BUTTON_REPORTING = 1u << 12,
// The touchpad settings changed.
- CHANGE_TOUCHPAD_SETTINGS = 1 << 13,
+ TOUCHPAD_SETTINGS = 1u << 13,
// All devices must be reopened.
- CHANGE_MUST_REOPEN = 1 << 31,
+ MUST_REOPEN = 1u << 31,
};
// Gets the amount of time to disable virtual keys after the screen is touched
@@ -367,8 +255,6 @@
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false) {}
- static std::string changesToString(uint32_t changes);
-
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
const;
@@ -383,6 +269,116 @@
std::vector<DisplayViewport> mDisplays;
};
+using ConfigurationChanges = ftl::Flags<InputReaderConfiguration::Change>;
+
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
+class InputReaderInterface {
+public:
+ InputReaderInterface() {}
+ virtual ~InputReaderInterface() {}
+ /* Dumps the state of the input reader.
+ *
+ * This method may be called on any thread (usually by the input manager). */
+ virtual void dump(std::string& dump) = 0;
+
+ /* Called by the heartbeat to ensures that the reader has not deadlocked. */
+ virtual void monitor() = 0;
+
+ /* Returns true if the input device is enabled. */
+ virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
+
+ /* Makes the reader start processing events from the kernel. */
+ virtual status_t start() = 0;
+
+ /* Makes the reader stop processing any more events. */
+ virtual status_t stop() = 0;
+
+ /* Gets information about all input devices.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual std::vector<InputDeviceInfo> getInputDevices() const = 0;
+
+ /* Query current input state. */
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0;
+
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
+ virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
+
+ /* Toggle Caps Lock */
+ virtual void toggleCapsLockState(int32_t deviceId) = 0;
+
+ /* Determine whether physical keys exist for the given framework-domain key codes. */
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0;
+
+ /* Requests that a reconfiguration of all input devices.
+ * The changes flag is a bitfield that indicates what has changed and whether
+ * the input devices must all be reopened. */
+ virtual void requestRefreshConfiguration(ConfigurationChanges changes) = 0;
+
+ /* Controls the vibrator of a particular input device. */
+ virtual void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
+ int32_t token) = 0;
+ virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0;
+
+ virtual bool isVibrating(int32_t deviceId) = 0;
+
+ virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+ /* Get battery level of a particular input device. */
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
+ /* Get battery status of a particular input device. */
+ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
+ /* Get the device path for the battery of an input device. */
+ virtual std::optional<std::string> getBatteryDevicePath(int32_t deviceId) = 0;
+
+ virtual std::vector<InputDeviceLightInfo> getLights(int32_t deviceId) = 0;
+
+ virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
+
+ /* Return true if the device can send input events to the specified display. */
+ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+
+ /* Enable sensor in input reader mapper. */
+ virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+ std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency) = 0;
+
+ /* Disable sensor in input reader mapper. */
+ virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+ /* Flush sensor data in input reader mapper. */
+ virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+ /* Set color for the light */
+ virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0;
+ /* Set player ID for the light */
+ virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0;
+ /* Get light color */
+ virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0;
+ /* Get light player ID */
+ virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0;
+
+ /* Get the Bluetooth address of an input device, if known. */
+ virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
+
+ /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
+ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
+};
+
// --- TouchAffineTransformation ---
struct TouchAffineTransformation {
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 132c3a1..b0edb57 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -42,6 +42,7 @@
"Macros.cpp",
"TouchVideoDevice.cpp",
"controller/PeripheralController.cpp",
+ "mapper/CapturedTouchpadEventConverter.cpp",
"mapper/CursorInputMapper.cpp",
"mapper/ExternalStylusInputMapper.cpp",
"mapper/InputMapper.cpp",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index b5ee044..b86906b 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -180,7 +180,7 @@
std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out;
mSources = 0;
mClasses = ftl::Flags<InputDeviceClass>(0);
@@ -201,12 +201,14 @@
mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
mHasMic = mClasses.test(InputDeviceClass::MIC);
+ using Change = InputReaderConfiguration::Change;
+
if (!isIgnored()) {
// Full configuration should happen the first time configure is called
// and when the device type is changed. Changing a device type can
// affect various other parameters so should result in a
// reconfiguration.
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
+ if (!changes.any() || changes.test(Change::DEVICE_TYPE)) {
mConfiguration.clear();
for_each_subdevice([this](InputDeviceContext& context) {
std::optional<PropertyMap> configuration =
@@ -220,7 +222,7 @@
getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location);
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
+ if (!changes.any() || changes.test(Change::KEYBOARD_LAYOUTS)) {
if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
std::shared_ptr<KeyCharacterMap> keyboardLayout =
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
@@ -237,7 +239,7 @@
}
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) {
+ if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) {
if (!(mClasses.test(InputDeviceClass::VIRTUAL))) {
std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier);
if (mAlias != alias) {
@@ -247,7 +249,7 @@
}
}
- if (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE) {
+ if (changes.test(Change::ENABLED_STATE)) {
// Do not execute this code on the first configure, because 'setEnabled' would call
// InputMapper::reset, and you can't reset a mapper before it has been configured.
// The mappers are configured for the first time at the bottom of this function.
@@ -256,7 +258,7 @@
out += setEnabled(enabled, when);
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes.any() || changes.test(Change::DISPLAY_INFO)) {
// In most situations, no port or name will be specified.
mAssociatedDisplayPort = std::nullopt;
mAssociatedDisplayUniqueId = std::nullopt;
@@ -305,7 +307,7 @@
}
}
- if (changes) {
+ if (changes.any()) {
// For first-time configuration, only allow device to be disabled after mappers have
// finished configuring. This is because we need to read some of the properties from
// the device's open fd.
@@ -320,7 +322,7 @@
// If a device is just plugged but it might be disabled, we need to update some info like
// axis range of touch from each InputMapper first, then disable it.
- if (!changes) {
+ if (!changes.any()) {
out += setEnabled(readerConfig.disabledDevices.find(mId) ==
readerConfig.disabledDevices.end(),
when);
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 0afe79d..ea95f78 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -85,7 +85,7 @@
mDisableVirtualKeysTimeout(LLONG_MIN),
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
- refreshConfigurationLocked(0);
+ refreshConfigurationLocked(/*changes=*/{});
updateGlobalMetaStateLocked();
}
@@ -122,9 +122,9 @@
oldGeneration = mGeneration;
timeoutMillis = -1;
- uint32_t changes = mConfigurationChangesToRefresh;
- if (changes) {
- mConfigurationChangesToRefresh = 0;
+ auto changes = mConfigurationChangesToRefresh;
+ if (changes.any()) {
+ mConfigurationChangesToRefresh.clear();
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
@@ -236,7 +236,7 @@
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
- notifyAll(device->configure(when, mConfig, 0));
+ notifyAll(device->configure(when, mConfig, /*changes=*/{}));
notifyAll(device->reset(when));
if (device->isIgnored()) {
@@ -312,7 +312,7 @@
std::list<NotifyArgs> resetEvents;
if (device->hasEventHubDevices()) {
- resetEvents += device->configure(when, mConfig, 0);
+ resetEvents += device->configure(when, mConfig, /*changes=*/{});
}
resetEvents += device->reset(when);
notifyAll(std::move(resetEvents));
@@ -390,21 +390,21 @@
mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when});
}
-void InputReader::refreshConfigurationLocked(uint32_t changes) {
+void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
- if (!changes) return;
+ using Change = InputReaderConfiguration::Change;
+ if (!changes.any()) return;
- ALOGI("Reconfiguring input devices, changes=%s",
- InputReaderConfiguration::changesToString(changes).c_str());
+ ALOGI("Reconfiguring input devices, changes=%s", changes.string().c_str());
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
+ if (changes.test(Change::DISPLAY_INFO)) {
updatePointerDisplayLocked();
}
- if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
+ if (changes.test(Change::MUST_REOPEN)) {
mEventHub->requestReopenDevices();
} else {
for (auto& devicePair : mDevices) {
@@ -413,7 +413,7 @@
}
}
- if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
+ if (changes.test(Change::POINTER_CAPTURE)) {
if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) {
ALOGV("Skipping notifying pointer capture changes: "
"There was no change in the pointer capture state.");
@@ -457,7 +457,7 @@
}
void InputReader::notifyExternalStylusPresenceChangedLocked() {
- refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+ refreshConfigurationLocked(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
}
void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
@@ -671,11 +671,11 @@
return device->getKeyCodeForKeyLocation(locationKeyCode);
}
-void InputReader::requestRefreshConfiguration(uint32_t changes) {
+void InputReader::requestRefreshConfiguration(ConfigurationChanges changes) {
std::scoped_lock _l(mLock);
- if (changes) {
- bool needWake = !mConfigurationChangesToRefresh;
+ if (changes.any()) {
+ bool needWake = !mConfigurationChangesToRefresh.any();
mConfigurationChangesToRefresh |= changes;
if (needWake) {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 2091e16..6303546 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -85,7 +85,7 @@
void removeEventHubDevice(int32_t eventHubId);
[[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
- uint32_t changes);
+ ConfigurationChanges changes);
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvents, size_t count);
[[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when);
@@ -407,7 +407,7 @@
inline const std::string getName() const { return mDevice.getName(); }
inline const std::string getDescriptor() { return mDevice.getDescriptor(); }
inline const std::string getLocation() { return mDevice.getLocation(); }
- inline bool isExternal() { return mDevice.isExternal(); }
+ inline bool isExternal() const { return mDevice.isExternal(); }
inline std::optional<uint8_t> getAssociatedDisplayPort() const {
return mDevice.getAssociatedDisplayPort();
}
@@ -424,7 +424,7 @@
return mDevice.cancelTouch(when, readTime);
}
inline void bumpGeneration() { mDevice.bumpGeneration(); }
- inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
+ inline const PropertyMap& getConfiguration() const { return mDevice.getConfiguration(); }
private:
InputDevice& mDevice;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 120e150..9112913 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -77,7 +77,7 @@
bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) override;
- void requestRefreshConfiguration(uint32_t changes) override;
+ void requestRefreshConfiguration(ConfigurationChanges changes) override;
void vibrate(int32_t deviceId, const VibrationSequence& sequence, ssize_t repeat,
int32_t token) override;
@@ -233,8 +233,8 @@
nsecs_t mNextTimeout GUARDED_BY(mLock);
void requestTimeoutAtTimeLocked(nsecs_t when) REQUIRES(mLock);
- uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock);
- void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock);
+ ConfigurationChanges mConfigurationChangesToRefresh GUARDED_BY(mLock);
+ void refreshConfigurationLocked(ConfigurationChanges changes) REQUIRES(mLock);
void notifyAll(std::list<NotifyArgs>&& argsList);
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
new file mode 100644
index 0000000..dab4661
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CapturedTouchpadEventConverter.h"
+
+#include <sstream>
+
+#include <android-base/stringprintf.h>
+#include <gui/constants.h>
+#include <input/PrintTools.h>
+#include <linux/input-event-codes.h>
+#include <log/log_main.h>
+
+namespace android {
+
+namespace {
+
+int32_t actionWithIndex(int32_t action, int32_t index) {
+ return action | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+}
+
+template <typename T>
+size_t firstUnmarkedBit(T set) {
+ // TODO: replace with std::countr_one from <bit> when that's available
+ LOG_ALWAYS_FATAL_IF(set.all());
+ size_t i = 0;
+ while (set.test(i)) {
+ i++;
+ }
+ return i;
+}
+
+} // namespace
+
+CapturedTouchpadEventConverter::CapturedTouchpadEventConverter(
+ InputReaderContext& readerContext, const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator, int32_t deviceId)
+ : mDeviceId(deviceId),
+ mReaderContext(readerContext),
+ mDeviceContext(deviceContext),
+ mMotionAccumulator(motionAccumulator),
+ mHasTouchMinor(deviceContext.hasAbsoluteAxis(ABS_MT_TOUCH_MINOR)),
+ mHasToolMinor(deviceContext.hasAbsoluteAxis(ABS_MT_WIDTH_MINOR)) {
+ RawAbsoluteAxisInfo orientationInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
+ if (orientationInfo.valid) {
+ if (orientationInfo.maxValue > 0) {
+ mOrientationScale = M_PI_2 / orientationInfo.maxValue;
+ } else if (orientationInfo.minValue < 0) {
+ mOrientationScale = -M_PI_2 / orientationInfo.minValue;
+ }
+ }
+
+ // TODO(b/275369880): support touch.pressure.calibration and .scale properties when captured.
+ RawAbsoluteAxisInfo pressureInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
+ if (pressureInfo.valid && pressureInfo.maxValue > 0) {
+ mPressureScale = 1.0 / pressureInfo.maxValue;
+ }
+
+ RawAbsoluteAxisInfo touchMajorInfo, toolMajorInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &touchMajorInfo);
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &toolMajorInfo);
+ mHasTouchMajor = touchMajorInfo.valid;
+ mHasToolMajor = toolMajorInfo.valid;
+ if (mHasTouchMajor && touchMajorInfo.maxValue != 0) {
+ mSizeScale = 1.0f / touchMajorInfo.maxValue;
+ } else if (mHasToolMajor && toolMajorInfo.maxValue != 0) {
+ mSizeScale = 1.0f / toolMajorInfo.maxValue;
+ }
+}
+
+std::string CapturedTouchpadEventConverter::dump() const {
+ std::stringstream out;
+ out << "Orientation scale: " << mOrientationScale << "\n";
+ out << "Pressure scale: " << mPressureScale << "\n";
+ out << "Size scale: " << mSizeScale << "\n";
+
+ out << "Dimension axes:";
+ if (mHasTouchMajor) out << " touch major";
+ if (mHasTouchMinor) out << ", touch minor";
+ if (mHasToolMajor) out << ", tool major";
+ if (mHasToolMinor) out << ", tool minor";
+ out << "\n";
+
+ out << "Down time: " << mDownTime << "\n";
+ out << StringPrintf("Button state: 0x%08x\n", mButtonState);
+
+ out << StringPrintf("Pointer IDs in use: %s\n", mPointerIdsInUse.to_string().c_str());
+
+ out << "Pointer IDs for slot numbers:\n";
+ out << addLinePrefix(dumpMap(mPointerIdForSlotNumber), " ") << "\n";
+ return out.str();
+}
+
+void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const {
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
+ tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MINOR, ABS_MT_WIDTH_MINOR);
+
+ RawAbsoluteAxisInfo pressureInfo;
+ mDeviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
+ if (pressureInfo.valid) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0, 1, 0, 0, 0);
+ }
+
+ RawAbsoluteAxisInfo orientationInfo;
+ mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
+ if (orientationInfo.valid && (orientationInfo.maxValue > 0 || orientationInfo.minValue < 0)) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, SOURCE, -M_PI_2, M_PI_2, 0, 0, 0);
+ }
+
+ if (mHasTouchMajor || mHasToolMajor) {
+ info.addMotionRange(AMOTION_EVENT_AXIS_SIZE, SOURCE, 0, 1, 0, 0, 0);
+ }
+}
+
+void CapturedTouchpadEventConverter::tryAddRawMotionRange(InputDeviceInfo& deviceInfo,
+ int32_t androidAxis,
+ int32_t evdevAxis) const {
+ RawAbsoluteAxisInfo info;
+ mDeviceContext.getAbsoluteAxisInfo(evdevAxis, &info);
+ if (info.valid) {
+ deviceInfo.addMotionRange(androidAxis, SOURCE, info.minValue, info.maxValue, info.flat,
+ info.fuzz, info.resolution);
+ }
+}
+
+void CapturedTouchpadEventConverter::reset() {
+ mCursorButtonAccumulator.reset(mDeviceContext);
+ mDownTime = 0;
+ mPointerIdsInUse.reset();
+ mPointerIdForSlotNumber.clear();
+}
+
+std::list<NotifyArgs> CapturedTouchpadEventConverter::process(const RawEvent& rawEvent) {
+ std::list<NotifyArgs> out;
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ out = sync(rawEvent.when, rawEvent.readTime);
+ mMotionAccumulator.finishSync();
+ }
+
+ mCursorButtonAccumulator.process(&rawEvent);
+ mMotionAccumulator.process(&rawEvent);
+ return out;
+}
+
+std::list<NotifyArgs> CapturedTouchpadEventConverter::sync(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ std::vector<PointerCoords> coords;
+ std::vector<PointerProperties> properties;
+ std::map<size_t, size_t> coordsIndexForSlotNumber;
+
+ // For all the touches that were already down, send a MOVE event with their updated coordinates.
+ // A convention of the MotionEvent API is that pointer coordinates in UP events match the
+ // pointer's coordinates from the previous MOVE, so we still include touches here even if
+ // they've been lifted in this evdev frame.
+ if (!mPointerIdForSlotNumber.empty()) {
+ for (const auto [slotNumber, pointerId] : mPointerIdForSlotNumber) {
+ // Note that we don't check whether the touch has actually moved — it's rare for a touch
+ // to stay perfectly still between frames, and if it does the worst that can happen is
+ // an extra MOVE event, so it's not worth the overhead of checking for changes.
+ coordsIndexForSlotNumber[slotNumber] = coords.size();
+ coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber)));
+ properties.push_back({.id = pointerId, .toolType = ToolType::FINGER});
+ }
+ out.push_back(
+ makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties));
+ }
+
+ std::vector<size_t> upSlots, downSlots;
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ const MultiTouchMotionAccumulator::Slot& slot = mMotionAccumulator.getSlot(i);
+ // Some touchpads continue to report contacts even after they've identified them as palms.
+ // We don't currently have a way to mark these as palms when reporting to apps, so don't
+ // report them at all.
+ const bool isInUse = slot.isInUse() && slot.getToolType() != ToolType::PALM;
+ const bool wasInUse = mPointerIdForSlotNumber.find(i) != mPointerIdForSlotNumber.end();
+ if (isInUse && !wasInUse) {
+ downSlots.push_back(i);
+ } else if (!isInUse && wasInUse) {
+ upSlots.push_back(i);
+ }
+ }
+
+ // For any touches that were lifted, send UP or POINTER_UP events.
+ for (size_t slotNumber : upSlots) {
+ const size_t indexToRemove = coordsIndexForSlotNumber.at(slotNumber);
+ const bool cancel = mMotionAccumulator.getSlot(slotNumber).getToolType() == ToolType::PALM;
+ int32_t action;
+ if (coords.size() == 1) {
+ action = cancel ? AMOTION_EVENT_ACTION_CANCEL : AMOTION_EVENT_ACTION_UP;
+ } else {
+ action = actionWithIndex(AMOTION_EVENT_ACTION_POINTER_UP, indexToRemove);
+ }
+ out.push_back(makeMotionArgs(when, readTime, action, coords, properties, /*actionButton=*/0,
+ /*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0));
+
+ freePointerIdForSlot(slotNumber);
+ coords.erase(coords.begin() + indexToRemove);
+ properties.erase(properties.begin() + indexToRemove);
+ // Now that we've removed some coords and properties, we might have to update the slot
+ // number to coords index mapping.
+ coordsIndexForSlotNumber.erase(slotNumber);
+ for (auto& [_, index] : coordsIndexForSlotNumber) {
+ if (index > indexToRemove) {
+ index--;
+ }
+ }
+ }
+
+ // For new touches, send DOWN or POINTER_DOWN events.
+ for (size_t slotNumber : downSlots) {
+ const size_t coordsIndex = coords.size();
+ const int32_t action = coords.empty()
+ ? AMOTION_EVENT_ACTION_DOWN
+ : actionWithIndex(AMOTION_EVENT_ACTION_POINTER_DOWN, coordsIndex);
+
+ coordsIndexForSlotNumber[slotNumber] = coordsIndex;
+ coords.push_back(makePointerCoordsForSlot(mMotionAccumulator.getSlot(slotNumber)));
+ properties.push_back(
+ {.id = allocatePointerIdToSlot(slotNumber), .toolType = ToolType::FINGER});
+
+ out.push_back(makeMotionArgs(when, readTime, action, coords, properties));
+ }
+
+ const uint32_t newButtonState = mCursorButtonAccumulator.getButtonState();
+ for (uint32_t button = 1; button <= AMOTION_EVENT_BUTTON_FORWARD; button <<= 1) {
+ if (newButtonState & button && !(mButtonState & button)) {
+ mButtonState |= button;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, coords,
+ properties, /*actionButton=*/button));
+ } else if (!(newButtonState & button) && mButtonState & button) {
+ mButtonState &= ~button;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ coords, properties, /*actionButton=*/button));
+ }
+ }
+ return out;
+}
+
+NotifyMotionArgs CapturedTouchpadEventConverter::makeMotionArgs(
+ nsecs_t when, nsecs_t readTime, int32_t action, const std::vector<PointerCoords>& coords,
+ const std::vector<PointerProperties>& properties, int32_t actionButton, int32_t flags) {
+ LOG_ALWAYS_FATAL_IF(coords.size() != properties.size(),
+ "Mismatched coords and properties arrays.");
+ return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, SOURCE,
+ ADISPLAY_ID_NONE, /*policyFlags=*/POLICY_FLAG_WAKE, action,
+ /*actionButton=*/actionButton, flags,
+ mReaderContext.getGlobalMetaState(), mButtonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, coords.size(),
+ properties.data(), coords.data(), /*xPrecision=*/1.0f,
+ /*yPrecision=*/1.0f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, mDownTime, /*videoFrames=*/{});
+}
+
+PointerCoords CapturedTouchpadEventConverter::makePointerCoordsForSlot(
+ const MultiTouchMotionAccumulator::Slot& slot) const {
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, slot.getToolMajor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, slot.getToolMinor());
+ coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, slot.getOrientation() * mOrientationScale);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, slot.getPressure() * mPressureScale);
+ float size = 0;
+ // TODO(b/275369880): support touch.size.calibration and .isSummed properties when captured.
+ if (mHasTouchMajor) {
+ size = mHasTouchMinor ? (slot.getTouchMajor() + slot.getTouchMinor()) / 2
+ : slot.getTouchMajor();
+ } else if (mHasToolMajor) {
+ size = mHasToolMinor ? (slot.getToolMajor() + slot.getToolMinor()) / 2
+ : slot.getToolMajor();
+ }
+ coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size * mSizeScale);
+ return coords;
+}
+
+int32_t CapturedTouchpadEventConverter::allocatePointerIdToSlot(size_t slotNumber) {
+ const int32_t pointerId = firstUnmarkedBit(mPointerIdsInUse);
+ mPointerIdsInUse.set(pointerId);
+ mPointerIdForSlotNumber[slotNumber] = pointerId;
+ return pointerId;
+}
+
+void CapturedTouchpadEventConverter::freePointerIdForSlot(size_t slotNumber) {
+ mPointerIdsInUse.reset(mPointerIdForSlotNumber.at(slotNumber));
+ mPointerIdForSlotNumber.erase(slotNumber);
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
new file mode 100644
index 0000000..9b6df7a
--- /dev/null
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <bitset>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android/input.h>
+#include <input/Input.h>
+#include <utils/Timers.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+namespace android {
+
+class CapturedTouchpadEventConverter {
+public:
+ explicit CapturedTouchpadEventConverter(InputReaderContext& readerContext,
+ const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator,
+ int32_t deviceId);
+ std::string dump() const;
+ void populateMotionRanges(InputDeviceInfo& info) const;
+ void reset();
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent);
+
+private:
+ void tryAddRawMotionRange(InputDeviceInfo& deviceInfo, int32_t androidAxis,
+ int32_t evdevAxis) const;
+ [[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ const std::vector<PointerCoords>& coords,
+ const std::vector<PointerProperties>& properties,
+ int32_t actionButton = 0, int32_t flags = 0);
+ PointerCoords makePointerCoordsForSlot(const MultiTouchMotionAccumulator::Slot& slot) const;
+ int32_t allocatePointerIdToSlot(size_t slotNumber);
+ void freePointerIdForSlot(size_t slotNumber);
+
+ const int32_t mDeviceId;
+ InputReaderContext& mReaderContext;
+ const InputDeviceContext& mDeviceContext;
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ MultiTouchMotionAccumulator& mMotionAccumulator;
+
+ float mOrientationScale = 0;
+ float mPressureScale = 1;
+ float mSizeScale = 0;
+ bool mHasTouchMajor;
+ const bool mHasTouchMinor;
+ bool mHasToolMajor;
+ const bool mHasToolMinor;
+ nsecs_t mDownTime = 0;
+ uint32_t mButtonState = 0;
+
+ std::bitset<MAX_POINTER_ID + 1> mPointerIdsInUse;
+ std::map<size_t, int32_t> mPointerIdForSlotNumber;
+
+ static constexpr uint32_t SOURCE = AINPUT_SOURCE_TOUCHPAD;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index adc893b..8ef5ff6 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -139,26 +139,26 @@
std::list<NotifyArgs> CursorInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, readerConfig, changes);
- if (!changes) {
+ if (!changes.any()) {
configureWithZeroChanges(readerConfig);
return out;
}
const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
- (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE);
if (configurePointerCapture) {
configureOnPointerCapture(readerConfig);
out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
}
- if ((changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) || configurePointerCapture) {
+ if (changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || configurePointerCapture) {
configureOnChangePointerSpeed(readerConfig);
}
- if ((changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) || configurePointerCapture) {
+ if (changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
configureOnChangeDisplayInfo(readerConfig);
}
return out;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index a7cdd51..caf2e5a 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -62,7 +62,7 @@
virtual void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 7100f88..987d2d0 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -49,7 +49,7 @@
std::list<NotifyArgs> ExternalStylusInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
mTouchButtonAccumulator.configure();
return {};
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 7f926a7..841c437 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -35,7 +35,7 @@
void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index bd86a5a..0692dbb 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -38,7 +38,7 @@
void InputMapper::dump(std::string& dump) {}
std::list<NotifyArgs> InputMapper::reconfigure(nsecs_t when, const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
return {};
}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 211be9f..f017317 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -55,7 +55,7 @@
virtual void dump(std::string& dump);
[[nodiscard]] virtual std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes);
+ ConfigurationChanges changes);
[[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
[[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
[[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 3450f86..099a955 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -106,10 +106,10 @@
std::list<NotifyArgs> JoystickInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
- if (!changes) { // first time only
+ if (!changes.any()) { // first time only
// Collect all axes.
for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index c9e92de..49673a2 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -31,7 +31,7 @@
virtual void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 63ea3a8..7388752 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -134,21 +134,26 @@
std::list<NotifyArgs> KeyboardInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
- if (!changes) { // first time only
+ if (!changes.any()) { // first time only
// Configure basic parameters.
configureParameters();
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
mViewport = findViewport(config);
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION)) {
- mKeyboardLayoutInfo =
+ if (!changes.any() ||
+ changes.test(InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION)) {
+ std::optional<KeyboardLayoutInfo> newKeyboardLayoutInfo =
getValueByKey(config.keyboardLayoutAssociations, getDeviceContext().getLocation());
+ if (mKeyboardLayoutInfo != newKeyboardLayoutInfo) {
+ mKeyboardLayoutInfo = newKeyboardLayoutInfo;
+ bumpGeneration();
+ }
}
return out;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 25fad57..bd27383 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -33,7 +33,7 @@
void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 2ffa5cd..13f2e59 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -66,12 +66,12 @@
std::list<NotifyArgs> RotaryEncoderInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
- if (!changes) {
+ if (!changes.any()) {
mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
config.getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 8feaf8e..d3dcbe1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -34,7 +34,7 @@
virtual void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index f7f23a4..a131e35 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -119,10 +119,10 @@
std::list<NotifyArgs> SensorInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
- if (!changes) { // first time only
+ if (!changes.any()) { // first time only
mDeviceEnabled = true;
// Check if device has MSC_TIMESTAMP event.
mHasHardwareTimestamp = getDeviceContext().hasMscEvent(MSC_TIMESTAMP);
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 2f3a396..1f82559 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -36,7 +36,7 @@
void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 60bf857..c72a263 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -125,9 +125,8 @@
const InputReaderConfiguration& readerConfig)
: InputMapper(deviceContext, readerConfig),
mTouchButtonAccumulator(deviceContext),
- mSource(0),
- mDeviceMode(DeviceMode::DISABLED),
- mInputDeviceOrientation(ui::ROTATION_0) {}
+ mConfig(readerConfig),
+ mParameters(computeParameters(deviceContext)) {}
TouchInputMapper::~TouchInputMapper() {}
@@ -290,7 +289,7 @@
std::list<NotifyArgs> TouchInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
+ ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
mConfig = config;
@@ -298,9 +297,9 @@
// Full configuration should happen the first time configure is called and
// when the device type is changed. Changing a device type can affect
// various other parameters so should result in a reconfiguration.
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::DEVICE_TYPE)) {
// Configure basic parameters.
- configureParameters();
+ mParameters = computeParameters(getDeviceContext());
// Configure common accumulators.
mCursorScrollAccumulator.configure(getDeviceContext());
@@ -314,33 +313,34 @@
resolveCalibration();
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) {
+ if (!changes.any() ||
+ changes.test(InputReaderConfiguration::Change::TOUCH_AFFINE_TRANSFORMATION)) {
// Update location calibration to reflect current settings
updateAffineTransformation();
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED)) {
// Update pointer speed.
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
}
+ using namespace ftl::flag_operators;
bool resetNeeded = false;
- if (!changes ||
- (changes &
- (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
- InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
- InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
- InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE |
- InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
+ if (!changes.any() ||
+ changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
+ InputReaderConfiguration::Change::POINTER_CAPTURE |
+ InputReaderConfiguration::Change::POINTER_GESTURE_ENABLEMENT |
+ InputReaderConfiguration::Change::SHOW_TOUCHES |
+ InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE |
+ InputReaderConfiguration::Change::DEVICE_TYPE)) {
// Configure device sources, display dimensions, orientation and
// scaling factors.
configureInputDevice(when, &resetNeeded);
}
- if (changes && resetNeeded) {
+ if (changes.any() && resetNeeded) {
out += reset(when);
// Send reset, unless this is the first time the device has been configured,
@@ -360,112 +360,119 @@
}
}
-void TouchInputMapper::configureParameters() {
+TouchInputMapper::Parameters TouchInputMapper::computeParameters(
+ const InputDeviceContext& deviceContext) {
+ Parameters parameters;
// Use the pointer presentation mode for devices that do not support distinct
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
- mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
+ parameters.gestureMode = deviceContext.hasInputProperty(INPUT_PROP_SEMI_MT)
? Parameters::GestureMode::SINGLE_TOUCH
: Parameters::GestureMode::MULTI_TOUCH;
- const PropertyMap& config = getDeviceContext().getConfiguration();
+ const PropertyMap& config = deviceContext.getConfiguration();
std::optional<std::string> gestureModeString = config.getString("touch.gestureMode");
if (gestureModeString.has_value()) {
if (*gestureModeString == "single-touch") {
- mParameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
+ parameters.gestureMode = Parameters::GestureMode::SINGLE_TOUCH;
} else if (*gestureModeString == "multi-touch") {
- mParameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
+ parameters.gestureMode = Parameters::GestureMode::MULTI_TOUCH;
} else if (*gestureModeString != "default") {
ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString->c_str());
}
}
- configureDeviceType();
+ parameters.deviceType = computeDeviceType(deviceContext);
- mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
+ parameters.hasButtonUnderPad = deviceContext.hasInputProperty(INPUT_PROP_BUTTONPAD);
- mParameters.orientationAware =
+ parameters.orientationAware =
config.getBool("touch.orientationAware")
- .value_or(mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
+ .value_or(parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN);
- mParameters.orientation = ui::ROTATION_0;
+ parameters.orientation = ui::ROTATION_0;
std::optional<std::string> orientationString = config.getString("touch.orientation");
if (orientationString.has_value()) {
- if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
+ if (parameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
} else if (*orientationString == "ORIENTATION_90") {
- mParameters.orientation = ui::ROTATION_90;
+ parameters.orientation = ui::ROTATION_90;
} else if (*orientationString == "ORIENTATION_180") {
- mParameters.orientation = ui::ROTATION_180;
+ parameters.orientation = ui::ROTATION_180;
} else if (*orientationString == "ORIENTATION_270") {
- mParameters.orientation = ui::ROTATION_270;
+ parameters.orientation = ui::ROTATION_270;
} else if (*orientationString != "ORIENTATION_0") {
ALOGW("Invalid value for touch.orientation: '%s'", orientationString->c_str());
}
}
- mParameters.hasAssociatedDisplay = false;
- mParameters.associatedDisplayIsExternal = false;
- if (mParameters.orientationAware ||
- mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
- mParameters.deviceType == Parameters::DeviceType::POINTER ||
- (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
- getDeviceContext().getAssociatedViewport())) {
- mParameters.hasAssociatedDisplay = true;
- if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
- mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
- mParameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
+ parameters.hasAssociatedDisplay = false;
+ parameters.associatedDisplayIsExternal = false;
+ if (parameters.orientationAware ||
+ parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
+ parameters.deviceType == Parameters::DeviceType::POINTER ||
+ (parameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
+ deviceContext.getAssociatedViewport())) {
+ parameters.hasAssociatedDisplay = true;
+ if (parameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
+ parameters.associatedDisplayIsExternal = deviceContext.isExternal();
+ parameters.uniqueDisplayId = config.getString("touch.displayId").value_or("").c_str();
}
}
- if (getDeviceContext().getAssociatedDisplayPort()) {
- mParameters.hasAssociatedDisplay = true;
+ if (deviceContext.getAssociatedDisplayPort()) {
+ parameters.hasAssociatedDisplay = true;
}
// Initial downs on external touch devices should wake the device.
// Normally we don't do this for internal touch screens to prevent them from waking
// up in your pocket but you can enable it using the input device configuration.
- mParameters.wake = config.getBool("touch.wake").value_or(getDeviceContext().isExternal());
+ parameters.wake = config.getBool("touch.wake").value_or(deviceContext.isExternal());
std::optional<int32_t> usiVersionMajor = config.getInt("touch.usiVersionMajor");
std::optional<int32_t> usiVersionMinor = config.getInt("touch.usiVersionMinor");
if (usiVersionMajor.has_value() && usiVersionMinor.has_value()) {
- mParameters.usiVersion = {
+ parameters.usiVersion = {
.majorVersion = *usiVersionMajor,
.minorVersion = *usiVersionMinor,
};
}
- mParameters.enableForInactiveViewport =
+ parameters.enableForInactiveViewport =
config.getBool("touch.enableForInactiveViewport").value_or(false);
+
+ return parameters;
}
-void TouchInputMapper::configureDeviceType() {
- if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
+TouchInputMapper::Parameters::DeviceType TouchInputMapper::computeDeviceType(
+ const InputDeviceContext& deviceContext) {
+ Parameters::DeviceType deviceType;
+ if (deviceContext.hasInputProperty(INPUT_PROP_DIRECT)) {
// The device is a touch screen.
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
+ deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (deviceContext.hasInputProperty(INPUT_PROP_POINTER)) {
// The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
+ deviceType = Parameters::DeviceType::POINTER;
} else {
// The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
+ deviceType = Parameters::DeviceType::POINTER;
}
// Type association takes precedence over the device type found in the idc file.
- std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
+ std::string deviceTypeString = deviceContext.getDeviceTypeAssociation().value_or("");
if (deviceTypeString.empty()) {
deviceTypeString =
- getDeviceContext().getConfiguration().getString("touch.deviceType").value_or("");
+ deviceContext.getConfiguration().getString("touch.deviceType").value_or("");
}
if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ deviceType = Parameters::DeviceType::TOUCH_SCREEN;
} else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
+ deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
} else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DeviceType::POINTER;
+ deviceType = Parameters::DeviceType::POINTER;
} else if (deviceTypeString != "default" && deviceTypeString != "") {
ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
}
+ return deviceType;
}
void TouchInputMapper::dumpParameters(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index e7d66a1..7141924 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -155,7 +155,7 @@
void dump(std::string& dump) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
@@ -192,7 +192,7 @@
};
// Input sources and device mode.
- uint32_t mSource;
+ uint32_t mSource{0};
enum class DeviceMode {
DISABLED, // input is disabled
@@ -203,7 +203,7 @@
ftl_last = POINTER
};
- DeviceMode mDeviceMode;
+ DeviceMode mDeviceMode{DeviceMode::DISABLED};
// The reader's configuration.
InputReaderConfiguration mConfig;
@@ -377,7 +377,6 @@
std::vector<VirtualKey> mVirtualKeys;
- virtual void configureParameters();
virtual void dumpParameters(std::string& dump);
virtual void configureRawPointerAxes();
virtual void dumpRawPointerAxes(std::string& dump);
@@ -413,7 +412,7 @@
// The orientation of the input device relative to that of the display panel. It specifies
// the rotation of the input device coordinates required to produce the display panel
// orientation, so it will depend on whether the device is orientation aware.
- ui::Rotation mInputDeviceOrientation;
+ ui::Rotation mInputDeviceOrientation{ui::ROTATION_0};
// The transform that maps the input device's raw coordinate space to the un-rotated display's
// coordinate space. InputReader generates events in the un-rotated display's coordinate space.
@@ -824,8 +823,8 @@
// Compute input transforms for DIRECT and POINTER modes.
void computeInputTransforms();
-
- void configureDeviceType();
+ static Parameters::DeviceType computeDeviceType(const InputDeviceContext& deviceContext);
+ static Parameters computeParameters(const InputDeviceContext& deviceContext);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 5a1ced4..c72425a 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -16,9 +16,11 @@
#include "../Macros.h"
+#include <chrono>
#include <limits>
#include <optional>
+#include <android-base/stringprintf.h>
#include <android/input.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
@@ -174,8 +176,18 @@
: InputMapper(deviceContext, readerConfig),
mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
mPointerController(getContext()->getPointerController(getDeviceId())),
- mStateConverter(deviceContext),
- mGestureConverter(*getContext(), deviceContext, getDeviceId()) {
+ mStateConverter(deviceContext, mMotionAccumulator),
+ mGestureConverter(*getContext(), deviceContext, getDeviceId()),
+ mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()) {
+ RawAbsoluteAxisInfo slotAxisInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+ ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+ "properly.",
+ deviceContext.getName().c_str());
+ }
+ mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
+
mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
// Even though we don't explicitly delete copy/move semantics, it's safe to
@@ -209,26 +221,39 @@
void TouchpadInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- mGestureConverter.populateMotionRanges(info);
+ if (mPointerCaptured) {
+ mCapturedEventConverter.populateMotionRanges(info);
+ } else {
+ mGestureConverter.populateMotionRanges(info);
+ }
}
void TouchpadInputMapper::dump(std::string& dump) {
dump += INDENT2 "Touchpad Input Mapper:\n";
+ if (mProcessing) {
+ dump += INDENT3 "Currently processing a hardware state\n";
+ }
+ if (mResettingInterpreter) {
+ dump += INDENT3 "Currently resetting gesture interpreter\n";
+ }
+ dump += StringPrintf(INDENT3 "Pointer captured: %s\n", toString(mPointerCaptured));
dump += INDENT3 "Gesture converter:\n";
dump += addLinePrefix(mGestureConverter.dump(), INDENT4);
dump += INDENT3 "Gesture properties:\n";
dump += addLinePrefix(mPropertyProvider.dump(), INDENT4);
+ dump += INDENT3 "Captured event converter:\n";
+ dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
}
std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) {
- if (!changes) {
+ ConfigurationChanges changes) {
+ if (!changes.any()) {
// First time configuration
mPropertyProvider.loadPropertiesFromIdcFile(getDeviceContext().getConfiguration());
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
std::optional<int32_t> displayId = mPointerController->getDisplayId();
ui::Rotation orientation = ui::ROTATION_0;
if (displayId.has_value()) {
@@ -238,13 +263,21 @@
}
mGestureConverter.setOrientation(orientation);
}
- if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) {
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
.setBoolValues({true});
GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve");
accelCurveProp.setRealValues(
createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
accelCurveProp.getCount()));
+ mPropertyProvider.getProperty("Use Custom Touchpad Scroll Accel Curve")
+ .setBoolValues({true});
+ GesturesProp scrollCurveProp = mPropertyProvider.getProperty("Scroll Accel Curve");
+ scrollCurveProp.setRealValues(
+ createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
+ scrollCurveProp.getCount()));
+ mPropertyProvider.getProperty("Scroll X Out Scale").setRealValues({1.0});
+ mPropertyProvider.getProperty("Scroll Y Out Scale").setRealValues({1.0});
mPropertyProvider.getProperty("Invert Scrolling")
.setBoolValues({config.touchpadNaturalScrollingEnabled});
mPropertyProvider.getProperty("Tap Enable")
@@ -252,17 +285,50 @@
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
}
- return {};
+ std::list<NotifyArgs> out;
+ if ((!changes.any() && config.pointerCaptureRequest.enable) ||
+ changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
+ mPointerCaptured = config.pointerCaptureRequest.enable;
+ // The motion ranges are going to change, so bump the generation to clear the cached ones.
+ bumpGeneration();
+ if (mPointerCaptured) {
+ // The touchpad is being captured, so we need to tidy up any fake fingers etc. that are
+ // still being reported for a gesture in progress.
+ out += reset(when);
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ } else {
+ // We're transitioning from captured to uncaptured.
+ mCapturedEventConverter.reset();
+ }
+ if (changes.any()) {
+ out.push_back(NotifyDeviceResetArgs(getContext()->getNextId(), when, getDeviceId()));
+ }
+ }
+ return out;
}
std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
mStateConverter.reset();
+ resetGestureInterpreter(when);
std::list<NotifyArgs> out = mGestureConverter.reset(when);
out += InputMapper::reset(when);
return out;
}
+void TouchpadInputMapper::resetGestureInterpreter(nsecs_t when) {
+ // The GestureInterpreter has no official reset method, but sending a HardwareState with no
+ // fingers down or buttons pressed should get it into a clean state.
+ HardwareState state;
+ state.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+ mResettingInterpreter = true;
+ mGestureInterpreter->PushHardwareState(&state);
+ mResettingInterpreter = false;
+}
+
std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+ if (mPointerCaptured) {
+ return mCapturedEventConverter.process(*rawEvent);
+ }
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
@@ -283,6 +349,11 @@
void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
+ if (mResettingInterpreter) {
+ // We already handle tidying up fake fingers etc. in GestureConverter::reset, so we should
+ // ignore any gestures produced from the interpreter while we're resetting it.
+ return;
+ }
if (!mProcessing) {
ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
return;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index e051097..3128d18 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -21,12 +21,15 @@
#include <vector>
#include <PointerControllerInterface.h>
+#include <utils/Timers.h>
+#include "CapturedTouchpadEventConverter.h"
#include "EventHub.h"
#include "InputDevice.h"
#include "InputMapper.h"
#include "InputReaderBase.h"
#include "NotifyArgs.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
#include "gestures/GestureConverter.h"
#include "gestures/HardwareStateConverter.h"
#include "gestures/PropertyProvider.h"
@@ -47,13 +50,14 @@
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
- uint32_t changes) override;
+ ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
void consumeGesture(const Gesture* gesture);
private:
+ void resetGestureInterpreter(nsecs_t when);
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
[[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
@@ -64,10 +68,19 @@
PropertyProvider mPropertyProvider;
+ // The MultiTouchMotionAccumulator is shared between the HardwareStateConverter and
+ // CapturedTouchpadEventConverter, so that if the touchpad is captured or released while touches
+ // are down, the relevant converter can still benefit from the current axis values stored in the
+ // accumulator.
+ MultiTouchMotionAccumulator mMotionAccumulator;
+
HardwareStateConverter mStateConverter;
GestureConverter mGestureConverter;
+ CapturedTouchpadEventConverter mCapturedEventConverter;
+ bool mPointerCaptured = false;
bool mProcessing = false;
+ bool mResettingInterpreter = false;
std::vector<Gesture> mGesturesToProcess;
};
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 8841b6e..6780dce 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -26,16 +26,11 @@
namespace android {
-HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext)
- : mDeviceContext(deviceContext), mTouchButtonAccumulator(deviceContext) {
- RawAbsoluteAxisInfo slotAxisInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
- if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
- ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
- "properly.",
- deviceContext.getName().c_str());
- }
- mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
+HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator)
+ : mDeviceContext(deviceContext),
+ mMotionAccumulator(motionAccumulator),
+ mTouchButtonAccumulator(deviceContext) {
mTouchButtonAccumulator.configure();
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index c314b0d..633448e 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -41,7 +41,8 @@
// Converts RawEvents into the HardwareState structs used by the gestures library.
class HardwareStateConverter {
public:
- HardwareStateConverter(const InputDeviceContext& deviceContext);
+ HardwareStateConverter(const InputDeviceContext& deviceContext,
+ MultiTouchMotionAccumulator& motionAccumulator);
std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
void reset();
@@ -51,7 +52,7 @@
const InputDeviceContext& mDeviceContext;
CursorButtonAccumulator mCursorButtonAccumulator;
- MultiTouchMotionAccumulator mMotionAccumulator;
+ MultiTouchMotionAccumulator& mMotionAccumulator;
TouchButtonAccumulator mTouchButtonAccumulator;
int32_t mMscTimestamp = 0;
};
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 97138c7..52277ff 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -39,6 +39,7 @@
srcs: [
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
+ "CapturedTouchpadEventConverter_test.cpp",
"EventHub_test.cpp",
"FakeEventHub.cpp",
"FakeInputReaderPolicy.cpp",
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
new file mode 100644
index 0000000..3dc5152
--- /dev/null
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -0,0 +1,784 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <CapturedTouchpadEventConverter.h>
+
+#include <list>
+#include <memory>
+
+#include <EventHub.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+#include <linux/input.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+#include "TestInputListenerMatchers.h"
+
+namespace android {
+
+using testing::AllOf;
+
+class CapturedTouchpadEventConverterTest : public testing::Test {
+public:
+ CapturedTouchpadEventConverterTest()
+ : mFakeEventHub(std::make_unique<FakeEventHub>()),
+ mFakePolicy(sp<FakeInputReaderPolicy>::make()),
+ mReader(mFakeEventHub, mFakePolicy, mFakeListener),
+ mDevice(newDevice()),
+ mDeviceContext(*mDevice, EVENTHUB_ID) {
+ const size_t slotCount = 8;
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
+ mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
+ }
+
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ std::shared_ptr<InputDevice> newDevice() {
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ identifier.bus = 0;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader.getContext(), DEVICE_ID, /*generation=*/2,
+ identifier);
+ mReader.pushNextDevice(device);
+ mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD,
+ identifier.bus);
+ mReader.loopOnce();
+ return device;
+ }
+
+ void addBasicAxesToEventHub() {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 1000, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 1000, 0, 0, 0);
+ }
+
+ CapturedTouchpadEventConverter createConverter() {
+ addBasicAxesToEventHub();
+ return CapturedTouchpadEventConverter(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+ }
+
+ void processAxis(CapturedTouchpadEventConverter& conv, int32_t type, int32_t code,
+ int32_t value) {
+ RawEvent event;
+ event.when = ARBITRARY_TIME;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::list<NotifyArgs> out = conv.process(event);
+ EXPECT_TRUE(out.empty());
+ }
+
+ std::list<NotifyArgs> processSync(CapturedTouchpadEventConverter& conv) {
+ RawEvent event;
+ event.when = ARBITRARY_TIME;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ return conv.process(event);
+ }
+
+ NotifyMotionArgs processSyncAndExpectSingleMotionArg(CapturedTouchpadEventConverter& conv) {
+ std::list<NotifyArgs> args = processSync(conv);
+ EXPECT_EQ(1u, args.size());
+ return std::get<NotifyMotionArgs>(args.front());
+ }
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ TestInputListener mFakeListener;
+ InstrumentedInputReader mReader;
+ std::shared_ptr<InputDevice> mDevice;
+ InputDeviceContext mDeviceContext;
+ MultiTouchMotionAccumulator mAccumulator;
+};
+
+TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_allAxesPresent_populatedCorrectly) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 1100, 0, 0, 35);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 1000, 0, 0, 30);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 900, 0, 0, 25);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 800, 0, 0, 20);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, -3, 4, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ InputDeviceInfo info;
+ conv.populateMotionRanges(info);
+
+ // Most axes should have min, max, and resolution matching the evdev axes.
+ const InputDeviceInfo::MotionRange* posX =
+ info.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, posX);
+ EXPECT_NEAR(0, posX->min, EPSILON);
+ EXPECT_NEAR(4000, posX->max, EPSILON);
+ EXPECT_NEAR(45, posX->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* posY =
+ info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, posY);
+ EXPECT_NEAR(0, posY->min, EPSILON);
+ EXPECT_NEAR(2500, posY->max, EPSILON);
+ EXPECT_NEAR(40, posY->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* touchMajor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, touchMajor);
+ EXPECT_NEAR(0, touchMajor->min, EPSILON);
+ EXPECT_NEAR(1100, touchMajor->max, EPSILON);
+ EXPECT_NEAR(35, touchMajor->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* touchMinor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, touchMinor);
+ EXPECT_NEAR(0, touchMinor->min, EPSILON);
+ EXPECT_NEAR(1000, touchMinor->max, EPSILON);
+ EXPECT_NEAR(30, touchMinor->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* toolMajor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOOL_MAJOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, toolMajor);
+ EXPECT_NEAR(0, toolMajor->min, EPSILON);
+ EXPECT_NEAR(900, toolMajor->max, EPSILON);
+ EXPECT_NEAR(25, toolMajor->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* toolMinor =
+ info.getMotionRange(AMOTION_EVENT_AXIS_TOOL_MINOR, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, toolMinor);
+ EXPECT_NEAR(0, toolMinor->min, EPSILON);
+ EXPECT_NEAR(800, toolMinor->max, EPSILON);
+ EXPECT_NEAR(20, toolMinor->resolution, EPSILON);
+
+ // ...except orientation and pressure, which get scaled, and size, which is generated from other
+ // values.
+ const InputDeviceInfo::MotionRange* orientation =
+ info.getMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, orientation);
+ EXPECT_NEAR(-M_PI_2, orientation->min, EPSILON);
+ EXPECT_NEAR(M_PI_2, orientation->max, EPSILON);
+ EXPECT_NEAR(0, orientation->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* pressure =
+ info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, pressure);
+ EXPECT_NEAR(0, pressure->min, EPSILON);
+ EXPECT_NEAR(1, pressure->max, EPSILON);
+ EXPECT_NEAR(0, pressure->resolution, EPSILON);
+
+ const InputDeviceInfo::MotionRange* size =
+ info.getMotionRange(AMOTION_EVENT_AXIS_SIZE, AINPUT_SOURCE_TOUCHPAD);
+ ASSERT_NE(nullptr, size);
+ EXPECT_NEAR(0, size->min, EPSILON);
+ EXPECT_NEAR(1, size->max, EPSILON);
+ EXPECT_NEAR(0, size->resolution, EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, MotionRanges_bareMinimumAxesPresent_populatedCorrectly) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ InputDeviceInfo info;
+ conv.populateMotionRanges(info);
+
+ // Only the bare minimum motion ranges should be reported, and no others (e.g. size shouldn't be
+ // present, since it's generated from axes that aren't provided by this device).
+ EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHPAD));
+ EXPECT_NE(nullptr, info.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHPAD));
+ EXPECT_EQ(2u, info.getMotionRanges().size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_motionReportedCorrectly) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(50, 100), WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_touchDimensionsPassedThrough) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 1000, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 1000, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MAJOR, 250);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MINOR, 120);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 400);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MINOR, 200);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithTouchDimensions(250, 120), WithToolDimensions(400, 200)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_orientationCalculatedCorrectly) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, -3, 4, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_ORIENTATION, -3);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(-3 * M_PI / 8,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION),
+ EPSILON);
+
+ processAxis(conv, EV_ABS, ABS_MT_ORIENTATION, 0);
+
+ EXPECT_NEAR(0,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION),
+ EPSILON);
+
+ processAxis(conv, EV_ABS, ABS_MT_ORIENTATION, 4);
+
+ EXPECT_NEAR(M_PI / 2,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_ORIENTATION),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OneFinger_pressureScaledCorrectly) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_PRESSURE, 128);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv), WithPressure(0.5));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withAllSizeAxes_sizeCalculatedFromTouchMajorMinorAverage) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MAJOR, 138);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MINOR, 118);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 200);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MINOR, 210);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withMajorDimensionsOnly_sizeCalculatedFromTouchMajor) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TOUCH_MAJOR, 128);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 200);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withToolDimensionsOnly_sizeCalculatedFromToolMajorMinorAverage) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 138);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MINOR, 118);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest,
+ OneFinger_withToolMajorOnly_sizeCalculatedFromTouchMajor) {
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, 0, 4000, 0, 0, 45);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, 0, 2500, 0, 0, 40);
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, 0, 256, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_WIDTH_MAJOR, 128);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_NEAR(0.5,
+ processSyncAndExpectSingleMotionArg(conv).pointerCoords[0].getAxisValue(
+ AMOTION_EVENT_AXIS_SIZE),
+ EPSILON);
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, OnePalm_neverReported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerTurningIntoPalm_cancelled) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithToolType(ToolType::FINGER),
+ WithPointerCount(1u)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL), WithPointerCount(1u)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, PalmTurningIntoFinger_reported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(51, 100)));
+
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 100)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerArrivingAfterPalm_onlyFingerReported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_EQ(0u, processSync(conv).size());
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 100);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 150);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(100, 150)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 102);
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 98);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 148);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(98, 148)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerAndFingerTurningIntoPalm_partiallyCancelled) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 250);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED), WithPointerCount(2u)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, FingerAndPalmTurningIntoFinger_reported) {
+ addBasicAxesToEventHub();
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_PALM, 0, 0, 0);
+ CapturedTouchpadEventConverter conv(*mReader.getContext(), mDeviceContext, mAccumulator,
+ DEVICE_ID);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 250);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 51);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 251);
+ processAxis(conv, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u)));
+}
+
+TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 100);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+
+ EXPECT_THAT(processSyncAndExpectSingleMotionArg(conv),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithCoords(50, 100), WithToolType(ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 52);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 99);
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 250);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 200);
+
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(52, 99), WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerCoords(0, 52, 99),
+ WithPointerCoords(1, 250, 200), WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 255);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_Y, 202);
+
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u),
+ WithPointerCoords(0, 52, 99), WithPointerCoords(1, 255, 202),
+ WithPointerToolType(1, ToolType::FINGER),
+ WithPointerToolType(0, ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerCoords(0, 52, 99),
+ WithPointerCoords(1, 255, 202), WithPointerToolType(0, ToolType::FINGER),
+ WithPointerToolType(1, ToolType::FINGER)));
+
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ processAxis(conv, EV_KEY, BTN_TOOL_FINGER, 0);
+ processAxis(conv, EV_KEY, BTN_TOUCH, 0);
+
+ args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(1u),
+ WithCoords(255, 202), WithToolType(ToolType::FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithPointerCount(1u),
+ WithCoords(255, 202), WithToolType(ToolType::FINGER)));
+}
+
+// Pointer IDs max out at 31, and so must be reused once a touch is lifted to avoid running out.
+TEST_F(CapturedTouchpadEventConverterTest, PointerIdsReusedAfterLift) {
+ CapturedTouchpadEventConverter conv = createConverter();
+
+ // Put down two fingers, which should get IDs 0 and 1.
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 10);
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 20);
+
+ processAxis(conv, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+
+ std::list<NotifyArgs> args = processSync(conv);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPointerCount(1u),
+ WithPointerId(/*index=*/0, /*id=*/0)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1)));
+
+ // Lift the finger in slot 0, freeing up pointer ID 0...
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, -1);
+
+ // ...and simultaneously add a finger in slot 2.
+ processAxis(conv, EV_ABS, ABS_MT_SLOT, 2);
+ processAxis(conv, EV_ABS, ABS_MT_TRACKING_ID, 3);
+ processAxis(conv, EV_ABS, ABS_MT_POSITION_X, 30);
+
+ args = processSync(conv);
+ ASSERT_EQ(3u, args.size());
+ // Slot 1 being present will result in a MOVE event, even though it hasn't actually moved (see
+ // comments in CapturedTouchpadEventConverter::sync).
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithPointerCount(2u),
+ WithPointerId(/*index=*/0, /*id=*/0), WithPointerId(/*index=*/1, /*id=*/1)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/0),
+ WithPointerId(/*index=*/1, /*id=*/1)));
+ args.pop_front();
+ // Slot 0 being lifted causes the finger from slot 1 to move up to index 0, but keep its
+ // previous ID. The new finger in slot 2 should take ID 0, which was just freed up.
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithPointerCount(2u), WithPointerId(/*index=*/0, /*id=*/1),
+ WithPointerId(/*index=*/1, /*id=*/0)));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 4626f5a..212fceb 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -617,9 +617,11 @@
}
// If device sysfs changed -> reopen the device
if (!mRawLightInfos.empty() && !foundDevice->classes.test(InputDeviceClass::LIGHT)) {
+ InputDeviceIdentifier identifier = foundDevice->identifier;
+ ftl::Flags<InputDeviceClass> classes = foundDevice->classes;
removeDevice(foundDeviceId);
- addDevice(foundDeviceId, foundDevice->identifier.name,
- foundDevice->classes | InputDeviceClass::LIGHT, foundDevice->identifier.bus);
+ addDevice(foundDeviceId, identifier.name, classes | InputDeviceClass::LIGHT,
+ identifier.bus);
}
}
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 19d46c8..5bea2ba 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -25,6 +25,7 @@
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
#include "InstrumentedInputReader.h"
+#include "MultiTouchMotionAccumulator.h"
#include "TestConstants.h"
#include "TestInputListener.h"
@@ -38,8 +39,10 @@
mReader(mFakeEventHub, mFakePolicy, mFakeListener),
mDevice(newDevice()),
mDeviceContext(*mDevice, EVENTHUB_ID) {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, 7, 0, 0, 0);
- mConverter = std::make_unique<HardwareStateConverter>(mDeviceContext);
+ const size_t slotCount = 8;
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
+ mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
+ mConverter = std::make_unique<HardwareStateConverter>(mDeviceContext, mAccumulator);
}
protected:
@@ -90,6 +93,7 @@
InstrumentedInputReader mReader;
std::shared_ptr<InputDevice> mDevice;
InputDeviceContext mDeviceContext;
+ MultiTouchMotionAccumulator mAccumulator;
std::unique_ptr<HardwareStateConverter> mConverter;
};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 27d7b9c..a6cdee5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -62,6 +62,7 @@
static constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
static constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP;
static constexpr int32_t ACTION_HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
+static constexpr int32_t ACTION_HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
static constexpr int32_t ACTION_HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
@@ -205,11 +206,9 @@
using AnrResult = std::pair<sp<IBinder>, int32_t /*pid*/>;
-protected:
- virtual ~FakeInputDispatcherPolicy() {}
-
public:
- FakeInputDispatcherPolicy() {}
+ FakeInputDispatcherPolicy() = default;
+ virtual ~FakeInputDispatcherPolicy() = default;
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
@@ -404,6 +403,16 @@
mInterceptKeyTimeout = timeout;
}
+ void assertUserActivityPoked() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked";
+ }
+
+ void assertUserActivityNotPoked() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mPokedUserActivity) << "Expected user activity not to have been poked";
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -425,6 +434,7 @@
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
+ bool mPokedUserActivity GUARDED_BY(mLock) = false;
std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
@@ -523,22 +533,20 @@
void notifyVibratorState(int32_t deviceId, bool isOn) override {}
- void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
- *outConfig = mConfig;
- }
+ InputDispatcherConfiguration getDispatcherConfiguration() override { return mConfig; }
- bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
- switch (inputEvent->getType()) {
+ switch (inputEvent.getType()) {
case InputEventType::KEY: {
- const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
- mFilteredEvent = std::make_unique<KeyEvent>(*keyEvent);
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent);
+ mFilteredEvent = std::make_unique<KeyEvent>(keyEvent);
break;
}
case InputEventType::MOTION: {
- const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
- mFilteredEvent = std::make_unique<MotionEvent>(*motionEvent);
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent);
+ mFilteredEvent = std::make_unique<MotionEvent>(motionEvent);
break;
}
default: {
@@ -549,8 +557,8 @@
return true;
}
- void interceptKeyBeforeQueueing(const KeyEvent* inputEvent, uint32_t&) override {
- if (inputEvent->getAction() == AKEY_EVENT_ACTION_UP) {
+ void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override {
+ if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
// Clear intercept state when we handled the event.
mInterceptKeyTimeout = 0ms;
}
@@ -558,15 +566,16 @@
void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
// Clear intercept state so we could dispatch the event in next wake.
mInterceptKeyTimeout = 0ms;
return delay;
}
- bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
- return false;
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) override {
+ return {};
}
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
@@ -578,7 +587,10 @@
mLastNotifySwitch = NotifySwitchArgs(/*id=*/1, when, policyFlags, switchValues, switchMask);
}
- void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t, int32_t) override {
+ std::scoped_lock lock(mLock);
+ mPokedUserActivity = true;
+ }
void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
@@ -610,12 +622,12 @@
class InputDispatcherTest : public testing::Test {
protected:
- sp<FakeInputDispatcherPolicy> mFakePolicy;
+ std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy;
std::unique_ptr<InputDispatcher> mDispatcher;
void SetUp() override {
- mFakePolicy = sp<FakeInputDispatcherPolicy>::make();
- mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy, STALE_EVENT_TIMEOUT);
+ mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
@@ -623,7 +635,7 @@
void TearDown() override {
ASSERT_EQ(OK, mDispatcher->stop());
- mFakePolicy.clear();
+ mFakePolicy.reset();
mDispatcher.reset();
}
@@ -1190,6 +1202,10 @@
mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
}
+ void setDisableUserActivity(bool disableUserActivity) {
+ mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
+ }
+
void setAlpha(float alpha) { mInfo.alpha = alpha; }
void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
@@ -1742,6 +1758,28 @@
return args;
}
+static NotifyKeyArgs generateSystemShortcutArgs(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, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, 0, action, /* flags */ 0, AKEYCODE_C, KEY_C, AMETA_META_ON,
+ currentTime);
+
+ return args;
+}
+
+static NotifyKeyArgs generateAssistantKeyArgs(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, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ displayId, 0, action, /* flags */ 0, AKEYCODE_ASSIST, KEY_ASSISTANT,
+ AMETA_NONE, currentTime);
+
+ return args;
+}
+
[[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source,
int32_t displayId,
const std::vector<PointF>& points) {
@@ -2418,6 +2456,116 @@
}
/**
+ * Start hovering in a window. While this hover is still active, make another window appear on top.
+ * The top, obstructing window has no input channel, so it's not supposed to receive input.
+ * While the top window is present, the hovering is stopped.
+ * Later, hovering gets resumed again.
+ * Ensure that new hover gesture is handled correctly.
+ * This test reproduces a crash where the HOVER_EXIT event wasn't getting dispatched correctly
+ * to the window that's currently being hovered over.
+ */
+TEST_F(InputDispatcherTest, HoverWhileWindowAppears) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ // Only a single window is present at first
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Start hovering in the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, an obscuring window appears!
+ sp<FakeWindowHandle> obscuringWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window",
+ ADISPLAY_ID_DEFAULT,
+ /*token=*/std::make_optional<sp<IBinder>>(nullptr));
+ obscuringWindow->setFrame(Rect(0, 0, 200, 200));
+ obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
+ obscuringWindow->setNoInputChannel(true);
+ obscuringWindow->setFocusable(false);
+ obscuringWindow->setAlpha(1.0);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+ // While this new obscuring window is present, the hovering is stopped
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Now the obscuring window goes away.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // And a new hover gesture starts.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
+/**
+ * Same test as 'HoverWhileWindowAppears' above, but here, we also send some HOVER_MOVE events to
+ * the obscuring window.
+ */
+TEST_F(InputDispatcherTest, HoverMoveWhileWindowAppears) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ // Only a single window is present at first
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Start hovering in the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, an obscuring window appears!
+ sp<FakeWindowHandle> obscuringWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window",
+ ADISPLAY_ID_DEFAULT,
+ /*token=*/std::make_optional<sp<IBinder>>(nullptr));
+ obscuringWindow->setFrame(Rect(0, 0, 200, 200));
+ obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
+ obscuringWindow->setNoInputChannel(true);
+ obscuringWindow->setFocusable(false);
+ obscuringWindow->setAlpha(1.0);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {obscuringWindow, window}}});
+
+ // While this new obscuring window is present, the hovering continues. The event can't go to the
+ // bottom window due to obstructed touches, so it should generate HOVER_EXIT for that window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ obscuringWindow->assertNoEvents();
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // Now the obscuring window goes away.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Hovering continues in the same position. The hovering pointer re-enters the bottom window,
+ // so it should generate a HOVER_ENTER
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now the MOVE should be getting dispatched normally
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
* down. Then, on the left window, also place second touch pointer down.
@@ -3409,7 +3557,9 @@
.build()));
window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ // We already canceled the hovering implicitly by injecting the "DOWN" event without lifting the
+ // hover first. Therefore, injection of HOVER_EXIT is inconsistent, and should fail.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT,
AINPUT_SOURCE_MOUSE)
@@ -3641,6 +3791,30 @@
AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
+TEST_F(InputDispatcherTest, NotifyDeviceResetCancelsHoveringStream) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(10))
+ .build());
+
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // When device reset happens, that hover stream should be terminated with ACTION_HOVER_EXIT
+ mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+
+ // After the device has been reset, a new hovering stream can be sent to the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(15).y(15))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
TEST_F(InputDispatcherTest, InterceptKeyByPolicy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -3771,7 +3945,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
window->setFocusable(true);
- mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(true);
@@ -3785,7 +3959,7 @@
window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
// All windows are removed from the display. Ensure that we can no longer dispatch to it.
- mDispatcher->onWindowInfosChanged({}, {});
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
window->consumeFocusEvent(false);
@@ -3801,7 +3975,7 @@
// Ensure window is non-split and have some transform.
window->setPreventSplitting(true);
window->setWindowOffset(20, 40);
- mDispatcher->onWindowInfosChanged({*window->getInfo()}, {});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -3848,12 +4022,12 @@
info.displayId = displayId;
info.transform = transform;
mDisplayInfos.push_back(std::move(info));
- mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+ mDispatcher->onWindowInfosChanged({mWindowInfos, mDisplayInfos, 0, 0});
}
void addWindow(const sp<WindowInfoHandle>& windowHandle) {
mWindowInfos.push_back(*windowHandle->getInfo());
- mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+ mDispatcher->onWindowInfosChanged({mWindowInfos, mDisplayInfos, 0, 0});
}
void removeAllWindowsAndDisplays() {
@@ -3957,6 +4131,7 @@
firstWindow->assertNoEvents();
const MotionEvent* event = secondWindow->consumeMotion();
+ ASSERT_NE(nullptr, event);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
// Ensure that the events from the "getRaw" API are in logical display coordinates.
@@ -4530,6 +4705,94 @@
// Window should receive key down event.
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_DisableUserActivity) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setDisableUserActivity(true);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityNotPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+
+ // System key is not passed down
+ window->assertNoEvents();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+
+ // System key is not passed down
+ window->assertNoEvents();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ window->setDisableUserActivity(true);
+ window->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->waitForIdle();
+
+ // System key is not passed down
+ window->assertNoEvents();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
}
TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
@@ -4952,7 +5215,7 @@
displayInfo.displayId = ADISPLAY_ID_DEFAULT;
displayInfo.transform = transform;
- mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo});
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0});
const NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -5281,9 +5544,10 @@
sp<FakeWindowHandle> mWindow;
virtual void SetUp() override {
- mFakePolicy = sp<FakeInputDispatcherPolicy>::make();
+ mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
- mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy);
+ mDispatcher->requestRefreshConfiguration();
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
ASSERT_EQ(OK, mDispatcher->start());
@@ -5721,7 +5985,7 @@
displayInfos[1].displayId = SECOND_DISPLAY_ID;
displayInfos[1].transform = secondDisplayTransform;
- mDispatcher->onWindowInfosChanged({}, displayInfos);
+ mDispatcher->onWindowInfosChanged({{}, displayInfos, 0, 0});
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
@@ -6140,6 +6404,28 @@
touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
+/**
+ * When one of the windows is slippery, the touch should not slip into the other window with the
+ * same input channel.
+ */
+TEST_F(InputDispatcherMultiWindowSameTokenTests, TouchDoesNotSlipEvenIfSlippery) {
+ mWindow1->setSlippery(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+
+ // Touch down in window 1
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{50, 50}}));
+ consumeMotionEvent(mWindow1, ACTION_DOWN, {{50, 50}});
+
+ // Move touch to be above window 2. Even though window 1 is slippery, touch should not slip.
+ // That means the gesture should continue normally, without any ACTION_CANCEL or ACTION_DOWN
+ // getting generated.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {{150, 150}}));
+
+ consumeMotionEvent(mWindow1, ACTION_MOVE, {{150, 150}});
+}
+
class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
virtual void SetUp() override {
InputDispatcherTest::SetUp();
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index ae30006..ad48a79 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -50,12 +50,12 @@
mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
}
-std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
- if (!changes ||
- (changes &
- (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
- InputReaderConfiguration::CHANGE_DEVICE_TYPE))) {
+std::list<NotifyArgs> InputMapperTest::configureDevice(ConfigurationChanges changes) {
+ using namespace ftl::flag_operators;
+ if (!changes.any() ||
+ (changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
+ InputReaderConfiguration::Change::POINTER_CAPTURE |
+ InputReaderConfiguration::Change::DEVICE_TYPE))) {
mReader->requestRefreshConfiguration(changes);
mReader->loopOnce();
}
@@ -94,7 +94,7 @@
ViewportType viewportType) {
mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
uniqueId, physicalPort, viewportType);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
}
void InputMapperTest::clearViewports() {
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index df601ea..d969034 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -54,7 +54,7 @@
void TearDown() override;
void addConfigurationProperty(const char* key, const char* value);
- std::list<NotifyArgs> configureDevice(uint32_t changes);
+ std::list<NotifyArgs> configureDevice(ConfigurationChanges changes);
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
const std::string& location, int32_t eventHubId,
ftl::Flags<InputDeviceClass> classes, int bus = 0);
@@ -62,7 +62,7 @@
T& addMapperAndConfigure(Args... args) {
T& mapper =
mDevice->addMapper<T>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), args...);
- configureDevice(0);
+ configureDevice(/*changes=*/{});
std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
resetArgList += mapper.reset(ARBITRARY_TIME);
// Loop the reader to flush the input listener queue.
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index da3fe5b..d56db11 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -265,13 +265,13 @@
}
std::list<NotifyArgs> reconfigure(nsecs_t, const InputReaderConfiguration& config,
- uint32_t changes) override {
+ ConfigurationChanges changes) override {
std::scoped_lock<std::mutex> lock(mLock);
mConfigureWasCalled = true;
// Find the associated viewport if exist.
const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
- if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ if (displayPort && changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
mViewport = config.getDisplayViewportByPort(*displayPort);
}
@@ -610,12 +610,12 @@
void disableDevice(int32_t deviceId) {
mFakePolicy->addDisabledDevice(deviceId);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::ENABLED_STATE);
}
void enableDevice(int32_t deviceId) {
mFakePolicy->removeDisabledDevice(deviceId);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::ENABLED_STATE);
}
FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
@@ -1043,7 +1043,7 @@
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
ui::ROTATION_0, /*isActive=*/true, "local:1", hdmi1,
ViewportType::EXTERNAL);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO);
mReader->loopOnce();
// Add the device, and make sure all of the callbacks are triggered.
@@ -1142,21 +1142,21 @@
NotifyPointerCaptureChangedArgs args;
auto request = mFakePolicy->setPointerCapture(true);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
mFakePolicy->setPointerCapture(false);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
// Verify that the Pointer Capture state is not updated when the configuration value
// does not change.
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasNotCalled();
}
@@ -1527,7 +1527,7 @@
ViewportType viewportType) {
mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /*isActive=*/true,
uniqueId, physicalPort, viewportType);
- mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO);
}
void assertReceivedMotion(int32_t action, const std::vector<Point>& points) {
@@ -2070,7 +2070,7 @@
TYPED_TEST(StylusButtonIntegrationTest, DISABLED_StylusButtonMotionEventsDisabled) {
TestFixture::mFakePolicy->setStylusButtonMotionEventsEnabled(false);
TestFixture::mReader->requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
+ InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
const Point centerPoint = TestFixture::mTouchscreen->getCenterPoint();
const auto touchscreenId = TestFixture::mTouchscreenInfo.getId();
@@ -2341,7 +2341,7 @@
TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
// Configuration.
InputReaderConfiguration config;
- std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, 0);
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
// Reset.
unused += mDevice->reset(ARBITRARY_TIME);
@@ -2402,7 +2402,7 @@
mapper2.setMetaState(AMETA_SHIFT_ON);
InputReaderConfiguration config;
- std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, 0);
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
std::optional<std::string> propertyValue = mDevice->getConfiguration().getString("key");
ASSERT_TRUE(propertyValue.has_value())
@@ -2484,7 +2484,8 @@
// First Configuration.
std::list<NotifyArgs> unused =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
// Device should be enabled by default.
ASSERT_TRUE(mDevice->isEnabled());
@@ -2495,7 +2496,7 @@
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
// Device should be disabled because it is associated with a specific display via
// input port <-> display port association, but the corresponding display is not found
ASSERT_FALSE(mDevice->isEnabled());
@@ -2505,18 +2506,18 @@
ui::ROTATION_0, /*isActive=*/true, UNIQUE_ID, hdmi,
ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
// Device should be disabled after set disable.
mFakePolicy->addDisabledDevice(mDevice->getId());
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ InputReaderConfiguration::Change::ENABLED_STATE);
ASSERT_FALSE(mDevice->isEnabled());
// Device should still be disabled even found the associated display.
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_FALSE(mDevice->isEnabled());
}
@@ -2526,14 +2527,15 @@
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
AINPUT_SOURCE_KEYBOARD);
std::list<NotifyArgs> unused =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
ASSERT_TRUE(mDevice->isEnabled());
// Device should be disabled because it is associated with a specific display, but the
// corresponding display is not found.
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_FALSE(mDevice->isEnabled());
// Device should be enabled when a display is found.
@@ -2541,18 +2543,18 @@
ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
// Device should be disabled after set disable.
mFakePolicy->addDisabledDevice(mDevice->getId());
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ InputReaderConfiguration::Change::ENABLED_STATE);
ASSERT_FALSE(mDevice->isEnabled());
// Device should still be disabled even found the associated display.
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_FALSE(mDevice->isEnabled());
}
@@ -2561,14 +2563,15 @@
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
AINPUT_SOURCE_KEYBOARD);
std::list<NotifyArgs> unused =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
NO_PORT, ViewportType::INTERNAL);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
}
@@ -3423,7 +3426,7 @@
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/0);
+ /*changes=*/{});
unused += device2->reset(ARBITRARY_TIME);
// Prepared displays and associated info.
@@ -3436,7 +3439,7 @@
// No associated display viewport found, should disable the device.
unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_FALSE(device2->isEnabled());
// Prepare second display.
@@ -3447,7 +3450,7 @@
SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL);
// Default device will reconfigure above, need additional reconfiguration for another device.
unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
// Device should be enabled after the associated display is found.
ASSERT_TRUE(mDevice->isEnabled());
@@ -3535,7 +3538,7 @@
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/0);
+ /*changes=*/{});
unused += device2->reset(ARBITRARY_TIME);
ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
@@ -3598,7 +3601,7 @@
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/0);
+ /*changes=*/{});
unused += device2->reset(ARBITRARY_TIME);
// Initial metastate is AMETA_NONE.
@@ -3667,7 +3670,7 @@
// Disable device, it should synthesize cancellation events for down events.
mFakePolicy->addDisabledDevice(DEVICE_ID);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ configureDevice(InputReaderConfiguration::Change::ENABLED_STATE);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -3681,18 +3684,28 @@
AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
std::list<NotifyArgs> unused =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+ uint32_t generation = mReader->getContext()->getGeneration();
mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION);
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag,
deviceInfo.getKeyboardLayoutInfo()->languageTag);
ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType,
deviceInfo.getKeyboardLayoutInfo()->layoutType);
+ ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
+
+ // Call change layout association with the same values: Generation shouldn't change
+ generation = mReader->getContext()->getGeneration();
+ mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO);
+ unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
+ ASSERT_TRUE(mReader->getContext()->getGeneration() == generation);
}
TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) {
@@ -3703,7 +3716,7 @@
addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
AINPUT_KEYBOARD_TYPE_ALPHABETIC);
InputReaderConfiguration config;
- std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, 0);
+ std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag);
ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType);
@@ -4509,7 +4522,7 @@
// and events are generated the usual way.
const uint32_t generation = mReader->getContext()->getGeneration();
mFakePolicy->setPointerCapture(false);
- configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -4558,7 +4571,7 @@
// Enable Pointer Capture
mFakePolicy->setPointerCapture(true);
- configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
NotifyPointerCaptureChangedArgs captureArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
ASSERT_TRUE(captureArgs.request.enable);
@@ -4600,7 +4613,7 @@
// Enable Pointer Capture.
mFakePolicy->setPointerCapture(true);
- configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
NotifyPointerCaptureChangedArgs captureArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
ASSERT_TRUE(captureArgs.request.enable);
@@ -4626,7 +4639,7 @@
// The InputDevice is not associated with any display.
prepareSecondaryDisplay();
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
mFakePointerController->setPosition(100, 200);
@@ -4653,7 +4666,7 @@
prepareSecondaryDisplay();
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
mFakePointerController->setPosition(100, 200);
@@ -4678,7 +4691,7 @@
// Associate the InputDevice with the secondary display.
prepareSecondaryDisplay();
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
// The mapper should not generate any events because it is associated with a display that is
// different from the pointer display.
@@ -5845,7 +5858,7 @@
viewport->physicalRight = 30;
viewport->physicalBottom = 610;
mFakePolicy->updateViewport(*viewport);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
// Start the touch.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
@@ -6570,7 +6583,7 @@
auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
viewport->isActive = false;
mFakePolicy->updateViewport(*viewport);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
// We should receive a cancel event for the ongoing gesture.
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6595,7 +6608,7 @@
// Make the viewport active again. The device should resume processing events.
viewport->isActive = true;
mFakePolicy->updateViewport(*viewport);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
// The device is reset because it changes back to direct mode, without generating any events.
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
@@ -6763,7 +6776,7 @@
// Send update to the mapper.
std::list<NotifyArgs> unused2 =
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DEVICE_TYPE /*changes*/);
+ InputReaderConfiguration::Change::DEVICE_TYPE /*changes*/);
// Check whether device type update was successful.
ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources());
@@ -6784,7 +6797,7 @@
viewport->physicalRight = DISPLAY_WIDTH / 2;
viewport->physicalBottom = DISPLAY_HEIGHT / 2;
mFakePolicy->updateViewport(*viewport);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
@@ -6873,7 +6886,7 @@
v.uniqueId = UNIQUE_ID;
v.type = ViewportType::INTERNAL;
mFakePolicy->updateViewport(v);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
}
void assertReceivedMove(const Point& point) {
@@ -7233,7 +7246,7 @@
mStylusState.pressure = 0.f;
mStylusState.toolType = ToolType::STYLUS;
mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
- configureDevice(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
+ configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
processExternalStylusState(mapper);
return mapper;
}
@@ -9289,7 +9302,7 @@
// Don't set touch.enableForInactiveViewport to verify the default behavior.
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
/*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9309,7 +9322,7 @@
addConfigurationProperty("touch.enableForInactiveViewport", "1");
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
/*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9331,7 +9344,7 @@
ASSERT_TRUE(optionalDisplayViewport.has_value());
DisplayViewport displayViewport = *optionalDisplayViewport;
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -9347,7 +9360,7 @@
// Deactivate display viewport
displayViewport.isActive = false;
ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
// The ongoing touch should be canceled immediately
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -9362,7 +9375,7 @@
// Reactivate display viewport
displayViewport.isActive = true;
ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport));
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
// Finger move again starts new gesture
x += 10, y += 10;
@@ -9405,7 +9418,7 @@
mFakePolicy->getReaderConfiguration());
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/0);
+ /*changes=*/{});
unused += device2->reset(ARBITRARY_TIME);
// Setup PointerController.
@@ -9426,8 +9439,8 @@
// Default device will reconfigure above, need additional reconfiguration for another device.
unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+ InputReaderConfiguration::Change::DISPLAY_INFO |
+ InputReaderConfiguration::Change::SHOW_TOUCHES);
// Two fingers down at default display.
int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -9458,7 +9471,7 @@
// Disable the show touches configuration and ensure the spots are cleared.
mFakePolicy->setShowTouches(false);
unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+ InputReaderConfiguration::Change::SHOW_TOUCHES);
ASSERT_TRUE(fakePointerController->getSpots().empty());
}
@@ -10368,7 +10381,7 @@
// non captured touchpad should be a mouse source
mFakePolicy->setPointerCapture(false);
- configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
}
@@ -10453,7 +10466,7 @@
// captured touchpad should be a touchpad device
mFakePolicy->setPointerCapture(true);
- configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
@@ -10839,7 +10852,7 @@
auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
viewport->isActive = false;
mFakePolicy->updateViewport(*viewport);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
@@ -10947,14 +10960,6 @@
mFakePolicy.clear();
}
- std::list<NotifyArgs> configureDevice(uint32_t changes) {
- if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
- mReader->requestRefreshConfiguration(changes);
- mReader->loopOnce();
- }
- return mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
- }
-
std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
const std::string& location, int32_t eventHubId,
ftl::Flags<InputDeviceClass> classes) {
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 338b747..db6f254 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -73,6 +73,12 @@
return arg.pointerCount == count;
}
+MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
+ const auto argPointerId = arg.pointerProperties[index].id;
+ *result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
+ return argPointerId == id;
+}
+
MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
const auto argX = arg.pointerCoords[0].getX();
const auto argY = arg.pointerCoords[0].getY();
@@ -136,6 +142,22 @@
return argPressure == pressure;
}
+MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
+ const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+ const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+ *result_listener << "expected touch dimensions " << maj << " major x " << min
+ << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+ return argMajor == maj && argMinor == min;
+}
+
+MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
+ const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+ const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
+ *result_listener << "expected tool dimensions " << maj << " major x " << min
+ << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+ return argMajor == maj && argMinor == min;
+}
+
MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
const auto argToolType = arg.pointerProperties[0].toolType;
*result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
@@ -143,6 +165,14 @@
return argToolType == toolType;
}
+MATCHER_P2(WithPointerToolType, pointer, toolType,
+ "InputEvent with specified tool type for pointer") {
+ const auto argToolType = arg.pointerProperties[pointer].toolType;
+ *result_listener << "expected pointer " << pointer << " to have tool type "
+ << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
+ return argToolType == toolType;
+}
+
MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
*result_listener << "expected flags " << flags << ", but got " << arg.flags;
return arg.flags == static_cast<int32_t>(flags);
diff --git a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index 8ed1f31..8098ef2 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -53,12 +53,14 @@
[&]() -> void {
std::list<NotifyArgs> unused =
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
- fdp->ConsumeIntegral<int32_t>());
+ InputReaderConfiguration::Change(
+ fdp->ConsumeIntegral<int32_t>()));
},
[&]() -> void {
// Need to reconfigure with 0 or you risk a NPE.
std::list<NotifyArgs> unused =
- mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, 0);
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ InputReaderConfiguration::Change(0));
InputDeviceInfo info;
mapper.populateDeviceInfo(info);
},
@@ -71,7 +73,8 @@
// Need to reconfigure with 0 or you risk a NPE.
std::list<NotifyArgs> unused =
- mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, 0);
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ InputReaderConfiguration::Change(0));
RawEvent rawEvent{fdp->ConsumeIntegral<nsecs_t>(),
fdp->ConsumeIntegral<nsecs_t>(),
fdp->ConsumeIntegral<int32_t>(),
@@ -90,7 +93,8 @@
[&]() -> void {
// Need to reconfigure with 0 or you risk a NPE.
std::list<NotifyArgs> unused =
- mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, 0);
+ mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
+ InputReaderConfiguration::Change(0));
mapper.getAssociatedDisplayId();
},
})();
diff --git a/services/inputflinger/tests/fuzzers/FuzzContainer.h b/services/inputflinger/tests/fuzzers/FuzzContainer.h
index d42d11c..84ac0fd 100644
--- a/services/inputflinger/tests/fuzzers/FuzzContainer.h
+++ b/services/inputflinger/tests/fuzzers/FuzzContainer.h
@@ -59,7 +59,7 @@
void configureDevice() {
nsecs_t arbitraryTime = mFdp->ConsumeIntegral<nsecs_t>();
std::list<NotifyArgs> out;
- out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, 0);
+ out += mFuzzDevice->configure(arbitraryTime, mPolicyConfig, /*changes=*/{});
out += mFuzzDevice->reset(arbitraryTime);
for (const NotifyArgs& args : out) {
mFuzzListener.notify(args);
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index baece3c..9223287 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -82,7 +82,7 @@
return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags);
}
- void requestRefreshConfiguration(uint32_t changes) {
+ void requestRefreshConfiguration(ConfigurationChanges changes) {
reader->requestRefreshConfiguration(changes);
}
@@ -232,7 +232,8 @@
fdp->ConsumeIntegral<uint32_t>(), keyCodes, outFlags.data());
},
[&]() -> void {
- reader->requestRefreshConfiguration(fdp->ConsumeIntegral<uint32_t>());
+ reader->requestRefreshConfiguration(
+ InputReaderConfiguration::Change(fdp->ConsumeIntegral<uint32_t>()));
},
[&]() -> void {
reader->cancelVibrate(fdp->ConsumeIntegral<int32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 0a6327d..616e870 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -65,7 +65,8 @@
[&]() -> void {
std::list<NotifyArgs> unused =
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
- fdp->ConsumeIntegral<uint32_t>());
+ InputReaderConfiguration::Change(
+ fdp->ConsumeIntegral<uint32_t>()));
},
[&]() -> void {
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index fdf9f41..212462d 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -80,7 +80,8 @@
[&]() -> void {
std::list<NotifyArgs> unused =
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
- fdp->ConsumeIntegral<uint32_t>());
+ InputReaderConfiguration::Change(
+ fdp->ConsumeIntegral<uint32_t>()));
},
[&]() -> void {
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp
index b0fbe5d..0ea548c 100644
--- a/services/sensorservice/BatteryService.cpp
+++ b/services/sensorservice/BatteryService.cpp
@@ -30,8 +30,8 @@
namespace android {
// ---------------------------------------------------------------------------
-BatteryService::BatteryService() : mBatteryStatService(nullptr) {
-}
+BatteryService::BatteryService()
+ : mBatteryStatService(nullptr), mLastWakeupSensorEventReportedMs(0) {}
bool BatteryService::addSensor(uid_t uid, int handle) {
Mutex::Autolock _l(mActivationsLock);
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index fe72a69..0aa1bcb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -288,7 +288,7 @@
void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
int64_t procStateSeq __unused,
int32_t capability __unused) override {}
- void onUidProcAdjChanged(uid_t uid __unused) override {}
+ void onUidProcAdjChanged(uid_t uid __unused, int32_t adj __unused) override {}
void addOverrideUid(uid_t uid, bool active);
void removeOverrideUid(uid_t uid);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 608c53a..ccff1ec 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -19,6 +19,7 @@
#include <optional>
#include <ostream>
#include <unordered_set>
+#include "ui/LayerStack.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -140,7 +141,7 @@
ClientCompositionTargetSettings&) const = 0;
// Called after the layer is displayed to update the presentation fence
- virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>) = 0;
+ virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 12e063b..15e4577 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -49,7 +49,8 @@
std::optional<compositionengine::LayerFE::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
- MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>), (override));
+ MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
+ (override));
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index d64231f..793959c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1556,8 +1556,9 @@
releaseFence =
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
- layer->getLayerFE().onLayerDisplayed(
- ftl::yield<FenceResult>(std::move(releaseFence)).share());
+ layer->getLayerFE()
+ .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
+ outputState.layerFilter.layerStack);
}
// We've got a list of layers needing fences, that are disjoint with
@@ -1565,7 +1566,8 @@
// supply them with the present fence.
for (auto& weakLayer : mReleasedLayers) {
if (const auto layer = weakLayer.promote()) {
- layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share());
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
+ outputState.layerFilter.layerStack);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index aaf0f06..9e0e7b5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3220,16 +3220,19 @@
// are passed. This happens to work with the current implementation, but
// would not survive certain calls like Fence::merge() which would return a
// new instance.
- EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_))
- .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+ EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_, _))
+ .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack) {
EXPECT_EQ(FenceResult(layer1Fence), futureFenceResult.get());
});
- EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_))
- .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+ EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_, _))
+ .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack) {
EXPECT_EQ(FenceResult(layer2Fence), futureFenceResult.get());
});
- EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_))
- .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+ EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_, _))
+ .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack) {
EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
});
@@ -3285,16 +3288,19 @@
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Each released layer should be given the presentFence.
- EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_))
- .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+ EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_, _))
+ .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack) {
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
- EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_))
- .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+ EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_, _))
+ .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack) {
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
- EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_))
- .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult) {
+ EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_, _))
+ .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack) {
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index f28bfd4..f7049b9 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -329,14 +329,6 @@
}
}
-void AidlComposer::resetCommands(Display display) {
- mMutex.lock_shared();
- if (auto writer = getWriter(display)) {
- writer->get().reset();
- }
- mMutex.unlock_shared();
-}
-
Error AidlComposer::executeCommands(Display display) {
mMutex.lock_shared();
auto error = execute(display);
@@ -1054,9 +1046,8 @@
return Error::BAD_DISPLAY;
}
- const auto& commands = writer->get().getPendingCommands();
+ auto commands = writer->get().takePendingCommands();
if (commands.empty()) {
- writer->get().reset();
return Error::NONE;
}
@@ -1088,8 +1079,6 @@
}
}
- writer->get().reset();
-
return error;
}
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 8313c09..ce05b38 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -72,10 +72,6 @@
void registerCallback(HWC2::ComposerCallback& callback) override;
- // Reset all pending commands in the command buffer. Useful if you want to
- // skip a frame but have already queued some commands.
- void resetCommands(Display) override;
-
// Explicitly flush all pending commands in the command buffer.
Error executeCommands(Display) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c65c572..cf67795 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -110,10 +110,6 @@
virtual void registerCallback(HWC2::ComposerCallback& callback) = 0;
- // Reset all pending commands in the command buffer. Useful if you want to
- // skip a frame but have already queued some commands.
- virtual void resetCommands(Display) = 0;
-
// Explicitly flush all pending commands in the command buffer.
virtual Error executeCommands(Display) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 23de4fa..e0f6c45 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -293,10 +293,6 @@
}
}
-void HidlComposer::resetCommands(Display) {
- mWriter.reset();
-}
-
Error HidlComposer::executeCommands(Display) {
return execute();
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d04652b..0521acf 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -174,10 +174,6 @@
void registerCallback(HWC2::ComposerCallback& callback) override;
- // Reset all pending commands in the command buffer. Useful if you want to
- // skip a frame but have already queued some commands.
- void resetCommands(Display) override;
-
// Explicitly flush all pending commands in the command buffer.
Error executeCommands(Display) override;
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index 8f39e26..e55cd3e 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -35,21 +35,24 @@
const Rect& sourceCrop, ui::Size reqSize,
ui::Dataspace reqDataSpace,
bool useIdentityTransform,
+ bool hintForSeamlessTransition,
bool allowSecureLayers) {
if (auto display = displayWeak.promote()) {
// Using new to access a private constructor.
return std::unique_ptr<DisplayRenderArea>(
new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
- useIdentityTransform, allowSecureLayers));
+ useIdentityTransform, hintForSeamlessTransition,
+ allowSecureLayers));
}
return nullptr;
}
DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace reqDataSpace,
- bool useIdentityTransform, bool allowSecureLayers)
- : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, allowSecureLayers,
- applyDeviceOrientation(useIdentityTransform, *display)),
+ bool useIdentityTransform, bool hintForSeamlessTransition,
+ bool allowSecureLayers)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition,
+ allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
mDisplay(std::move(display)),
mSourceCrop(sourceCrop) {}
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index ce5410a..9a4981c 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -30,6 +30,7 @@
static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace,
bool useIdentityTransform,
+ bool hintForSeamlessTransition,
bool allowSecureLayers = true);
const ui::Transform& getTransform() const override;
@@ -39,7 +40,8 @@
private:
DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
+ ui::Dataspace, bool useIdentityTransform, bool hintForSeamlessTransition,
+ bool allowSecureLayers = true);
const sp<const DisplayDevice> mDisplay;
const Rect mSourceCrop;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
index 6af352c..97af445 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -23,6 +23,7 @@
namespace android::surfaceflinger {
std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+std::atomic<uint32_t> LayerCreationArgs::sInternalSequence{1};
uint32_t LayerCreationArgs::getInternalLayerId(uint32_t id) {
return id | INTERNAL_LAYER_PREFIX;
@@ -48,13 +49,11 @@
metadata.getInt32(gui::METADATA_OWNER_UID, static_cast<int32_t>(ownerUid)));
}
- if (id) {
+ if (internalLayer) {
+ sequence = id.value_or(getInternalLayerId(sInternalSequence++));
+ } else if (id) {
sequence = *id;
- if (internalLayer) {
- sequence = getInternalLayerId(*id);
- } else {
- sSequence = *id + 1;
- }
+ sSequence = *id + 1;
} else {
sequence = sSequence++;
if (sequence >= INTERNAL_LAYER_PREFIX) {
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 3a0fc6d..c26edb5 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -36,6 +36,7 @@
struct LayerCreationArgs {
static std::atomic<uint32_t> sSequence;
+ static std::atomic<uint32_t> sInternalSequence;
static uint32_t getInternalLayerId(uint32_t id);
static LayerCreationArgs fromOtherArgs(const LayerCreationArgs& other);
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 33d9dbe..cd9515c 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -38,10 +38,11 @@
RequestedLayerState& layer = *newLayer.get();
auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
if (!inserted) {
- LOG_ALWAYS_FATAL("Duplicate layer id %d found. Existing layer: %s", layer.id,
+ LOG_ALWAYS_FATAL("Duplicate layer id found. New layer: %s Existing layer: %s",
+ layer.getDebugString().c_str(),
it->second.owner.getDebugString().c_str());
}
-
+ mAddedLayers.push_back(newLayer.get());
layer.parentId = linkLayer(layer.parentId, layer.id);
layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
@@ -200,8 +201,10 @@
if (layer->what & layer_state_t::eBackgroundColorChanged) {
if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) {
- LayerCreationArgs backgroundLayerArgs(layer->id,
- /*internalLayer=*/true);
+ LayerCreationArgs
+ backgroundLayerArgs(LayerCreationArgs::getInternalLayerId(
+ LayerCreationArgs::sInternalSequence++),
+ /*internalLayer=*/true);
backgroundLayerArgs.parentId = layer->id;
backgroundLayerArgs.name = layer->name + "BackgroundColorLayer";
backgroundLayerArgs.flags = ISurfaceComposerClient::eFXSurfaceEffect;
@@ -258,23 +261,19 @@
}
void LayerLifecycleManager::commitChanges() {
- for (auto& layer : mLayers) {
- if (layer->changes.test(RequestedLayerState::Changes::Created)) {
- for (auto listener : mListeners) {
- listener->onLayerAdded(*layer);
- }
+ for (auto layer : mAddedLayers) {
+ for (auto& listener : mListeners) {
+ listener->onLayerAdded(*layer);
}
+ }
+ mAddedLayers.clear();
+
+ for (auto& layer : mLayers) {
layer->clearChanges();
}
for (auto& destroyedLayer : mDestroyedLayers) {
- if (destroyedLayer->changes.test(RequestedLayerState::Changes::Created)) {
- for (auto listener : mListeners) {
- listener->onLayerAdded(*destroyedLayer);
- }
- }
-
- for (auto listener : mListeners) {
+ for (auto& listener : mListeners) {
listener->onLayerDestroyed(*destroyedLayer);
}
}
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index f258678..f0d2c22 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -108,6 +108,9 @@
std::vector<std::unique_ptr<RequestedLayerState>> mLayers;
// Layers pending destruction. Layers will be destroyed once changes are committed.
std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers;
+ // Keeps track of all the layers that were added in order. Changes will be cleared once
+ // committed.
+ std::vector<RequestedLayerState*> mAddedLayers;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 1e931a7..a992584 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -27,7 +27,6 @@
LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
const LayerHierarchy::TraversalPath& path)
: path(path) {
- static uint32_t sUniqueSequenceId = 0;
// Provide a unique id for all snapshots.
// A front end layer can generate multiple snapshots if its mirrored.
// Additionally, if the layer is not reachable, we may choose to destroy
@@ -35,7 +34,12 @@
// change. The consumer shouldn't tie any lifetimes to this unique id but
// register a LayerLifecycleManager::ILifecycleListener or get a list of
// destroyed layers from LayerLifecycleManager.
- uniqueSequence = sUniqueSequenceId++;
+ if (path.isClone()) {
+ uniqueSequence =
+ LayerCreationArgs::getInternalLayerId(LayerCreationArgs::sInternalSequence++);
+ } else {
+ uniqueSequence = state.id;
+ }
sequence = static_cast<int32_t>(state.id);
name = state.name;
textureName = state.textureName;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index b397b82..23bb54c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -130,7 +130,8 @@
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
const uint32_t oldFlags = flags;
const half oldAlpha = color.a;
- const bool hadBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
+ const bool hadBuffer = externalTexture != nullptr;
+ const bool hadSideStream = sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
const bool hadBlur = hasBlur();
uint64_t clientChanges = what | layer_state_t::diff(clientState);
@@ -146,23 +147,32 @@
changes |= RequestedLayerState::Changes::Geometry;
}
}
- if (clientState.what &
- (layer_state_t::eBufferChanged | layer_state_t::eSidebandStreamChanged)) {
- const bool hasBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
- if (hadBufferOrSideStream != hasBufferOrSideStream) {
+ if (clientState.what & layer_state_t::eBufferChanged) {
+ externalTexture = resolvedComposerState.externalTexture;
+ barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
+ barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
+ // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
+
+ const bool hasBuffer = externalTexture != nullptr;
+ if (hasBuffer || hasBuffer != hadBuffer) {
+ changes |= RequestedLayerState::Changes::Buffer;
+ }
+
+ if (hasBuffer != hadBuffer) {
changes |= RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::VisibleRegion |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
}
}
- if (clientState.what & layer_state_t::eBufferChanged) {
- barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
- barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
- // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
- changes |= RequestedLayerState::Changes::Buffer;
- }
+
if (clientState.what & layer_state_t::eSidebandStreamChanged) {
changes |= RequestedLayerState::Changes::SidebandStream;
+ const bool hasSideStream = sidebandStream != nullptr;
+ if (hasSideStream != hadSideStream) {
+ changes |= RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
+ }
}
if (what & (layer_state_t::eAlphaChanged)) {
if (oldAlpha == 0 || color.a == 0) {
@@ -236,10 +246,6 @@
// TODO(b/238781169) handle callbacks
}
- if (clientState.what & layer_state_t::eBufferChanged) {
- externalTexture = resolvedComposerState.externalTexture;
- }
-
if (clientState.what & layer_state_t::ePositionChanged) {
requestedTransform.set(x, y);
}
@@ -465,6 +471,10 @@
return hasFrameUpdate() && sidebandStream.get();
}
+bool RequestedLayerState::willReleaseBufferOnLatch() const {
+ return changes.test(Changes::Buffer) && !externalTexture;
+}
+
void RequestedLayerState::clearChanges() {
what = 0;
changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index f15f023..0ef50bc 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -79,6 +79,7 @@
bool hasFrameUpdate() const;
bool hasReadyFrame() const;
bool hasSidebandStreamFrame() const;
+ bool willReleaseBufferOnLatch() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index a209cad..9cbe0bb 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -21,6 +21,7 @@
#include <cutils/trace.h>
#include <utils/Log.h>
+#include <utils/Trace.h>
#include "TransactionHandler.h"
@@ -73,12 +74,13 @@
void TransactionHandler::applyUnsignaledBufferTransaction(
std::vector<TransactionState>& transactions, TransactionFlushState& flushState) {
- // only apply an unsignaled buffer transaction if it's the first one
- if (!transactions.empty()) {
+ if (!flushState.queueWithUnsignaledBuffer) {
return;
}
- if (!flushState.queueWithUnsignaledBuffer) {
+ // only apply an unsignaled buffer transaction if it's the first one
+ if (!transactions.empty()) {
+ ATRACE_NAME("fence unsignaled");
return;
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9c232b1..3371ae2 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2552,7 +2552,10 @@
return outputLayer ? outputLayer->getState().visibleRegion : Region();
}
-void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId) {
+ mSnapshot->path.id = clonedFrom->getSequence();
+ mSnapshot->path.mirrorRootId = mirrorRootId;
+
cloneDrawingState(clonedFrom.get());
mClonedFrom = clonedFrom;
mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha;
@@ -2653,7 +2656,7 @@
}
sp<Layer> clonedChild = clonedLayersMap[child];
if (clonedChild == nullptr) {
- clonedChild = child->createClone();
+ clonedChild = child->createClone(mirrorRoot->getSequence());
clonedLayersMap[child] = clonedChild;
}
addChildToDrawing(clonedChild);
@@ -2795,7 +2798,8 @@
currentMaxAcquiredBufferCount);
}
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
+void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack layerStack) {
// If we are displayed on multiple displays in a single composition cycle then we would
// need to do careful tracking to enable the use of the mLastClientCompositionFence.
// For example we can only use it if all the displays are client comp, and we need
@@ -2825,8 +2829,7 @@
// transaction doesn't need a previous release fence.
sp<CallbackHandle> ch;
for (auto& handle : mDrawingState.callbackHandles) {
- if (handle->releasePreviousBuffer &&
- mDrawingState.releaseBufferEndpoint == handle->listener) {
+ if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) {
ch = handle;
break;
}
@@ -2842,6 +2845,7 @@
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
ch->name = mName;
}
+ mPreviouslyPresentedLayerStacks.push_back(layerStack);
}
void Layer::onSurfaceFrameCreated(
@@ -2880,8 +2884,7 @@
}
for (auto& handle : mDrawingState.callbackHandles) {
- if (handle->releasePreviousBuffer &&
- mDrawingState.releaseBufferEndpoint == handle->listener) {
+ if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) {
handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
break;
}
@@ -3018,14 +3021,22 @@
return true;
}
+void Layer::resetDrawingStateBufferInfo() {
+ mDrawingState.producerId = 0;
+ mDrawingState.frameNumber = 0;
+ mDrawingState.releaseBufferListener = nullptr;
+ mDrawingState.buffer = nullptr;
+ mDrawingState.acquireFence = sp<Fence>::make(-1);
+ mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+ mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
+ mDrawingState.releaseBufferEndpoint = nullptr;
+}
+
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
const FrameTimelineInfo& info) {
ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
- if (!buffer) {
- return false;
- }
const bool frameNumberChanged =
bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
@@ -3057,12 +3068,24 @@
mLastClientCompositionFence);
mLastClientCompositionFence = nullptr;
}
- } else {
+ } else if (buffer) {
// if we are latching a buffer for the first time then clear the mLastLatchTime since
// we don't want to incorrectly classify a frame if we miss the desired present time.
updateLastLatchTime(0);
}
+ mDrawingState.desiredPresentTime = desiredPresentTime;
+ mDrawingState.isAutoTimestamp = isAutoTimestamp;
+ mDrawingState.latchedVsyncId = info.vsyncId;
+ mDrawingState.modified = true;
+ if (!buffer) {
+ resetDrawingStateBufferInfo();
+ setTransactionFlags(eTransactionNeeded);
+ mDrawingState.bufferSurfaceFrameTX = nullptr;
+ setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+ return true;
+ }
+
mDrawingState.producerId = bufferData.producerId;
mDrawingState.barrierProducerId =
std::max(mDrawingState.producerId, mDrawingState.barrierProducerId);
@@ -3073,7 +3096,6 @@
// TODO(b/277265947) log and flush transaction trace when we detect out of order updates
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
- mDrawingState.clientCacheId = bufferData.cachedBuffer;
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
? bufferData.acquireFence
: Fence::NO_FENCE;
@@ -3086,15 +3108,11 @@
} else {
mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
}
- mDrawingState.latchedVsyncId = info.vsyncId;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
mOwnerUid, postTime, getGameMode());
- mDrawingState.desiredPresentTime = desiredPresentTime;
- mDrawingState.isAutoTimestamp = isAutoTimestamp;
if (mFlinger->mLegacyFrontEndEnabled) {
recordLayerHistoryBufferUpdate(getLayerProps());
@@ -3342,7 +3360,7 @@
const State& s(getDrawingState());
if (!s.buffer) {
- if (bgColorOnly) {
+ if (bgColorOnly || mBufferInfo.mBuffer) {
for (auto& handle : mDrawingState.callbackHandles) {
handle->latchTime = latchTime;
}
@@ -3389,12 +3407,19 @@
}
void Layer::gatherBufferInfo() {
- if (!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
+ mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
+ mPreviousReleaseBufferEndpoint = mBufferInfo.mReleaseBufferEndpoint;
+ if (!mDrawingState.buffer) {
+ mBufferInfo = {};
+ return;
+ }
+
+ if ((!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer))) {
decrementPendingBufferCount();
}
- mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
mBufferInfo.mBuffer = mDrawingState.buffer;
+ mBufferInfo.mReleaseBufferEndpoint = mDrawingState.releaseBufferEndpoint;
mBufferInfo.mFence = mDrawingState.acquireFence;
mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
mBufferInfo.mPixelFormat =
@@ -3469,11 +3494,11 @@
}
}
-sp<Layer> Layer::createClone() {
+sp<Layer> Layer::createClone(uint32_t mirrorRootId) {
LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
- layer->setInitialValuesForClone(sp<Layer>::fromExisting(this));
+ layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId);
return layer;
}
@@ -3924,6 +3949,10 @@
mBufferInfo.mFrameLatencyNeeded = false;
}
+bool Layer::willReleaseBufferOnLatch() const {
+ return !mDrawingState.buffer && mBufferInfo.mBuffer;
+}
+
bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
@@ -3947,9 +3976,6 @@
return false;
}
updateTexImage(latchTime, bgColorOnly);
- if (mDrawingState.buffer == nullptr) {
- return false;
- }
// Capture the old state of the layer for comparisons later
BufferInfo oldBufferInfo = mBufferInfo;
@@ -3958,6 +3984,18 @@
mCurrentFrameNumber = mDrawingState.frameNumber;
gatherBufferInfo();
+ if (mBufferInfo.mBuffer) {
+ // We latched a buffer that will be presented soon. Clear the previously presented layer
+ // stack list.
+ mPreviouslyPresentedLayerStacks.clear();
+ }
+
+ if (mDrawingState.buffer == nullptr) {
+ const bool bufferReleased = oldBufferInfo.mBuffer != nullptr;
+ recomputeVisibleRegions = bufferReleased;
+ return bufferReleased;
+ }
+
if (oldBufferInfo.mBuffer == nullptr) {
// the first time we receive a buffer, we need to trigger a
// geometry invalidation.
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8d7c362..2640c92 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -153,7 +153,6 @@
bool transformToDisplayInverse;
Region transparentRegionHint;
std::shared_ptr<renderengine::ExternalTexture> buffer;
- client_cache_t clientCacheId;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
HdrMetadata hdrMetadata;
@@ -249,7 +248,7 @@
// true if this layer is visible, false otherwise
virtual bool isVisible() const;
- virtual sp<Layer> createClone();
+ virtual sp<Layer> createClone(uint32_t mirrorRoot);
// Set a 2x2 transformation matrix on the layer. This transform
// will be applied after parent transforms, but before any final
@@ -455,6 +454,12 @@
bool bgColorOnly);
/*
+ * Returns true if the currently presented buffer will be released when this layer state
+ * is latched. This will return false if there is no buffer currently presented.
+ */
+ bool willReleaseBufferOnLatch() const;
+
+ /*
* Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
* This is used if the buffer is just latched and releases to free up the buffer
* and will not be shown on screen.
@@ -536,6 +541,7 @@
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
+ sp<IBinder> mReleaseBufferEndpoint;
bool mFrameLatencyNeeded{false};
float mDesiredHdrSdrRatio = 1.f;
@@ -547,7 +553,7 @@
const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
void onPreComposition(nsecs_t refreshStartTime);
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>);
+ void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack);
void setWasClientComposed(const sp<Fence>& fence) {
mLastClientCompositionFence = fence;
@@ -900,7 +906,10 @@
void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
mTransformHint = transformHint;
}
-
+ // Keeps track of the previously presented layer stacks. This is used to get
+ // the release fences from the correct displays when we release the last buffer
+ // from the layer.
+ std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
// Exposed so SurfaceFlinger can assert that it's held
const sp<SurfaceFlinger> mFlinger;
@@ -913,7 +922,7 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+ virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId);
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
@@ -1177,6 +1186,7 @@
half4 mBorderColor;
void setTransformHintLegacy(ui::Transform::RotationFlags);
+ void resetDrawingStateBufferInfo();
const uint32_t mTextureName;
@@ -1187,6 +1197,7 @@
std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
+ sp<IBinder> mPreviousReleaseBufferEndpoint;
uint64_t mPreviousReleasedFrameNumber = 0;
uint64_t mPreviousBarrierFrameNumber = 0;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b9c8b78..e713263 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -325,8 +325,9 @@
caster.shadow = state;
}
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
- mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult));
+void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack layerStack) {
+ mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
}
CompositionResult&& LayerFE::stealCompositionResult() {
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index c23bd31..d584fb7 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -29,7 +29,7 @@
// TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
// and remove this field.
nsecs_t refreshStartTime = 0;
- std::vector<ftl::SharedFuture<FenceResult>> releaseFences;
+ std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
sp<Fence> lastClientCompositionFence = nullptr;
};
@@ -40,7 +40,7 @@
// compositionengine::LayerFE overrides
const compositionengine::LayerFECompositionState* getCompositionState() const override;
bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override;
- void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
+ void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
const char* getDebugName() const override;
int32_t getSequence() const override;
bool hasRoundedCorners() const override;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 5d92485..3472d20 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -392,7 +392,11 @@
layerInfo->set_id(snapshot.uniqueSequence);
layerInfo->set_original_id(snapshot.sequence);
- layerInfo->set_name(requestedState.name);
+ if (!snapshot.path.isClone()) {
+ layerInfo->set_name(requestedState.name);
+ } else {
+ layerInfo->set_name(requestedState.name + "(Mirror)");
+ }
layerInfo->set_type("Layer");
LayerProtoHelper::writeToProto(requestedState.transparentRegion,
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 1b8ff28..f6b5afc 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -40,8 +40,9 @@
LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
bool allowSecureLayers, const ui::Transform& layerTransform,
- const Rect& layerBufferSize)
- : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, allowSecureLayers),
+ const Rect& layerBufferSize, bool hintForSeamlessTransition)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, hintForSeamlessTransition,
+ allowSecureLayers),
mLayer(std::move(layer)),
mLayerTransform(layerTransform),
mLayerBufferSize(layerBufferSize),
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index 9bb13b3..aa609ee 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -34,7 +34,8 @@
public:
LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers,
- const ui::Transform& layerTransform, const Rect& layerBufferSize);
+ const ui::Transform& layerTransform, const Rect& layerBufferSize,
+ bool hintForSeamlessTransition);
const ui::Transform& getTransform() const override;
bool isSecure() const override;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9a4261d..f1fd6db 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -355,6 +355,8 @@
if (isSetByHwc()) {
transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
layer_state_t::eLayerIsRefreshRateIndicator);
+ // Disable overlay layer caching when refresh rate is updated by the HWC.
+ transaction.setCachingHint(surface, gui::CachingHint::Disabled);
}
transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
return transaction;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 531d277..8f658d5 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -277,10 +277,12 @@
const Rect sampledBounds = sampleRegion.bounds();
constexpr bool kUseIdentityTransform = false;
+ constexpr bool kHintForSeamlessTransition = false;
SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
- ui::Dataspace::V0_SRGB, kUseIdentityTransform);
+ ui::Dataspace::V0_SRGB, kUseIdentityTransform,
+ kHintForSeamlessTransition);
});
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 910fce0..71b85bd 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -25,12 +25,14 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
- bool allowSecureLayers = false, RotationFlags rotation = ui::Transform::ROT_0)
+ bool hintForSeamlessTransition, bool allowSecureLayers = false,
+ RotationFlags rotation = ui::Transform::ROT_0)
: mAllowSecureLayers(allowSecureLayers),
mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
- mRotationFlags(rotation) {}
+ mRotationFlags(rotation),
+ mHintForSeamlessTransition(hintForSeamlessTransition) {}
static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
std::function<void(const LayerVector::Visitor&)> traverseLayers) {
@@ -90,6 +92,10 @@
// capture operation.
virtual sp<Layer> getParentLayer() const { return nullptr; }
+ // Returns whether the render result may be used for system animations that
+ // must preserve the exact colors of the display.
+ bool getHintForSeamlessTransition() const { return mHintForSeamlessTransition; }
+
protected:
const bool mAllowSecureLayers;
@@ -98,7 +104,7 @@
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const RotationFlags mRotationFlags;
- const Rect mLayerStackSpaceRect;
+ const bool mHintForSeamlessTransition;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 74665a7..af9acf3 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -42,6 +42,7 @@
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include <scheduler/VsyncConfig.h>
#include "DisplayHardware/DisplayMode.h"
#include "FrameTimeline.h"
#include "VSyncDispatch.h"
@@ -597,25 +598,34 @@
nsecs_t timestamp,
nsecs_t preferredExpectedPresentationTime,
nsecs_t preferredDeadlineTimestamp) const {
+ uint32_t currentIndex = 0;
// Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
- for (int64_t multiplier = -VsyncEventData::kFrameTimelinesLength + 1, currentIndex = 0;
- currentIndex < VsyncEventData::kFrameTimelinesLength; multiplier++) {
+ for (int64_t multiplier = -VsyncEventData::kFrameTimelinesCapacity + 1;
+ currentIndex < VsyncEventData::kFrameTimelinesCapacity; multiplier++) {
nsecs_t deadlineTimestamp = preferredDeadlineTimestamp + multiplier * frameInterval;
- // Valid possible frame timelines must have future values.
- if (deadlineTimestamp > timestamp) {
- if (multiplier == 0) {
- outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
- }
- nsecs_t expectedPresentationTime =
- preferredExpectedPresentationTime + multiplier * frameInterval;
- outVsyncEventData.frameTimelines[currentIndex] =
- {.vsyncId =
- generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
- .deadlineTimestamp = deadlineTimestamp,
- .expectedPresentationTime = expectedPresentationTime};
- currentIndex++;
+ // Valid possible frame timelines must have future values, so find a later frame timeline.
+ if (deadlineTimestamp <= timestamp) {
+ continue;
}
+
+ nsecs_t expectedPresentationTime =
+ preferredExpectedPresentationTime + multiplier * frameInterval;
+ if (expectedPresentationTime >= preferredExpectedPresentationTime +
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()) {
+ break;
+ }
+
+ if (multiplier == 0) {
+ outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
+ }
+
+ outVsyncEventData.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
+ .deadlineTimestamp = deadlineTimestamp,
+ .expectedPresentationTime = expectedPresentationTime};
+ currentIndex++;
}
+ outVsyncEventData.frameTimelinesLength = currentIndex;
}
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
@@ -692,17 +702,27 @@
}
void EventThread::onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule> schedule) {
+ // Hold onto the old registration until after releasing the mutex to avoid deadlock.
+ scheduler::VSyncCallbackRegistration oldRegistration =
+ onNewVsyncScheduleInternal(std::move(schedule));
+}
+
+scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal(
+ std::shared_ptr<scheduler::VsyncSchedule> schedule) {
std::lock_guard<std::mutex> lock(mMutex);
const bool reschedule = mVsyncRegistration.cancel() == scheduler::CancelResult::Cancelled;
mVsyncSchedule = std::move(schedule);
- mVsyncRegistration =
- scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
- createDispatchCallback(), mThreadName);
+ auto oldRegistration =
+ std::exchange(mVsyncRegistration,
+ scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
+ createDispatchCallback(),
+ mThreadName));
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
.earliestVsync = mLastVsyncCallbackTime.ns()});
}
+ return oldRegistration;
}
scheduler::VSyncDispatch::Callback EventThread::createDispatchCallback() {
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 30869e9..684745b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -174,7 +174,7 @@
size_t getEventThreadConnectionCount() override;
- void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override;
+ void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
private:
friend EventThreadTest;
@@ -201,6 +201,11 @@
scheduler::VSyncDispatch::Callback createDispatchCallback();
+ // Returns the old registration so it can be destructed outside the lock to
+ // avoid deadlock.
+ scheduler::VSyncCallbackRegistration onNewVsyncScheduleInternal(
+ std::shared_ptr<scheduler::VsyncSchedule>) EXCLUDES(mMutex);
+
const char* const mThreadName;
TracedOrdinal<int> mVsyncTracer;
TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index ed65bc6..67e1b9c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -22,6 +22,7 @@
#include <string>
#include <android-base/stringprintf.h>
+#include <ftl/algorithm.h>
#include <ftl/small_map.h>
#include <utils/Timers.h>
@@ -82,12 +83,18 @@
flushTime();
TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
- const auto zero = std::chrono::milliseconds::zero();
// Sum the times for modes that map to the same name, e.g. "60 Hz".
for (const auto& [fps, time] : mFpsTotalTimes) {
const auto string = to_string(fps);
- const auto total = std::as_const(totalTimes).get(string).value_or(std::cref(zero));
+ const auto total = std::as_const(totalTimes)
+ .get(string)
+ .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
+ using namespace std::chrono_literals;
+ return 0ms;
+ }))
+ .value();
+
totalTimes.emplace_or_replace(string, total.get() + time);
}
@@ -114,15 +121,18 @@
mPreviousRecordedTime = currentTime;
const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
- const auto zero = std::chrono::milliseconds::zero();
-
uint32_t fps = 0;
if (mCurrentPowerMode == PowerMode::ON) {
// Normal power mode is counted under different config modes.
const auto total = std::as_const(mFpsTotalTimes)
.get(mCurrentRefreshRate)
- .value_or(std::cref(zero));
+ .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
+ using namespace std::chrono_literals;
+ return 0ms;
+ }))
+ .value();
+
mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 8ddcfa1..9319543 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -50,8 +50,9 @@
#include "FrontEnd/LayerHandle.h"
#include "OneShotTimer.h"
#include "SurfaceFlingerProperties.h"
-#include "VSyncPredictor.h"
-#include "VSyncReactor.h"
+#include "VSyncTracker.h"
+#include "VsyncController.h"
+#include "VsyncSchedule.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -119,18 +120,17 @@
void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
RefreshRateSelectorPtr selectorPtr,
- std::shared_ptr<VsyncSchedule> vsyncSchedule) {
+ VsyncSchedulePtr schedulePtr) {
demotePacesetterDisplay();
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
std::scoped_lock lock(mDisplayLock);
- mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
- mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule));
+ mDisplays.emplace_or_replace(displayId, std::move(selectorPtr), std::move(schedulePtr));
pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
}
- applyNewVsyncScheduleIfNonNull(std::move(pacesetterVsyncSchedule));
+ applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
@@ -139,17 +139,16 @@
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
std::scoped_lock lock(mDisplayLock);
- mRefreshRateSelectors.erase(displayId);
- mVsyncSchedules.erase(displayId);
+ mDisplays.erase(displayId);
// Do not allow removing the final display. Code in the scheduler expects
// there to be at least one display. (This may be relaxed in the future with
// headless virtual display.)
- LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!");
+ LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!");
pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
}
- applyNewVsyncScheduleIfNonNull(std::move(pacesetterVsyncSchedule));
+ applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
void Scheduler::run() {
@@ -199,20 +198,27 @@
impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
return [this](uid_t uid) {
- const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().fps;
- const nsecs_t currentPeriod =
- getVsyncSchedule()->period().ns() ?: refreshRate.getPeriodNsecs();
+ const auto [refreshRate, period] = [this] {
+ std::scoped_lock lock(mDisplayLock);
+ const auto pacesetterOpt = pacesetterDisplayLocked();
+ LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+ const Display& pacesetter = *pacesetterOpt;
+ return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
+ pacesetter.schedulePtr->period());
+ }();
+
+ const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
const auto frameRate = getFrameRateOverride(uid);
if (!frameRate.has_value()) {
- return currentPeriod;
+ return currentPeriod.ns();
}
const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
if (divisor <= 1) {
- return currentPeriod;
+ return currentPeriod.ns();
}
- return currentPeriod * divisor;
+ return currentPeriod.ns() * divisor;
};
}
@@ -400,11 +406,13 @@
void Scheduler::enableHardwareVsync(PhysicalDisplayId id) {
auto schedule = getVsyncSchedule(id);
+ LOG_ALWAYS_FATAL_IF(!schedule);
schedule->enableHardwareVsync(mSchedulerCallback);
}
void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) {
auto schedule = getVsyncSchedule(id);
+ LOG_ALWAYS_FATAL_IF(!schedule);
schedule->disableHardwareVsync(mSchedulerCallback, disallow);
}
@@ -413,23 +421,27 @@
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- for (const auto& [id, _] : mRefreshRateSelectors) {
+ for (const auto& [id, _] : mDisplays) {
resyncToHardwareVsyncLocked(id, allowToEnable);
}
}
void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
std::optional<Fps> refreshRate) {
- auto schedule = getVsyncScheduleLocked(id);
- if (schedule->isHardwareVsyncAllowed(allowToEnable)) {
+ const auto displayOpt = mDisplays.get(id);
+ if (!displayOpt) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return;
+ }
+ const Display& display = *displayOpt;
+
+ if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
if (!refreshRate) {
- auto selectorPtr = mRefreshRateSelectors.get(id);
- LOG_ALWAYS_FATAL_IF(!selectorPtr);
- refreshRate = selectorPtr->get()->getActiveMode().modePtr->getFps();
+ refreshRate = display.selectorPtr->getActiveMode().modePtr->getFps();
}
if (refreshRate->isValid()) {
- schedule->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
- false /* force */);
+ display.schedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
+ false /* force */);
}
}
}
@@ -438,9 +450,13 @@
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- auto selectorPtr = mRefreshRateSelectors.get(id);
- LOG_ALWAYS_FATAL_IF(!selectorPtr);
- const auto mode = selectorPtr->get()->getActiveMode();
+ const auto displayOpt = mDisplays.get(id);
+ if (!displayOpt) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return;
+ }
+ const Display& display = *displayOpt;
+ const auto mode = display.selectorPtr->getActiveMode();
using fps_approx_ops::operator!=;
LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
@@ -451,7 +467,7 @@
ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
to_string(mode.modePtr->getFps()).c_str());
- getVsyncScheduleLocked(id)->getTracker().setRenderRate(renderFrameRate);
+ display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
}
void Scheduler::resync() {
@@ -470,12 +486,18 @@
const auto hwcVsyncPeriod = ftl::Optional(hwcVsyncPeriodIn).transform([](nsecs_t nanos) {
return Period::fromNs(nanos);
});
- return getVsyncSchedule(id)->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
- hwcVsyncPeriod);
+ auto schedule = getVsyncSchedule(id);
+ if (!schedule) {
+ ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+ return false;
+ }
+ return schedule->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
+ hwcVsyncPeriod);
}
void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
auto schedule = getVsyncSchedule(id);
+ LOG_ALWAYS_FATAL_IF(!schedule);
const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
if (needMoreSignals) {
schedule->enableHardwareVsync(mSchedulerCallback);
@@ -545,6 +567,7 @@
{
std::scoped_lock lock(mDisplayLock);
auto vsyncSchedule = getVsyncScheduleLocked(id);
+ LOG_ALWAYS_FATAL_IF(!vsyncSchedule);
vsyncSchedule->getController().setDisplayPowerMode(powerMode);
}
if (!isPacesetter) return;
@@ -558,22 +581,26 @@
mLayerHistory.clear();
}
-std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncSchedule(
- std::optional<PhysicalDisplayId> idOpt) const {
+auto Scheduler::getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt) const
+ -> ConstVsyncSchedulePtr {
std::scoped_lock lock(mDisplayLock);
return getVsyncScheduleLocked(idOpt);
}
-std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncScheduleLocked(
- std::optional<PhysicalDisplayId> idOpt) const {
+auto Scheduler::getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt) const
+ -> ConstVsyncSchedulePtr {
ftl::FakeGuard guard(kMainThreadContext);
+
if (!idOpt) {
LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!");
idOpt = mPacesetterDisplayId;
}
- auto scheduleOpt = mVsyncSchedules.get(*idOpt);
- LOG_ALWAYS_FATAL_IF(!scheduleOpt);
- return std::const_pointer_cast<const VsyncSchedule>(scheduleOpt->get());
+
+ const auto displayOpt = mDisplays.get(*idOpt);
+ if (!displayOpt) {
+ return nullptr;
+ }
+ return displayOpt->get().schedulePtr;
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -597,9 +624,9 @@
// need to update the VsyncController model anyway.
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
- constexpr bool disallow = false;
- for (auto& [_, schedule] : mVsyncSchedules) {
- schedule->disableHardwareVsync(mSchedulerCallback, disallow);
+ for (const auto& [_, display] : mDisplays) {
+ constexpr bool kDisallow = false;
+ display.schedulePtr->disableHardwareVsync(mSchedulerCallback, kDisallow);
}
}
@@ -664,12 +691,12 @@
to_string(*mPacesetterDisplayId).c_str());
getVsyncScheduleLocked()->dump(out);
}
- for (auto& [id, vsyncSchedule] : mVsyncSchedules) {
+ for (auto& [id, display] : mDisplays) {
if (id == mPacesetterDisplayId) {
continue;
}
base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str());
- vsyncSchedule->dump(out);
+ display.schedulePtr->dump(out);
}
}
@@ -693,49 +720,39 @@
pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterIdOpt);
}
- applyNewVsyncScheduleIfNonNull(std::move(pacesetterVsyncSchedule));
+ applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
std::optional<PhysicalDisplayId> pacesetterIdOpt) {
// TODO(b/241286431): Choose the pacesetter display.
- const auto oldPacesetterDisplayIdOpt = mPacesetterDisplayId;
- mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+ mPacesetterDisplayId = pacesetterIdOpt.value_or(mDisplays.begin()->first);
ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
- auto newVsyncSchedule = getVsyncScheduleLocked(*mPacesetterDisplayId);
- if (const auto pacesetterPtr = pacesetterSelectorPtrLocked()) {
- pacesetterPtr->setIdleTimerCallbacks(
+ std::shared_ptr<VsyncSchedule> newVsyncSchedulePtr;
+ if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
+ const Display& pacesetter = *pacesetterOpt;
+
+ pacesetter.selectorPtr->setIdleTimerCallbacks(
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
.kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
.onExpired =
[this] { kernelIdleTimerCallback(TimerState::Expired); }}});
- pacesetterPtr->startIdleTimer();
+ pacesetter.selectorPtr->startIdleTimer();
- // Track the new period, which may have changed due to switching to a
- // new pacesetter or due to a hotplug event. In the former case, this
- // is important so that VSYNC modulation does not get stuck in the
- // initiated state if a transition started on the old pacesetter.
- const Fps refreshRate = pacesetterPtr->getActiveMode().modePtr->getFps();
- newVsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
- true /* force */);
+ newVsyncSchedulePtr = pacesetter.schedulePtr;
+
+ const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getFps();
+ newVsyncSchedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
+ true /* force */);
}
- if (oldPacesetterDisplayIdOpt == mPacesetterDisplayId) {
- return nullptr;
- }
- return newVsyncSchedule;
+ return newVsyncSchedulePtr;
}
-void Scheduler::applyNewVsyncScheduleIfNonNull(
- std::shared_ptr<VsyncSchedule> pacesetterSchedulePtr) {
- if (!pacesetterSchedulePtr) {
- // The pacesetter has not changed, so there is no new VsyncSchedule to
- // apply.
- return;
- }
- onNewVsyncSchedule(pacesetterSchedulePtr->getDispatch());
+void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedule) {
+ onNewVsyncSchedule(vsyncSchedule->getDispatch());
std::vector<android::EventThread*> threads;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -745,7 +762,7 @@
}
}
for (auto* thread : threads) {
- thread->onNewVsyncSchedule(pacesetterSchedulePtr);
+ thread->onNewVsyncSchedule(vsyncSchedule);
}
}
@@ -830,81 +847,35 @@
using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
-
- // Tallies the score of a refresh rate across `displayCount` displays.
- struct RefreshRateTally {
- explicit RefreshRateTally(float score) : score(score) {}
-
- float score;
- size_t displayCount = 1;
- };
-
- // Chosen to exceed a typical number of refresh rates across displays.
- constexpr size_t kStaticCapacity = 8;
- ftl::SmallMap<Fps, RefreshRateTally, kStaticCapacity, FpsApproxEqual> refreshRateTallies;
-
const auto globalSignals = makeGlobalSignals();
+ Fps pacesetterFps;
- for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
+ for (const auto& [id, display] : mDisplays) {
auto rankedFrameRates =
- selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
-
- for (const auto& [frameRateMode, score] : rankedFrameRates.ranking) {
- const auto [it, inserted] = refreshRateTallies.try_emplace(frameRateMode.fps, score);
-
- if (!inserted) {
- auto& tally = it->second;
- tally.score += score;
- tally.displayCount++;
- }
+ display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
+ globalSignals);
+ if (id == *mPacesetterDisplayId) {
+ pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
}
-
perDisplayRanking.push_back(std::move(rankedFrameRates));
}
- auto maxScoreIt = refreshRateTallies.cbegin();
-
- // Find the first refresh rate common to all displays.
- while (maxScoreIt != refreshRateTallies.cend() &&
- maxScoreIt->second.displayCount != mRefreshRateSelectors.size()) {
- ++maxScoreIt;
- }
-
- if (maxScoreIt != refreshRateTallies.cend()) {
- // Choose the highest refresh rate common to all displays, if any.
- for (auto it = maxScoreIt + 1; it != refreshRateTallies.cend(); ++it) {
- const auto [fps, tally] = *it;
-
- if (tally.displayCount == mRefreshRateSelectors.size() &&
- tally.score > maxScoreIt->second.score) {
- maxScoreIt = it;
- }
- }
- }
-
- const std::optional<Fps> chosenFps = maxScoreIt != refreshRateTallies.cend()
- ? std::make_optional(maxScoreIt->first)
- : std::nullopt;
-
DisplayModeChoiceMap modeChoices;
-
using fps_approx_ops::operator==;
- for (auto& [ranking, signals] : perDisplayRanking) {
- if (!chosenFps) {
- const auto& [frameRateMode, _] = ranking.front();
- modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{frameRateMode, signals});
- continue;
- }
+ for (auto& [rankings, signals] : perDisplayRanking) {
+ const auto chosenFrameRateMode =
+ ftl::find_if(rankings,
+ [&](const auto& ranking) {
+ return ranking.frameRateMode.fps == pacesetterFps;
+ })
+ .transform([](const auto& scoredFrameRate) {
+ return scoredFrameRate.get().frameRateMode;
+ })
+ .value_or(rankings.front().frameRateMode);
- for (auto& [frameRateMode, _] : ranking) {
- if (frameRateMode.fps == *chosenFps) {
- modeChoices.try_emplace(frameRateMode.modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{frameRateMode, signals});
- break;
- }
- }
+ modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
+ DisplayModeChoice{chosenFrameRateMode, signals});
}
return modeChoices;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 720a1cb..f13c878 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -50,7 +50,6 @@
#include "RefreshRateSelector.h"
#include "Utils/Dumper.h"
#include "VsyncModulator.h"
-#include "VsyncSchedule.h"
namespace android::scheduler {
@@ -93,6 +92,8 @@
using GlobalSignals = RefreshRateSelector::GlobalSignals;
+class VsyncSchedule;
+
class Scheduler : android::impl::MessageQueue {
using Impl = android::impl::MessageQueue;
@@ -108,6 +109,9 @@
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
+ using ConstVsyncSchedulePtr = std::shared_ptr<const VsyncSchedule>;
+ using VsyncSchedulePtr = std::shared_ptr<VsyncSchedule>;
+
void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
EXCLUDES(mDisplayLock);
void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
@@ -192,8 +196,8 @@
// Sets the render rate for the scheduler to run at.
void setRenderRate(PhysicalDisplayId, Fps);
- void enableHardwareVsync(PhysicalDisplayId);
- void disableHardwareVsync(PhysicalDisplayId, bool disallow);
+ void enableHardwareVsync(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+ void disableHardwareVsync(PhysicalDisplayId, bool disallow) REQUIRES(kMainThreadContext);
// Resyncs the scheduler to hardware vsync.
// If allowToEnable is true, then hardware vsync will be turned on.
@@ -215,7 +219,8 @@
// otherwise.
bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
std::optional<nsecs_t> hwcVsyncPeriod);
- void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock);
+ void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
+ REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
@@ -236,12 +241,12 @@
void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode)
REQUIRES(kMainThreadContext);
- std::shared_ptr<const VsyncSchedule> getVsyncSchedule(
- std::optional<PhysicalDisplayId> idOpt = std::nullopt) const EXCLUDES(mDisplayLock);
- std::shared_ptr<VsyncSchedule> getVsyncSchedule(
- std::optional<PhysicalDisplayId> idOpt = std::nullopt) EXCLUDES(mDisplayLock) {
- return std::const_pointer_cast<VsyncSchedule>(
- static_cast<const Scheduler*>(this)->getVsyncSchedule(idOpt));
+ ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const
+ EXCLUDES(mDisplayLock);
+
+ VsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt = std::nullopt)
+ EXCLUDES(mDisplayLock) {
+ return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
}
// Returns true if a given vsync timestamp is considered valid vsync
@@ -329,20 +334,17 @@
// MessageQueue and EventThread need to use the new pacesetter's
// VsyncSchedule, and this must happen while mDisplayLock is *not* locked,
// or else we may deadlock with EventThread.
- // Returns the new pacesetter's VsyncSchedule, or null if the pacesetter is
- // unchanged.
std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(
std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
REQUIRES(kMainThreadContext, mDisplayLock);
- void applyNewVsyncScheduleIfNonNull(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
+ void applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
// Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
// the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
- void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr,
- std::shared_ptr<VsyncSchedule>) REQUIRES(kMainThreadContext)
- EXCLUDES(mDisplayLock);
+ void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
struct Policy;
@@ -421,16 +423,37 @@
// must lock for writes but not reads. See also mPolicyLock for locking order.
mutable std::mutex mDisplayLock;
- display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
- GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+ struct Display {
+ Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
+ : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
- // TODO (b/266715559): Store in the same map as mRefreshRateSelectors.
- display::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<VsyncSchedule>> mVsyncSchedules
- GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+ // Effectively const except in move constructor.
+ RefreshRateSelectorPtr selectorPtr;
+ VsyncSchedulePtr schedulePtr;
+ };
+
+ using DisplayRef = std::reference_wrapper<Display>;
+ using ConstDisplayRef = std::reference_wrapper<const Display>;
+
+ display::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays GUARDED_BY(mDisplayLock)
+ GUARDED_BY(kMainThreadContext);
ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
GUARDED_BY(kMainThreadContext);
+ ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
+ return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
+ [](const Display& display) { return std::ref(const_cast<Display&>(display)); });
+ }
+
+ ftl::Optional<ConstDisplayRef> pacesetterDisplayLocked() const REQUIRES(mDisplayLock) {
+ ftl::FakeGuard guard(kMainThreadContext);
+ return mPacesetterDisplayId.and_then([this](PhysicalDisplayId pacesetterId)
+ REQUIRES(mDisplayLock, kMainThreadContext) {
+ return mDisplays.get(pacesetterId);
+ });
+ }
+
RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
std::scoped_lock lock(mDisplayLock);
return pacesetterSelectorPtrLocked();
@@ -438,19 +461,17 @@
RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
ftl::FakeGuard guard(kMainThreadContext);
- const RefreshRateSelectorPtr noPacesetter;
- return mPacesetterDisplayId
- .and_then([this](PhysicalDisplayId pacesetterId)
- REQUIRES(mDisplayLock, kMainThreadContext) {
- return mRefreshRateSelectors.get(pacesetterId);
- })
- .value_or(std::cref(noPacesetter));
+ return pacesetterDisplayLocked()
+ .transform([](const Display& display) { return display.selectorPtr; })
+ .or_else([] { return std::optional<RefreshRateSelectorPtr>(nullptr); })
+ .value();
}
- std::shared_ptr<const VsyncSchedule> getVsyncScheduleLocked(
- std::optional<PhysicalDisplayId> idOpt = std::nullopt) const REQUIRES(mDisplayLock);
- std::shared_ptr<VsyncSchedule> getVsyncScheduleLocked(
- std::optional<PhysicalDisplayId> idOpt = std::nullopt) REQUIRES(mDisplayLock) {
+ ConstVsyncSchedulePtr getVsyncScheduleLocked(
+ std::optional<PhysicalDisplayId> = std::nullopt) const REQUIRES(mDisplayLock);
+
+ VsyncSchedulePtr getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt = std::nullopt)
+ REQUIRES(mDisplayLock) {
return std::const_pointer_cast<VsyncSchedule>(
static_cast<const Scheduler*>(this)->getVsyncScheduleLocked(idOpt));
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 77875e3..c3a952f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -155,10 +155,6 @@
VSyncDispatch& operator=(const VSyncDispatch&) = delete;
};
-/*
- * Helper class to operate on registered callbacks. It is up to user of the class to ensure
- * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
- */
class VSyncCallbackRegistration {
public:
VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch>, VSyncDispatch::Callback,
@@ -178,9 +174,10 @@
CancelResult cancel();
private:
+ friend class VSyncCallbackRegistrationTest;
+
std::shared_ptr<VSyncDispatch> mDispatch;
- VSyncDispatch::CallbackToken mToken;
- bool mValidToken;
+ std::optional<VSyncDispatch::CallbackToken> mToken;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 26389eb..1f922f1 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -21,12 +21,16 @@
#include <android-base/stringprintf.h>
#include <ftl/concat.h>
#include <utils/Trace.h>
+#include <log/log_main.h>
#include <scheduler/TimeKeeper.h>
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"
+#undef LOG_TAG
+#define LOG_TAG "VSyncDispatch"
+
namespace android::scheduler {
using base::StringAppendF;
@@ -225,6 +229,10 @@
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard lock(mMutex);
cancelTimer();
+ for (auto& [_, entry] : mCallbacks) {
+ ALOGE("Forgot to unregister a callback on VSyncDispatch!");
+ entry->ensureNotRunning();
+ }
}
void VSyncDispatchTimerQueue::cancelTimer() {
@@ -438,47 +446,44 @@
VSyncDispatch::Callback callback,
std::string callbackName)
: mDispatch(std::move(dispatch)),
- mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))),
- mValidToken(true) {}
+ mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))) {}
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
- : mDispatch(std::move(other.mDispatch)),
- mToken(std::move(other.mToken)),
- mValidToken(std::move(other.mValidToken)) {
- other.mValidToken = false;
-}
+ : mDispatch(std::move(other.mDispatch)), mToken(std::exchange(other.mToken, std::nullopt)) {}
VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+ if (this == &other) return *this;
+ if (mToken) {
+ mDispatch->unregisterCallback(*mToken);
+ }
mDispatch = std::move(other.mDispatch);
- mToken = std::move(other.mToken);
- mValidToken = std::move(other.mValidToken);
- other.mValidToken = false;
+ mToken = std::exchange(other.mToken, std::nullopt);
return *this;
}
VSyncCallbackRegistration::~VSyncCallbackRegistration() {
- if (mValidToken) mDispatch->unregisterCallback(mToken);
+ if (mToken) mDispatch->unregisterCallback(*mToken);
}
ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
- if (!mValidToken) {
+ if (!mToken) {
return std::nullopt;
}
- return mDispatch->schedule(mToken, scheduleTiming);
+ return mDispatch->schedule(*mToken, scheduleTiming);
}
ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
- if (!mValidToken) {
+ if (!mToken) {
return std::nullopt;
}
- return mDispatch->update(mToken, scheduleTiming);
+ return mDispatch->update(*mToken, scheduleTiming);
}
CancelResult VSyncCallbackRegistration::cancel() {
- if (!mValidToken) {
+ if (!mToken) {
return CancelResult::Error;
}
- return mDispatch->cancel(mToken);
+ return mDispatch->cancel(*mToken);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
index 3b1985f..47d95a8 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
@@ -22,6 +22,8 @@
namespace android::scheduler {
+using namespace std::chrono_literals;
+
// Phase offsets and work durations for SF and app deadlines from VSYNC.
struct VsyncConfig {
nsecs_t sfOffset;
@@ -35,6 +37,10 @@
}
bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+
+ // The duration for which SF can delay a frame if it is considered early based on the
+ // VsyncConfig::appWorkDuration.
+ static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
};
struct VsyncConfigSet {
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index a1d5cd7..09dac23 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -36,6 +36,7 @@
output->setLayerFilter({args.layerStack});
output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits);
+ output->editState().clientTargetBrightness = args.targetBrightness;
output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>(
compositionengine::DisplayColorProfileCreationArgsBuilder()
@@ -75,7 +76,6 @@
auto clientCompositionDisplay =
compositionengine::impl::Output::generateClientCompositionDisplaySettings();
clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
- clientCompositionDisplay.targetLuminanceNits = -1;
return clientCompositionDisplay;
}
@@ -94,6 +94,16 @@
}
}
+ if (outputDataspace == ui::Dataspace::BT2020_HLG) {
+ for (auto& layer : clientCompositionLayers) {
+ auto transfer = layer.sourceDataspace & ui::Dataspace::TRANSFER_MASK;
+ if (transfer != static_cast<int32_t>(ui::Dataspace::TRANSFER_HLG) &&
+ transfer != static_cast<int32_t>(ui::Dataspace::TRANSFER_ST2084)) {
+ layer.whitePointNits *= (1000.0f / 203.0f);
+ }
+ }
+ }
+
Rect sourceCrop = mRenderArea.getSourceCrop();
compositionengine::LayerFE::LayerSettings fillLayer;
fillLayer.source.buffer.buffer = nullptr;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 4e5a0cc..3c307b0 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -33,6 +33,8 @@
std::shared_ptr<renderengine::ExternalTexture> buffer;
float sdrWhitePointNits;
float displayBrightnessNits;
+ // Counterintuitively, when targetBrightness > 1.0 then dim the scene.
+ float targetBrightness;
bool regionSampling;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 31bf7ef..3c55caf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1146,17 +1146,26 @@
std::optional<PhysicalDisplayId> displayIdOpt;
{
Mutex::Autolock lock(mStateLock);
- displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+ if (displayToken) {
+ displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayIdOpt) {
+ ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ } else {
+ // TODO (b/277364366): Clients should be updated to pass in the display they
+ // want, rather than us picking an arbitrary one (the active display, in this
+ // case).
+ displayIdOpt = mActiveDisplayId;
+ }
}
- // TODO (b/277364366): Clients should be updated to pass in the display they
- // want, rather than us picking an arbitrary one (the pacesetter, in this
- // case).
- if (displayToken && !displayIdOpt) {
- ALOGE("%s: Invalid physical display token %p", __func__, displayToken.get());
+ const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
+ if (!schedule) {
+ ALOGE("%s: Missing VSYNC schedule for display %s!", __func__,
+ to_string(*displayIdOpt).c_str());
return NAME_NOT_FOUND;
}
- const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns();
outStats->vsyncPeriod = schedule->period().ns();
return NO_ERROR;
@@ -2136,7 +2145,9 @@
static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
{
ftl::FakeGuard guard(kMainThreadContext);
- mScheduler->getVsyncSchedule(id)->setPendingHardwareVsyncState(enabled);
+ if (auto schedule = mScheduler->getVsyncSchedule(id)) {
+ schedule->setPendingHardwareVsyncState(enabled);
+ }
}
ATRACE_FORMAT("%s (%d) for %" PRIu64 " (main thread)", whence, enabled, id.value);
@@ -2146,7 +2157,14 @@
}));
}
-auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod)
+bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
+ const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
+ return isThreeVsyncsAhead ||
+ getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() !=
+ Fence::SIGNAL_TIME_PENDING;
+}
+
+auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const
-> const FenceTimePtr& {
const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod;
const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
@@ -2300,16 +2318,20 @@
sp<Layer> bgColorLayer = getFactory().createEffectLayer(
LayerCreationArgs(this, nullptr, layer->name,
ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
- std::make_optional(layer->parentId), true));
+ std::make_optional(layer->id), true));
mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
}
- if (!layer->hasReadyFrame()) continue;
+ const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
+ if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) continue;
auto it = mLegacyLayers.find(layer->id);
LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
layer->getDebugString().c_str());
const bool bgColorOnly =
!layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
+ if (willReleaseBufferOnLatch) {
+ mLayersWithBuffersRemoved.emplace(it->second);
+ }
it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
mLayersWithQueuedFrames.emplace(it->second);
}
@@ -2522,7 +2544,7 @@
}
updateCursorAsync();
- updateInputFlinger();
+ updateInputFlinger(vsyncId);
if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
// This will block and tracing should only be enabled for debugging.
@@ -2610,21 +2632,14 @@
refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
- const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule()->period();
- const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
-
const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
- const bool threeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
- // We should wait for the earliest present time if HWC doesn't support ExpectedPresentTime,
- // and the next vsync is not already taken by the previous frame.
- const bool waitForEarliestPresent =
- !getHwComposer().getComposer()->isSupported(
- Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
- (threeVsyncsAhead ||
- mPreviousPresentFences[0].fenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING);
+ if (!getHwComposer().getComposer()->isSupported(
+ Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+ wouldPresentEarly(frameTime, vsyncPeriod)) {
+ const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod;
+ const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
- if (waitForEarliestPresent) {
refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
}
@@ -2644,10 +2659,10 @@
for (auto [layer, layerFE] : layers) {
CompositionResult compositionResult{layerFE->stealCompositionResult()};
layer->onPreComposition(compositionResult.refreshStartTime);
- for (auto releaseFence : compositionResult.releaseFences) {
+ for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
Layer* clonedFrom = layer->getClonedFrom().get();
auto owningLayer = clonedFrom ? clonedFrom : layer;
- owningLayer->onLayerDisplayed(releaseFence);
+ owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack);
}
if (compositionResult.lastClientCompositionFence) {
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
@@ -2658,9 +2673,10 @@
// Send a power hint hint after presentation is finished
if (mPowerHintSessionEnabled) {
- mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(mPreviousPresentFences[0]
- .fenceTime->getSignalTime()),
- TimePoint::now());
+ const nsecs_t pastPresentTime =
+ getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime();
+
+ mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(pastPresentTime), TimePoint::now());
mPowerAdvisor->reportActualWorkDuration();
}
@@ -2858,6 +2874,32 @@
const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
presentLatency.ns());
+ display::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
+ {
+ if (!mLayersWithBuffersRemoved.empty() || mNumTrustedPresentationListeners > 0) {
+ Mutex::Autolock lock(mStateLock);
+ for (const auto& [token, display] : mDisplays) {
+ layerStackToDisplay.emplace_or_replace(display->getLayerStack(), display.get());
+ }
+ }
+ }
+
+ for (auto layer : mLayersWithBuffersRemoved) {
+ std::vector<ui::LayerStack> previouslyPresentedLayerStacks =
+ std::move(layer->mPreviouslyPresentedLayerStacks);
+ layer->mPreviouslyPresentedLayerStacks.clear();
+ for (auto layerStack : previouslyPresentedLayerStacks) {
+ auto optDisplay = layerStackToDisplay.get(layerStack);
+ if (optDisplay && !optDisplay->get()->isVirtual()) {
+ auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+ ui::INVALID_LAYER_STACK);
+ }
+ }
+ layer->releasePendingBuffer(presentTime.ns());
+ }
+ mLayersWithBuffersRemoved.clear();
+
for (const auto& layer: mLayersWithQueuedFrames) {
layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
compositorTiming);
@@ -2984,14 +3026,6 @@
}
if (mNumTrustedPresentationListeners > 0) {
- display::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
- {
- Mutex::Autolock lock(mStateLock);
- for (const auto& [token, display] : mDisplays) {
- layerStackToDisplay.emplace_or_replace(display->getLayerStack(), display.get());
- }
- }
-
// We avoid any reverse traversal upwards so this shouldn't be too expensive
traverseLegacyLayers([&](Layer* layer) {
if (!layer->hasTrustedPresentationListener()) {
@@ -3698,11 +3732,11 @@
doCommitTransactions();
}
-void SurfaceFlinger::updateInputFlinger() {
- ATRACE_CALL();
- if (!mInputFlinger) {
+void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId) {
+ if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
+ ATRACE_CALL();
std::vector<WindowInfo> windowInfos;
std::vector<DisplayInfo> displayInfos;
@@ -3710,21 +3744,21 @@
if (mUpdateInputInfo) {
mUpdateInputInfo = false;
updateWindowInfo = true;
+ mLastInputFlingerUpdateVsyncId = vsyncId;
+ mLastInputFlingerUpdateTimestamp = systemTime();
buildWindowInfos(windowInfos, displayInfos);
- } else if (mInputWindowCommands.empty()) {
- return;
}
- std::unordered_set<Layer*> visibleLayers;
- mDrawingState.traverse([&visibleLayers](Layer* layer) {
- if (layer->isVisibleForInput()) {
- visibleLayers.insert(layer);
+ std::unordered_set<int32_t> visibleWindowIds;
+ for (WindowInfo& windowInfo : windowInfos) {
+ if (!windowInfo.inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
+ visibleWindowIds.insert(windowInfo.id);
}
- });
- bool visibleLayersChanged = false;
- if (visibleLayers != mVisibleLayers) {
- visibleLayersChanged = true;
- mVisibleLayers = std::move(visibleLayers);
+ }
+ bool visibleWindowsChanged = false;
+ if (visibleWindowIds != mVisibleWindowIds) {
+ visibleWindowsChanged = true;
+ mVisibleWindowIds = std::move(visibleWindowIds);
}
BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
@@ -3733,15 +3767,17 @@
inputWindowCommands =
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
- visibleLayersChanged]() {
+ visibleWindowsChanged]() {
ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
mWindowInfosListenerInvoker
->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
std::move(
inputWindowCommands.windowInfosReportedListeners),
- /* forceImmediateCall= */ visibleLayersChanged ||
- !inputWindowCommands.focusRequests.empty());
+ /* forceImmediateCall= */ visibleWindowsChanged ||
+ !inputWindowCommands.focusRequests.empty(),
+ mLastInputFlingerUpdateVsyncId,
+ mLastInputFlingerUpdateTimestamp);
} else {
// If there are listeners but no changes to input windows, call the listeners
// immediately.
@@ -4039,7 +4075,7 @@
}
}
- if (layer->hasReadyFrame()) {
+ if (layer->hasReadyFrame() || layer->willReleaseBufferOnLatch()) {
frameQueued = true;
mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
} else {
@@ -4070,6 +4106,9 @@
Mutex::Autolock lock(mStateLock);
for (const auto& layer : mLayersWithQueuedFrames) {
+ if (layer->willReleaseBufferOnLatch()) {
+ mLayersWithBuffersRemoved.emplace(layer);
+ }
if (layer->latchBuffer(visibleRegions, latchTime)) {
mLayersPendingRefresh.push_back(layer);
newDataLatched = true;
@@ -4203,20 +4242,20 @@
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
const auto& transaction = *flushState.transaction;
- ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64,
- transaction.frameTimelineInfo.vsyncId);
TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
// Do not present if the desiredPresentTime has not passed unless it is more than
// one second in the future. We ignore timestamps more than 1 second in the future
// for stability reasons.
if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
desiredPresentTime < mExpectedPresentTime + 1s) {
- ATRACE_NAME("not current");
+ ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
+ desiredPresentTime, mExpectedPresentTime);
return TransactionReadiness::NotReady;
}
if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
- ATRACE_NAME("!isVsyncValid");
+ ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
+ mExpectedPresentTime, transaction.originUid);
return TransactionReadiness::NotReady;
}
@@ -4224,7 +4263,8 @@
// expected present time of this transaction.
if (transaction.isAutoTimestamp &&
frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
- ATRACE_NAME("frameIsEarly");
+ ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
+ transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime);
return TransactionReadiness::NotReady;
}
return TransactionReadiness::Ready;
@@ -4254,7 +4294,9 @@
s.bufferData->acquireFence);
// Delete the entire state at this point and not just release the buffer because
// everything associated with the Layer in this Transaction is now out of date.
- ATRACE_NAME("DeleteStaleBuffer");
+ ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
+ layer->getDebugName(), layer->getDrawingState().barrierProducerId,
+ s.bufferData->producerId);
return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
}
@@ -4264,7 +4306,10 @@
((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
s.bufferData->barrierFrameNumber));
if (!willApplyBarrierFrame) {
- ATRACE_NAME("NotReadyBarrier");
+ ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
+ layer->getDebugName(),
+ layer->getDrawingState().barrierFrameNumber,
+ s.bufferData->barrierFrameNumber);
ready = TransactionReadiness::NotReadyBarrier;
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
@@ -4276,7 +4321,7 @@
const bool hasPendingBuffer =
flushState.bufferLayersReadyToPresent.contains(s.surface.get());
if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
- ATRACE_NAME("hasPendingBuffer");
+ ATRACE_FORMAT("hasPendingBuffer %s", layer->getDebugName());
ready = TransactionReadiness::NotReady;
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
@@ -4293,9 +4338,9 @@
const bool allowLatchUnsignaled =
shouldLatchUnsignaled(layer, s, transaction.states.size(),
flushState.firstTransaction);
- ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
- allowLatchUnsignaled ? "true" : "false");
if (allowLatchUnsignaled) {
+ ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
+ layer->getDebugName());
ready = TransactionReadiness::NotReadyUnsignaled;
} else {
ready = TransactionReadiness::NotReady;
@@ -4308,12 +4353,12 @@
"Buffer processing hung up due to stuck "
"fence. Indicates GPU hang");
}
+ ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
}
return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
});
- ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
return ready;
}
@@ -4367,10 +4412,8 @@
const auto predictedPresentTime = TimePoint::fromNs(prediction->presentTime);
- // The duration for which SF can delay a frame if it is considered early based on the
- // VsyncConfig::appWorkDuration.
- if (constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
- std::chrono::abs(predictedPresentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold) {
+ if (std::chrono::abs(predictedPresentTime - expectedPresentTime) >=
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold) {
return false;
}
@@ -5063,7 +5106,8 @@
}
if (layer->setTransactionCompletedListeners(callbackHandles,
- layer->willPresentCurrentTransaction())) {
+ layer->willPresentCurrentTransaction() ||
+ layer->willReleaseBufferOnLatch())) {
flags |= eTraversalNeeded;
}
@@ -5177,8 +5221,9 @@
}
const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence());
- bool willPresentCurrentTransaction =
- requestedLayerState && requestedLayerState->hasReadyFrame();
+ bool willPresentCurrentTransaction = requestedLayerState &&
+ (requestedLayerState->hasReadyFrame() ||
+ requestedLayerState->willReleaseBufferOnLatch());
if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
flags |= eTraversalNeeded;
@@ -5214,7 +5259,7 @@
return result;
}
- mirrorLayer->setClonedChild(mirrorFrom->createClone());
+ mirrorLayer->setClonedChild(mirrorFrom->createClone(mirrorLayer->getSequence()));
}
outResult.layerId = mirrorLayer->sequence;
@@ -5841,8 +5886,8 @@
return layersProto;
}
- return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos, {},
- traceFlags)
+ return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
+ mLegacyLayers, traceFlags)
.generate(mLayerHierarchyBuilder.getHierarchy());
}
@@ -6076,6 +6121,29 @@
result.append(mTimeStats->miniDump());
result.append("\n");
+
+ result.append("Window Infos:\n");
+ StringAppendF(&result, " input flinger update vsync id: %" PRId64 "\n",
+ mLastInputFlingerUpdateVsyncId.value);
+ StringAppendF(&result, " input flinger update timestamp (ns): %" PRId64 "\n",
+ mLastInputFlingerUpdateTimestamp);
+ result.append("\n");
+
+ if (int64_t unsentVsyncId = mWindowInfosListenerInvoker->getUnsentMessageVsyncId().value;
+ unsentVsyncId != -1) {
+ StringAppendF(&result, " unsent input flinger update vsync id: %" PRId64 "\n",
+ unsentVsyncId);
+ StringAppendF(&result, " unsent input flinger update timestamp (ns): %" PRId64 "\n",
+ mWindowInfosListenerInvoker->getUnsentMessageTimestamp());
+ result.append("\n");
+ }
+
+ if (uint32_t pendingMessages = mWindowInfosListenerInvoker->getPendingMessageCount();
+ pendingMessages != 0) {
+ StringAppendF(&result, " pending input flinger calls: %" PRIu32 "\n",
+ mWindowInfosListenerInvoker->getPendingMessageCount());
+ result.append("\n");
+ }
}
mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
@@ -6886,6 +6954,32 @@
return NO_ERROR;
}
+namespace {
+
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayDevice* display,
+ bool capturingHdrLayers, bool hintForSeamlessTransition) {
+ if (requestedDataspace != ui::Dataspace::UNKNOWN || display == nullptr) {
+ return requestedDataspace;
+ }
+
+ const auto& state = display->getCompositionDisplay()->getState();
+
+ const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
+
+ if (capturingHdrLayers && !hintForSeamlessTransition) {
+ // For now since we only support 8-bit screenshots, just use HLG and
+ // assume that 1.0 >= display max luminance. This isn't quite as future
+ // proof as PQ is, but is good enough.
+ // Consider using PQ once we support 16-bit screenshots and we're able
+ // to consistently supply metadata to image encoders.
+ return ui::Dataspace::BT2020_HLG;
+ }
+
+ return dataspaceForColorMode;
+}
+
+} // namespace
+
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -6901,7 +6995,6 @@
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
std::unordered_set<uint32_t> excludeLayerIds;
- ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
@@ -6923,17 +7016,12 @@
return NAME_NOT_FOUND;
}
}
-
- // Allow the caller to specify a dataspace regardless of the display's color mode, e.g. if
- // it wants sRGB regardless of the display's wide color mode.
- dataspace = args.dataspace == ui::Dataspace::UNKNOWN
- ? ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode)
- : args.dataspace;
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
- args.useIdentityTransform, args.captureSecureLayers);
+ return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize,
+ ui::Dataspace::UNKNOWN, args.useIdentityTransform,
+ args.hintForSeamlessTransition, args.captureSecureLayers);
});
GetLayerSnapshotsFunction getLayerSnapshots;
@@ -6959,7 +7047,6 @@
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
ui::Size size;
- ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
@@ -6971,12 +7058,12 @@
displayWeak = display;
layerStack = display->getLayerStack();
size = display->getLayerStackSpaceRect().getSize();
- dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
}
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
+ return DisplayRenderArea::create(displayWeak, Rect(), size, ui::Dataspace::UNKNOWN,
false /* useIdentityTransform */,
+ false /* hintForSeamlessTransition */,
false /* captureSecureLayers */);
});
@@ -7018,7 +7105,7 @@
sp<Layer> parent;
Rect crop(args.sourceCrop);
std::unordered_set<uint32_t> excludeLayerIds;
- ui::Dataspace dataspace;
+ ui::Dataspace dataspace = args.dataspace;
// Call this before holding mStateLock to avoid any deadlocking.
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
@@ -7065,12 +7152,6 @@
return NAME_NOT_FOUND;
}
}
-
- // The dataspace is depended on the color mode of display, that could use non-native mode
- // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
- // and failed if display is not in native mode. This provide a way to force using native
- // colors when capture.
- dataspace = args.dataspace;
} // mStateLock
// really small crop or frameScale
@@ -7099,7 +7180,8 @@
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
childrenOnly, args.captureSecureLayers,
- layerTransform, layerBufferSize);
+ layerTransform, layerBufferSize,
+ args.hintForSeamlessTransition);
});
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
@@ -7285,29 +7367,48 @@
return ftl::yield<FenceResult>(base::unexpected(PERMISSION_DENIED)).share();
}
- captureResults.buffer = buffer->getBuffer();
- auto dataspace = renderArea->getReqDataSpace();
+ auto capturedBuffer = buffer;
+
+ auto requestedDataspace = renderArea->getReqDataSpace();
auto parent = renderArea->getParentLayer();
auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
- if (dataspace == ui::Dataspace::UNKNOWN && parent) {
+ captureResults.capturedDataspace = requestedDataspace;
+
+ {
Mutex::Autolock lock(mStateLock);
- auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
- return display.getLayerStack() == layerStack;
- });
- if (!display) {
- // If the layer is not on a display, use the dataspace for the default display.
- display = getDefaultDisplayDeviceLocked();
+ const DisplayDevice* display = nullptr;
+ if (parent) {
+ display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+ return display.getLayerStack() == layerStack;
+ }).get();
}
- dataspace = ui::pickDataspaceFor(display->getCompositionDisplay()->getState().colorMode);
- renderIntent = display->getCompositionDisplay()->getState().renderIntent;
- sdrWhitePointNits = display->getCompositionDisplay()->getState().sdrWhitePointNits;
- displayBrightnessNits = display->getCompositionDisplay()->getState().displayBrightnessNits;
+ if (display == nullptr) {
+ display = renderArea->getDisplayDevice().get();
+ }
+
+ if (display == nullptr) {
+ display = getDefaultDisplayDeviceLocked().get();
+ }
+
+ if (display != nullptr) {
+ const auto& state = display->getCompositionDisplay()->getState();
+ captureResults.capturedDataspace =
+ pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers,
+ renderArea->getHintForSeamlessTransition());
+ sdrWhitePointNits = state.sdrWhitePointNits;
+ displayBrightnessNits = state.displayBrightnessNits;
+
+ if (requestedDataspace == ui::Dataspace::UNKNOWN) {
+ renderIntent = state.renderIntent;
+ }
+ }
}
- captureResults.capturedDataspace = dataspace;
+
+ captureResults.buffer = capturedBuffer->getBuffer();
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
if (!layers.empty()) {
@@ -7324,9 +7425,9 @@
return layerFEs;
};
- auto present = [this, buffer = std::move(buffer), dataspace, sdrWhitePointNits,
- displayBrightnessNits, grayscale, layerFEs = copyLayerFEs(), layerStack,
- regionSampling, renderArea = std::move(renderArea),
+ auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
+ sdrWhitePointNits, displayBrightnessNits, grayscale, layerFEs = copyLayerFEs(),
+ layerStack, regionSampling, renderArea = std::move(renderArea),
renderIntent]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
@@ -7335,6 +7436,16 @@
compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace,
.renderIntent = renderIntent};
+ float targetBrightness = 1.0f;
+ if (dataspace == ui::Dataspace::BT2020_HLG) {
+ const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203;
+ // With a low dimming ratio, don't fit the entire curve. Otherwise mixed content
+ // will appear way too bright.
+ if (maxBrightnessNits < 1000.f) {
+ targetBrightness = 1000.f / maxBrightnessNits;
+ }
+ }
+
std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine,
.colorProfile = colorProfile,
@@ -7343,6 +7454,7 @@
.buffer = std::move(buffer),
.sdrWhitePointNits = sdrWhitePointNits,
.displayBrightnessNits = displayBrightnessNits,
+ .targetBrightness = targetBrightness,
.regionSampling = regionSampling});
const float colorSaturation = grayscale ? 0 : 1;
@@ -7374,12 +7486,14 @@
: ftl::yield(present()).share();
for (auto& [layer, layerFE] : layers) {
- layer->onLayerDisplayed(
- ftl::Future(presentFuture)
- .then([layerFE = std::move(layerFE)](FenceResult) {
- return layerFE->stealCompositionResult().releaseFences.back().get();
- })
- .share());
+ layer->onLayerDisplayed(ftl::Future(presentFuture)
+ .then([layerFE = std::move(layerFE)](FenceResult) {
+ return layerFE->stealCompositionResult()
+ .releaseFences.back()
+ .first.get();
+ })
+ .share(),
+ ui::INVALID_LAYER_STACK);
}
return presentFuture;
@@ -7947,7 +8061,7 @@
Mutex::Autolock lock(mStateLock);
createEffectLayer(mirrorArgs, &unused, &childMirror);
MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock);
- childMirror->setClonedChild(layer->createClone());
+ childMirror->setClonedChild(layer->createClone(childMirror->getSequence()));
childMirror->reparent(mirrorDisplay.rootHandle);
}
// lock on mStateLock needs to be released before binder handle gets destroyed
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index eb9dc74..8eaa1c7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -28,6 +28,7 @@
#include <android/gui/ISurfaceComposerClient.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
+#include <ftl/algorithm.h>
#include <ftl/future.h>
#include <ftl/non_null.h>
#include <gui/BufferQueue.h>
@@ -716,7 +717,7 @@
void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
- void updateInputFlinger();
+ void updateInputFlinger(VsyncId);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
std::vector<gui::DisplayInfo>& outDisplayInfos);
@@ -854,8 +855,9 @@
}
sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) REQUIRES(mStateLock) {
- const sp<DisplayDevice> nullDisplay;
- return mDisplays.get(displayToken).value_or(std::cref(nullDisplay));
+ return mDisplays.get(displayToken)
+ .or_else(ftl::static_ref<sp<DisplayDevice>>([] { return nullptr; }))
+ .value();
}
sp<const DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) const
@@ -996,7 +998,9 @@
using FenceTimePtr = std::shared_ptr<FenceTime>;
- const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period)
+ bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
+
+ const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const
REQUIRES(kMainThreadContext);
// Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
@@ -1011,10 +1015,10 @@
*/
sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
REQUIRES(mStateLock) {
- const sp<display::DisplayToken> nullToken;
return mPhysicalDisplays.get(displayId)
.transform([](const display::PhysicalDisplay& display) { return display.token(); })
- .value_or(std::cref(nullToken));
+ .or_else([] { return std::optional<sp<display::DisplayToken>>(nullptr); })
+ .value();
}
std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
@@ -1191,6 +1195,7 @@
// Tracks layers that have pending frames which are candidates for being
// latched.
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
+ std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
@@ -1243,6 +1248,9 @@
VsyncId mLastCommittedVsyncId;
+ VsyncId mLastInputFlingerUpdateVsyncId;
+ nsecs_t mLastInputFlingerUpdateTimestamp;
+
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
@@ -1425,10 +1433,8 @@
display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
bool mFrontEndDisplayInfosChanged = false;
- // Layers visible during the last commit. This set should only be used for testing set equality
- // and membership. The pointers should not be dereferenced as it's possible the set contains
- // pointers to freed layers.
- std::unordered_set<Layer*> mVisibleLayers;
+ // WindowInfo ids visible during the last commit.
+ std::unordered_set<int32_t> mVisibleWindowIds;
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 2daea25..35c8b6c 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -78,8 +78,7 @@
template <typename Visitor>
void traverseStatesWithBuffers(Visitor&& visitor) const {
for (const auto& state : states) {
- if (state.state.hasBufferChanges() && state.state.hasValidBuffer() &&
- state.state.surface) {
+ if (state.state.hasBufferChanges() && state.externalTexture && state.state.surface) {
visitor(state.state);
}
}
@@ -88,8 +87,7 @@
template <typename Visitor>
void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
for (auto state = states.begin(); state != states.end();) {
- if (state->state.hasBufferChanges() && state->state.hasValidBuffer() &&
- state->state.surface) {
+ if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
int result = visitor(state->state, state->externalTexture);
if (result == STOP_TRAVERSAL) return;
if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 856fbbb..2b62638 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -16,6 +16,7 @@
#include <ftl/small_vector.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/WindowInfosUpdate.h>
#include "WindowInfosListenerInvoker.h"
@@ -86,11 +87,12 @@
void WindowInfosListenerInvoker::windowInfosChanged(
std::vector<WindowInfo> windowInfos, std::vector<DisplayInfo> displayInfos,
- WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall) {
+ WindowInfosReportedListenerSet reportedListeners, bool forceImmediateCall, VsyncId vsyncId,
+ nsecs_t timestamp) {
reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
auto callListeners = [this, windowInfos = std::move(windowInfos),
- displayInfos = std::move(displayInfos)](
- WindowInfosReportedListenerSet reportedListeners) mutable {
+ displayInfos = std::move(displayInfos), vsyncId,
+ timestamp](WindowInfosReportedListenerSet reportedListeners) mutable {
WindowInfosListenerVector windowInfosListeners;
{
std::scoped_lock lock(mListenersMutex);
@@ -103,6 +105,9 @@
sp<WindowInfosReportedListenerInvoker>::make(windowInfosListeners,
std::move(reportedListeners));
+ gui::WindowInfosUpdate update(std::move(windowInfos), std::move(displayInfos),
+ vsyncId.value, timestamp);
+
for (const auto& listener : windowInfosListeners) {
sp<IBinder> asBinder = IInterface::asBinder(listener);
@@ -111,8 +116,7 @@
// calling onWindowInfosReported.
asBinder->linkToDeath(reportedInvoker);
- auto status =
- listener->onWindowInfosChanged(windowInfos, displayInfos, reportedInvoker);
+ auto status = listener->onWindowInfosChanged(update, reportedInvoker);
if (!status.isOk()) {
reportedInvoker->onWindowInfosReported();
}
@@ -129,11 +133,15 @@
// to reduce the amount of binder memory used.
if (mActiveMessageCount > 0 && !forceImmediateCall) {
mWindowInfosChangedDelayed = std::move(callListeners);
+ mUnsentVsyncId = vsyncId;
+ mUnsentTimestamp = timestamp;
mReportedListenersDelayed.merge(reportedListeners);
return;
}
mWindowInfosChangedDelayed = nullptr;
+ mUnsentVsyncId = {-1};
+ mUnsentTimestamp = -1;
reportedListeners.merge(mReportedListenersDelayed);
mActiveMessageCount++;
}
@@ -154,6 +162,8 @@
mActiveMessageCount++;
callListeners = std::move(mWindowInfosChangedDelayed);
mWindowInfosChangedDelayed = nullptr;
+ mUnsentVsyncId = {-1};
+ mUnsentTimestamp = -1;
reportedListeners = std::move(mReportedListenersDelayed);
mReportedListenersDelayed.clear();
}
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 4da9828..e35d056 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -26,6 +26,8 @@
#include <gui/SpHash.h>
#include <utils/Mutex.h>
+#include "scheduler/VsyncId.h"
+
namespace android {
using WindowInfosReportedListenerSet =
@@ -40,10 +42,25 @@
void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
WindowInfosReportedListenerSet windowInfosReportedListeners,
- bool forceImmediateCall);
+ bool forceImmediateCall, VsyncId vsyncId, nsecs_t timestamp);
binder::Status onWindowInfosReported() override;
+ VsyncId getUnsentMessageVsyncId() {
+ std::scoped_lock lock(mMessagesMutex);
+ return mUnsentVsyncId;
+ }
+
+ nsecs_t getUnsentMessageTimestamp() {
+ std::scoped_lock lock(mMessagesMutex);
+ return mUnsentTimestamp;
+ }
+
+ uint32_t getPendingMessageCount() {
+ std::scoped_lock lock(mMessagesMutex);
+ return mActiveMessageCount;
+ }
+
protected:
void binderDied(const wp<IBinder>& who) override;
@@ -58,6 +75,8 @@
uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
std::function<void(WindowInfosReportedListenerSet)> mWindowInfosChangedDelayed
GUARDED_BY(mMessagesMutex);
+ VsyncId mUnsentVsyncId GUARDED_BY(mMessagesMutex) = {-1};
+ nsecs_t mUnsentTimestamp GUARDED_BY(mMessagesMutex) = -1;
WindowInfosReportedListenerSet mReportedListenersDelayed;
};
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index a9247fe..9fac14e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -327,7 +327,6 @@
invokeComposerHal2_4(&composer, display, outLayer);
composer.executeCommands(display);
- composer.resetCommands(display);
composer.destroyLayer(display, outLayer);
composer.destroyVirtualDisplay(display);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index c1bab0e..4d13aca 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -590,7 +590,7 @@
mFlinger->binderDied(display);
mFlinger->onFirstRef();
- mFlinger->updateInputFlinger();
+ mFlinger->updateInputFlinger(VsyncId{0});
mFlinger->updateCursorAsync();
mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(),
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 4304259..921cae4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -125,9 +125,12 @@
mFdp.ConsumeIntegral<int64_t>(),
mFdp.ConsumeIntegral<int64_t>());
- layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share());
- layer->onLayerDisplayed(
- ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share());
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+ ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(
+ base::unexpected(mFdp.ConsumeIntegral<status_t>()))
+ .share(),
+ ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
@@ -174,7 +177,8 @@
{mFdp.ConsumeIntegral<int32_t>(),
mFdp.ConsumeIntegral<int32_t>()} /*reqSize*/,
mFdp.PickValueInArray(kDataspaces), mFdp.ConsumeBool(),
- mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect());
+ mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect(),
+ mFdp.ConsumeBool());
layerArea.render([]() {} /*drawLayers*/);
if (!ownsHandle) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index a32750e..8061a8f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -75,7 +75,7 @@
bool isVisible() const override { return true; }
- sp<Layer> createClone() override { return nullptr; }
+ sp<Layer> createClone(uint32_t /* mirrorRootId */) override { return nullptr; }
};
class FuzzImplVSyncTracker : public scheduler::VSyncTracker {
diff --git a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
index 0df7e2f..4c26017 100644
--- a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
+++ b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
@@ -33,9 +33,14 @@
const VsyncEventData& vsyncEventData = parcelableVsyncEventData.vsync;
EXPECT_NE(std::numeric_limits<size_t>::max(), vsyncEventData.preferredFrameTimelineIndex);
+ EXPECT_GT(static_cast<int64_t>(vsyncEventData.frameTimelinesLength), 0)
+ << "Frame timelines length should be greater than 0";
+ EXPECT_LE(static_cast<int64_t>(vsyncEventData.frameTimelinesLength),
+ VsyncEventData::kFrameTimelinesCapacity)
+ << "Frame timelines length should not exceed max capacity";
EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
<< "Deadline timestamp should be greater than frame time";
- for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
EXPECT_NE(gui::FrameTimelineInfo::INVALID_VSYNC_ID,
vsyncEventData.frameTimelines[i].vsyncId);
EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 26dbc76..79886bd 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -1224,4 +1224,75 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
+TEST_F(LayerCallbackTest, SetNullBuffer) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, /*setBuffer=*/true,
+ /*setBackgroundColor=*/false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ transaction.apply();
+
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+ }
+
+ transaction.setBuffer(layer, nullptr);
+ transaction.addTransactionCompletedCallback(callback.function, callback.getContext());
+ transaction.apply();
+
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED_NULL,
+ ExpectedResult::PreviousBuffer::RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+ }
+
+ err = fillTransaction(transaction, &callback, layer, /*setBuffer=*/true,
+ /*setBackgroundColor=*/false);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.apply();
+
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+ }
+}
+
+TEST_F(LayerCallbackTest, SetNullBufferOnLayerWithoutBuffer) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
+
+ Transaction transaction;
+ transaction.setBuffer(layer, nullptr);
+ CallbackHelper callback;
+ transaction.addTransactionCompletedCallback(callback.function, callback.getContext());
+ transaction.apply();
+
+ {
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 0b8c51e..b8068f7 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1636,6 +1636,65 @@
getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
}
}
+
+TEST_P(LayerRenderTypeTransactionTest, SetNullBuffer) {
+ const Rect bounds(0, 0, 32, 32);
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
+
+ ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bounds, Color::GREEN));
+ Transaction().setBuffer(layer, buffer).apply();
+ {
+ SCOPED_TRACE("before null buffer");
+ auto shot = getScreenCapture();
+ shot->expectColor(bounds, Color::GREEN);
+ }
+
+ Transaction().setBuffer(layer, nullptr).apply();
+ {
+ SCOPED_TRACE("null buffer removes buffer");
+ auto shot = getScreenCapture();
+ shot->expectColor(bounds, Color::BLACK);
+ }
+
+ Transaction().setBuffer(layer, buffer).apply();
+ {
+ SCOPED_TRACE("after null buffer");
+ auto shot = getScreenCapture();
+ shot->expectColor(bounds, Color::GREEN);
+ }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetNullBufferOnLayerWithoutBuffer) {
+ const Rect bounds(0, 0, 32, 32);
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+ {
+ SCOPED_TRACE("starting state");
+ auto shot = getScreenCapture();
+ shot->expectColor(bounds, Color::BLACK);
+ }
+
+ Transaction().setBuffer(layer, nullptr).apply();
+ {
+ SCOPED_TRACE("null buffer has no effect");
+ auto shot = getScreenCapture();
+ shot->expectColor(bounds, Color::BLACK);
+ }
+
+ Transaction().setBuffer(layer, nullptr).apply();
+ {
+ SCOPED_TRACE("null buffer has no effect");
+ auto shot = getScreenCapture();
+ shot->expectColor(bounds, Color::BLACK);
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index f4a8f03..3f27360 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
#include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfosUpdate.h>
#include <private/android_filesystem_config.h>
#include <cstdint>
#include <future>
@@ -41,9 +42,8 @@
WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
: mPredicate(std::move(predicate)), mPromise(promise) {}
- void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>&) override {
- if (mPredicate(windowInfos)) {
+ void onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
+ if (mPredicate(update.windowInfos)) {
mPromise.set_value();
}
}
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index aa6c74e..21ebaea 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -30,7 +30,7 @@
],
test_suites: ["device-tests"],
sanitize: {
- address: false,
+ address: true,
},
srcs: [
":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 201d37f..55705ca 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -104,6 +104,7 @@
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
"SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_GetDisplayStatsTest.cpp",
"SurfaceFlinger_HdrOutputControlTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
"SurfaceFlinger_InitializeDisplaysTest.cpp",
@@ -128,6 +129,7 @@
"TransactionTracingTest.cpp",
"TunnelModeEnabledReporterTest.cpp",
"StrongTypingTest.cpp",
+ "VSyncCallbackRegistrationTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
"VsyncModulatorTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 156007b..6ca21bd 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -200,7 +200,7 @@
constexpr bool regionSampling = false;
auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
- ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
+ ui::Dataspace::V0_SRGB, true, true);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index f1cdca3..5fed9b4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -24,6 +24,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
+#include <scheduler/VsyncConfig.h>
#include <utils/Errors.h>
#include "AsyncCallRecorder.h"
@@ -77,7 +78,7 @@
EventThreadTest();
~EventThreadTest() override;
- void createThread();
+ void setupEventThread(std::chrono::nanoseconds vsyncPeriod);
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
EventRegistrationFlags eventRegistration = {},
uid_t ownerUid = mConnectionUid);
@@ -90,8 +91,9 @@
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventFrameTimelinesCorrect(
- nsecs_t expectedTimestamp,
- /*VSyncSource::VSyncData*/ gui::VsyncEventData::FrameTimeline preferredVsyncData);
+ nsecs_t expectedTimestamp, gui::VsyncEventData::FrameTimeline preferredVsyncData);
+ void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData,
+ std::chrono::nanoseconds vsyncPeriod);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -154,19 +156,6 @@
.WillRepeatedly(Invoke(mVSyncCallbackUpdateRecorder.getInvocable()));
EXPECT_CALL(mockDispatch, unregisterCallback(_))
.WillRepeatedly(Invoke(mVSyncCallbackUnregisterRecorder.getInvocable()));
-
- createThread();
- mConnection =
- createConnection(mConnectionEventCallRecorder,
- gui::ISurfaceComposer::EventRegistration::modeChanged |
- gui::ISurfaceComposer::EventRegistration::frameRateOverride);
- mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
- gui::ISurfaceComposer::EventRegistration::modeChanged,
- mThrottledConnectionUid);
-
- // A display must be connected for VSYNC events to be delivered.
- mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
- expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
EventThreadTest::~EventThreadTest() {
@@ -179,14 +168,12 @@
EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value());
}
-void EventThreadTest::createThread() {
+void EventThreadTest::setupEventThread(std::chrono::nanoseconds vsyncPeriod) {
const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
return (uid == mThrottledConnectionUid);
};
- const auto getVsyncPeriod = [](uid_t uid) {
- return VSYNC_PERIOD.count();
- };
+ const auto getVsyncPeriod = [vsyncPeriod](uid_t uid) { return vsyncPeriod.count(); };
mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule,
@@ -195,6 +182,18 @@
// EventThread should register itself as VSyncSource callback.
EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
+
+ mConnection =
+ createConnection(mConnectionEventCallRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged |
+ gui::ISurfaceComposer::EventRegistration::frameRateOverride);
+ mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
+ gui::ISurfaceComposer::EventRegistration::modeChanged,
+ mThrottledConnectionUid);
+
+ // A display must be connected for VSYNC events to be delivered.
+ mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+ expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
@@ -259,7 +258,7 @@
ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
<< expectedTimestamp;
const auto& event = std::get<0>(args.value());
- for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ for (int i = 0; i < event.vsync.vsyncData.frameTimelinesLength; i++) {
auto prediction = mTokenManager->getPredictionsForToken(
event.vsync.vsyncData.frameTimelines[i].vsyncId);
EXPECT_TRUE(prediction.has_value());
@@ -293,6 +292,21 @@
}
}
+void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(
+ VsyncEventData vsyncEventData, std::chrono::nanoseconds vsyncPeriod) {
+ float nonPreferredTimelinesAmount =
+ scheduler::VsyncConfig::kEarlyLatchMaxThreshold / vsyncPeriod;
+ EXPECT_LE(vsyncEventData.frameTimelinesLength, nonPreferredTimelinesAmount + 1)
+ << "Amount of non-preferred frame timelines too many;"
+ << " expected presentation time will be over threshold";
+ EXPECT_LT(nonPreferredTimelinesAmount, VsyncEventData::kFrameTimelinesCapacity)
+ << "Amount of non-preferred frame timelines should be less than max capacity";
+ EXPECT_GT(static_cast<int64_t>(vsyncEventData.frameTimelinesLength), 0)
+ << "Frame timelines length should be greater than 0";
+ EXPECT_LT(vsyncEventData.preferredFrameTimelineIndex, vsyncEventData.frameTimelinesLength)
+ << "Preferred frame timeline index should be less than frame timelines length";
+}
+
void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
@@ -343,6 +357,8 @@
*/
TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
+ setupEventThread(VSYNC_PERIOD);
+
EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value());
EXPECT_FALSE(mVSyncCallbackUpdateRecorder.waitForCall(0us).has_value());
@@ -352,6 +368,8 @@
}
TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
@@ -363,6 +381,8 @@
}
TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -394,6 +414,8 @@
}
TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+ setupEventThread(VSYNC_PERIOD);
+
// Signal that we want the next vsync event to be posted to the connection
mThread->requestNextVsync(mConnection);
@@ -405,7 +427,34 @@
expectVsyncEventFrameTimelinesCorrect(123, {-1, 789, 456});
}
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesValidLength) {
+ // The VsyncEventData should not have kFrameTimelinesCapacity amount of valid frame timelines,
+ // due to longer vsync period and kEarlyLatchMaxThreshold. Use length-2 to avoid decimal
+ // truncation (e.g. 60Hz has 16.6... ms vsync period).
+ std::chrono::nanoseconds vsyncPeriod(scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
+ (VsyncEventData::kFrameTimelinesCapacity - 2));
+ setupEventThread(vsyncPeriod);
+
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ expectVSyncCallbackScheduleReceived(true);
+
+ // Use the received callback to signal a vsync event.
+ // The throttler should receive the event, as well as the connection.
+ nsecs_t expectedTimestamp = 123;
+ onVSyncEvent(expectedTimestamp, 456, 789);
+
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+ << expectedTimestamp;
+ const VsyncEventData vsyncEventData = std::get<0>(args.value()).vsync.vsyncData;
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, vsyncPeriod);
+}
+
TEST_F(EventThreadTest, getLatestVsyncEventData) {
+ setupEventThread(VSYNC_PERIOD);
+
const nsecs_t now = systemTime();
const nsecs_t preferredExpectedPresentationTime = now + 20000000;
const nsecs_t preferredDeadline = preferredExpectedPresentationTime - kReadyDuration.count();
@@ -420,9 +469,10 @@
// Check EventThread immediately requested a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+ expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, VSYNC_PERIOD);
EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
<< "Deadline timestamp should be greater than frame time";
- for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
auto prediction =
mTokenManager->getPredictionsForToken(vsyncEventData.frameTimelines[i].vsyncId);
EXPECT_TRUE(prediction.has_value());
@@ -458,6 +508,8 @@
}
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
@@ -485,6 +537,8 @@
}
TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setVsyncRate(1, mConnection);
// EventThread should enable vsync callbacks.
@@ -508,6 +562,8 @@
}
TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setVsyncRate(2, mConnection);
// EventThread should enable vsync callbacks.
@@ -534,6 +590,8 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setVsyncRate(1, mConnection);
// EventThread should enable vsync callbacks.
@@ -551,6 +609,8 @@
}
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
+ setupEventThread(VSYNC_PERIOD);
+
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
@@ -575,6 +635,8 @@
}
TEST_F(EventThreadTest, tracksEventConnections) {
+ setupEventThread(VSYNC_PERIOD);
+
EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -598,6 +660,8 @@
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
+ setupEventThread(VSYNC_PERIOD);
+
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
mThread->setVsyncRate(1, errorConnection);
@@ -622,31 +686,43 @@
}
TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->setDuration(321ns, 456ns);
expectVSyncSetDurationCallReceived(321ns, 456ns);
}
TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugInternalConnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
}
TEST_F(EventThreadTest, postHotplugExternalConnect) {
+ setupEventThread(VSYNC_PERIOD);
+
mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
+ setupEventThread(VSYNC_PERIOD);
+
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
.setId(DisplayModeId(7))
@@ -659,6 +735,8 @@
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
+ setupEventThread(VSYNC_PERIOD);
+
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(EXTERNAL_DISPLAY_ID)
.setId(DisplayModeId(5))
@@ -671,6 +749,8 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
+ setupEventThread(VSYNC_PERIOD);
+
const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
.setPhysicalDisplayId(DISPLAY_ID_64BIT)
.setId(DisplayModeId(7))
@@ -682,6 +762,8 @@
}
TEST_F(EventThreadTest, suppressConfigChanged) {
+ setupEventThread(VSYNC_PERIOD);
+
ConnectionEventRecorder suppressConnectionEventRecorder{0};
sp<MockEventThreadConnection> suppressConnection =
createConnection(suppressConnectionEventRecorder);
@@ -701,6 +783,8 @@
}
TEST_F(EventThreadTest, postUidFrameRateMapping) {
+ setupEventThread(VSYNC_PERIOD);
+
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
{.uid = 3, .frameRateHz = 40},
@@ -712,6 +796,8 @@
}
TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
+ setupEventThread(VSYNC_PERIOD);
+
const std::vector<FrameRateOverride> overrides = {
{.uid = 1, .frameRateHz = 20},
{.uid = 3, .frameRateHz = 40},
@@ -730,6 +816,8 @@
}
TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+ setupEventThread(VSYNC_PERIOD);
+
// Signal that we want the next vsync event to be posted to the throttled connection
mThread->requestNextVsync(mThrottledConnection);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 88d39db..e4f49e8 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -466,7 +466,8 @@
updateBackgroundColor(1, 0.5);
UPDATE_AND_VERIFY(hierarchyBuilder);
- auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ auto hierarchy = hierarchyBuilder.getPartialHierarchy(1, /*childrenOnly=*/true);
+ auto bgLayerId = hierarchy.mChildren.front().first->getLayer()->id;
std::vector<uint32_t> expectedTraversalPath = {1, bgLayerId, 11, 111, 12,
121, 122, 1221, 13, 2};
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 14b8e4b..97ef5a2 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -36,13 +36,13 @@
class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener {
public:
void onLayerAdded(const RequestedLayerState& layer) override {
- mActualLayersAdded.emplace(layer.id);
+ mActualLayersAdded.push_back(layer.id);
};
void onLayerDestroyed(const RequestedLayerState& layer) override {
mActualLayersDestroyed.emplace(layer.id);
};
- void expectLayersAdded(const std::unordered_set<uint32_t>& expectedLayersAdded) {
+ void expectLayersAdded(const std::vector<uint32_t>& expectedLayersAdded) {
EXPECT_EQ(expectedLayersAdded, mActualLayersAdded);
mActualLayersAdded.clear();
}
@@ -51,7 +51,7 @@
mActualLayersDestroyed.clear();
}
- std::unordered_set<uint32_t> mActualLayersAdded;
+ std::vector<uint32_t> mActualLayersAdded;
std::unordered_set<uint32_t> mActualLayersDestroyed;
};
@@ -318,7 +318,8 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+ auto bgLayerId = listener->mActualLayersAdded[1];
listener->expectLayersAdded({1, bgLayerId});
listener->expectLayersDestroyed({});
EXPECT_EQ(getRequestedLayerState(lifecycleManager, bgLayerId)->color.a, 0.5_hf);
@@ -352,7 +353,8 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+ auto bgLayerId = listener->mActualLayersAdded[1];
listener->expectLayersAdded({1, bgLayerId});
listener->expectLayersDestroyed({bgLayerId});
}
@@ -381,7 +383,8 @@
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
- auto bgLayerId = LayerCreationArgs::getInternalLayerId(1);
+ ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+ auto bgLayerId = listener->mActualLayersAdded[1];
listener->expectLayersAdded({1, bgLayerId});
listener->expectLayersDestroyed({1, bgLayerId});
}
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 0c43831..965e378 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -359,7 +359,8 @@
EXPECT_EQ(expectedChoices, actualChoices);
}
{
- // This display does not support 120 Hz, so we should choose 60 Hz despite the touch signal.
+ // The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120
+ // Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz.
mScheduler
->registerDisplay(kDisplayId3,
std::make_shared<RefreshRateSelector>(kDisplay3Modes,
@@ -371,6 +372,26 @@
expectedChoices = ftl::init::map<
const PhysicalDisplayId&,
+ DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120},
+ globalSignals)(kDisplayId2,
+ FrameRateMode{120_Hz, kDisplay2Mode120},
+ globalSignals)(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ globalSignals);
+
+ const auto actualChoices = mScheduler->chooseDisplayModes();
+ EXPECT_EQ(expectedChoices, actualChoices);
+ }
+ {
+ // We should choose 60Hz despite the touch signal as pacesetter only supports 60Hz
+ mScheduler->setPacesetterDisplay(kDisplayId3);
+ const GlobalSignals globalSignals = {.touch = true};
+ mScheduler->replaceTouchTimer(10);
+ mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+ expectedChoices = ftl::init::map<
+ const PhysicalDisplayId&,
DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
globalSignals)(kDisplayId2,
FrameRateMode{60_Hz, kDisplay2Mode60},
@@ -384,23 +405,4 @@
}
}
-TEST_F(SchedulerTest, changingPacesetterChangesVsyncSchedule) {
- // Add a second display so we can change the pacesetter.
- mScheduler->registerDisplay(kDisplayId2,
- std::make_shared<RefreshRateSelector>(kDisplay2Modes,
- kDisplay2Mode60->getId()));
- // Ensure that the pacesetter is the one we expect.
- mScheduler->setPacesetterDisplay(kDisplayId1);
-
- // Switching to the other will call onNewVsyncSchedule.
- EXPECT_CALL(*mEventThread, onNewVsyncSchedule(mScheduler->getVsyncSchedule(kDisplayId2)))
- .Times(1);
- mScheduler->setPacesetterDisplay(kDisplayId2);
-}
-
-TEST_F(SchedulerTest, promotingSamePacesetterDoesNotChangeVsyncSchedule) {
- EXPECT_CALL(*mEventThread, onNewVsyncSchedule(_)).Times(0);
- mScheduler->setPacesetterDisplay(kDisplayId1);
-}
-
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
new file mode 100644
index 0000000..29acfaa
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SurfaceFlingerGetDisplayStatsTest"
+
+#include <compositionengine/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DisplayStatInfo.h>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockTimeStats.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+using namespace android;
+using namespace testing;
+
+namespace android {
+namespace {
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+
+class SurfaceFlingerGetDisplayStatsTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ TestableSurfaceFlinger mFlinger;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ sp<DisplayDevice> mDisplay;
+ sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+ sp<compositionengine::mock::DisplaySurface>::make();
+ sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+ mock::TimeStats* mTimeStats = new mock::TimeStats();
+ Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+ Hwc2::mock::Composer* mComposer = nullptr;
+};
+
+void SurfaceFlingerGetDisplayStatsTest::SetUp() {
+ mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
+ mComposer = new Hwc2::mock::Composer();
+ mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+ mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+ static constexpr bool kIsPrimary = true;
+ FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
+ .setPowerMode(hal::PowerMode::ON)
+ .inject(&mFlinger, mComposer);
+ auto compostionEngineDisplayArgs =
+ compositionengine::DisplayCreationArgsBuilder()
+ .setId(DEFAULT_DISPLAY_ID)
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPowerAdvisor(mPowerAdvisor)
+ .setName("injected display")
+ .build();
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
+ std::move(compostionEngineDisplayArgs));
+ mDisplay =
+ FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary)
+ .setDisplaySurface(mDisplaySurface)
+ .setNativeWindow(mNativeWindow)
+ .setPowerMode(hal::PowerMode::ON)
+ .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
+ .skipRegisterDisplay()
+ .inject();
+}
+
+// TODO (b/277364366): Clients should be updated to pass in the display they want.
+TEST_F(SurfaceFlingerGetDisplayStatsTest, nullptrSucceeds) {
+ DisplayStatInfo info;
+ status_t status = mFlinger.getDisplayStats(nullptr, &info);
+ EXPECT_EQ(status, NO_ERROR);
+}
+
+TEST_F(SurfaceFlingerGetDisplayStatsTest, explicitToken) {
+ DisplayStatInfo info;
+ status_t status = mFlinger.getDisplayStats(mDisplay->getDisplayToken().promote(), &info);
+ EXPECT_EQ(status, NO_ERROR);
+}
+
+TEST_F(SurfaceFlingerGetDisplayStatsTest, invalidToken) {
+ const String8 displayName("fakeDisplay");
+ sp<IBinder> displayToken = mFlinger.createDisplay(displayName, false);
+ DisplayStatInfo info;
+ status_t status = mFlinger.getDisplayStats(displayToken, &info);
+ EXPECT_EQ(status, NAME_NOT_FOUND);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index f1a5fc4..3b6a987 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -65,10 +65,6 @@
auto refreshRateSelector() { return pacesetterSelectorPtr(); }
- const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
- return mRefreshRateSelectors;
- }
-
void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
registerDisplay(displayId, std::move(selectorPtr),
std::make_unique<mock::VsyncController>(),
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index cfa366f..a189c00 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -56,6 +56,9 @@
#include "mock/system/window/MockNativeWindow.h"
namespace android {
+
+struct DisplayStatInfo;
+
namespace renderengine {
class RenderEngine;
@@ -545,6 +548,10 @@
return sp<DisplayDevice>::make(creationArgs);
}
+ status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* outInfo) {
+ return mFlinger->getDisplayStats(displayToken, outInfo);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 03c4e71..dbb7c6c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -274,6 +274,34 @@
EXPECT_EQ(nullptr, ret.get());
}
+class FakeExternalTexture : public renderengine::ExternalTexture {
+ const sp<GraphicBuffer> mEmptyBuffer = nullptr;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint64_t mId;
+ PixelFormat mPixelFormat;
+ uint64_t mUsage;
+
+public:
+ FakeExternalTexture(BufferData& bufferData)
+ : mWidth(bufferData.getWidth()),
+ mHeight(bufferData.getHeight()),
+ mId(bufferData.getId()),
+ mPixelFormat(bufferData.getPixelFormat()),
+ mUsage(bufferData.getUsage()) {}
+ const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
+ bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+ return getId() == other.getId();
+ }
+ uint32_t getWidth() const override { return mWidth; }
+ uint32_t getHeight() const override { return mHeight; }
+ uint64_t getId() const override { return mId; }
+ PixelFormat getPixelFormat() const override { return mPixelFormat; }
+ uint64_t getUsage() const override { return mUsage; }
+ void remapBuffer() override {}
+ ~FakeExternalTexture() = default;
+};
+
class LatchUnsignaledTest : public TransactionApplicationTest {
public:
void TearDown() override {
@@ -346,7 +374,11 @@
std::vector<ResolvedComposerState> resolvedStates;
resolvedStates.reserve(transaction.states.size());
for (auto& state : transaction.states) {
- resolvedStates.emplace_back(std::move(state));
+ ResolvedComposerState resolvedState;
+ resolvedState.state = std::move(state.state);
+ resolvedState.externalTexture =
+ std::make_shared<FakeExternalTexture>(*resolvedState.state.bufferData);
+ resolvedStates.emplace_back(resolvedState);
}
TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
diff --git a/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
new file mode 100644
index 0000000..69b3861
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VSyncDispatch.h"
+#include "mock/MockVSyncDispatch.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class VSyncCallbackRegistrationTest : public Test {
+protected:
+ VSyncDispatch::Callback mCallback = [](nsecs_t, nsecs_t, nsecs_t) {};
+
+ std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch = std::make_shared<mock::VSyncDispatch>();
+ VSyncDispatch::CallbackToken mCallbackToken{7};
+ std::string mCallbackName = "callback";
+
+ std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch2 = std::make_shared<mock::VSyncDispatch>();
+ VSyncDispatch::CallbackToken mCallbackToken2{42};
+ std::string mCallbackName2 = "callback2";
+
+ void assertDispatch(const VSyncCallbackRegistration& registration,
+ std::shared_ptr<VSyncDispatch> dispatch) {
+ ASSERT_EQ(registration.mDispatch, dispatch);
+ }
+
+ void assertToken(const VSyncCallbackRegistration& registration,
+ const std::optional<VSyncDispatch::CallbackToken>& token) {
+ ASSERT_EQ(registration.mToken, token);
+ }
+};
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnDestruction) {
+ // TODO (b/279581095): With ftl::Function, `_` can be replaced with
+ // `mCallback`, here and in other calls to `registerCallback, since the
+ // ftl version has an operator==, unlike std::function.
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnPointerMove) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+ .WillOnce(Return(mCallbackToken2));
+ EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ auto registration =
+ std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch, mCallback, mCallbackName);
+
+ auto registration2 =
+ std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch2, mCallback, mCallbackName2);
+
+ registration2 = std::move(registration);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(*registration2.get(), mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(*registration2.get(), mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnMoveOperator) {
+ {
+ InSequence seq;
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+ .WillOnce(Return(mCallbackToken2));
+ EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+ VSyncCallbackRegistration registration2(mVsyncDispatch2, mCallback, mCallbackName2);
+
+ registration2 = std::move(registration);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveConstructor) {
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+ VSyncCallbackRegistration registration2(std::move(registration));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveOperatorEqualsSelf) {
+ EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+ .WillOnce(Return(mCallbackToken));
+ EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+ VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+ // Use a reference so the compiler doesn't realize that registration is
+ // being moved to itself.
+ VSyncCallbackRegistration& registrationRef = registration;
+ registration = std::move(registrationRef);
+
+ ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+ ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5dc3490..d3fb9fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -55,7 +55,6 @@
std::vector<aidl::android::hardware::graphics::composer3::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&));
- MOCK_METHOD1(resetCommands, void(Display));
MOCK_METHOD1(executeCommands, Error(Display));
MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 0d94f4c..50e07fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -32,7 +32,7 @@
MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
- MOCK_METHOD0(createClone, sp<Layer>());
+ MOCK_METHOD1(createClone, sp<Layer>(uint32_t));
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility,
scheduler::LayerInfo::FrameRateCompatibility());
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 53f3daf..971a0b9 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "PowerHalControllerBenchmarks"
+#define LOG_TAG "VibratorHalControllerBenchmarks"
#include <benchmark/benchmark.h>
#include <vibratorservice/VibratorHalController.h>
@@ -183,7 +183,7 @@
return;
}
- auto duration = 6000s;
+ auto duration = 60s;
auto callback = []() {};
auto amplitude = 1.0f;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 922a44f..5965953 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -227,6 +227,10 @@
android::sp<ANativeWindow> window;
VkSwapchainKHR swapchain_handle;
uint64_t consumer_usage;
+
+ // Indicate whether this surface has been used by a swapchain, no matter the
+ // swapchain is still current or has been destroyed.
+ bool used_by_swapchain;
};
VkSurfaceKHR HandleFromSurface(Surface* surface) {
@@ -601,6 +605,7 @@
surface->window = pCreateInfo->window;
surface->swapchain_handle = VK_NULL_HANDLE;
+ surface->used_by_swapchain = false;
int err = native_window_get_consumer_usage(surface->window.get(),
&surface->consumer_usage);
if (err != android::OK) {
@@ -1394,14 +1399,20 @@
// orphans the previous buffers, getting us back to the state where we can
// dequeue all buffers.
//
+ // This is not necessary if the surface was never used previously.
+ //
// TODO(http://b/134186185) recycle swapchain images more efficiently
ANativeWindow* window = surface.window.get();
- err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
- strerror(-err), err);
- err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
- ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
- strerror(-err), err);
+ if (surface.used_by_swapchain) {
+ err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != android::OK,
+ "native_window_api_disconnect failed: %s (%d)", strerror(-err),
+ err);
+ err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != android::OK,
+ "native_window_api_connect failed: %s (%d)", strerror(-err),
+ err);
+ }
err =
window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, nsecs_t{-1});
@@ -1787,6 +1798,7 @@
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN);
+ surface.used_by_swapchain = true;
surface.swapchain_handle = HandleFromSwapchain(swapchain);
*swapchain_handle = surface.swapchain_handle;
return VK_SUCCESS;