Merge "Lock all of SurfaceFlinger::processDisplayAdded for kMainThreadContext instead of just a portion of it." into main
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index c74ba0a..65ad896 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -47,6 +47,71 @@
return mHaveMessage ? OK : WOULD_BLOCK;
}
+ void moveMsgStart(ipc_msg_t* msg, size_t msg_size, size_t offset) {
+ LOG_ALWAYS_FATAL_IF(offset > msg_size, "tried to move message past its end %zd>%zd", offset,
+ msg_size);
+ while (true) {
+ if (offset == 0) {
+ break;
+ }
+ if (offset >= msg->iov[0].iov_len) {
+ // Move to the next iov, this one was sent already
+ offset -= msg->iov[0].iov_len;
+ msg->iov++;
+ msg->num_iov -= 1;
+ } else {
+ // We need to move the base of the current iov
+ msg->iov[0].iov_len -= offset;
+ msg->iov[0].iov_base = static_cast<char*>(msg->iov[0].iov_base) + offset;
+ offset = 0;
+ }
+ }
+ // We only send handles on the first message. This can be changed in the future if we want
+ // to send more handles than the maximum per message limit (which would require sending
+ // multiple messages). The current code makes sure that we send less handles than the
+ // maximum trusty allows.
+ msg->num_handles = 0;
+ }
+
+ status_t sendTrustyMsg(ipc_msg_t* msg, size_t msg_size) {
+ do {
+ ssize_t rc = send_msg(mSocket.fd.get(), msg);
+ if (rc == ERR_NOT_ENOUGH_BUFFER) {
+ // Peer is blocked, wait until it unblocks.
+ // TODO: when tipc supports a send-unblocked handler,
+ // save the message here in a queue and retry it asynchronously
+ // when the handler gets called by the library
+ uevent uevt;
+ do {
+ rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+ if (uevt.event & IPC_HANDLE_POLL_HUP) {
+ return DEAD_OBJECT;
+ }
+ } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
+
+ // Retry the send, it should go through this time because
+ // sending is now unblocked
+ rc = send_msg(mSocket.fd.get(), msg);
+ }
+ if (rc < 0) {
+ return statusFromTrusty(rc);
+ }
+ size_t sent_bytes = static_cast<size_t>(rc);
+ if (sent_bytes < msg_size) {
+ moveMsgStart(msg, msg_size, static_cast<size_t>(sent_bytes));
+ msg_size -= sent_bytes;
+ } else {
+ LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != msg_size,
+ "Sent the wrong number of bytes %zd!=%zu", rc, msg_size);
+ break;
+ }
+ } while (true);
+ return OK;
+ }
+
status_t interruptableWriteFully(
FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs,
const std::optional<SmallFunction<status_t()>>& /*altPoll*/,
@@ -86,34 +151,7 @@
msg.handles = msgHandles;
}
- ssize_t rc = send_msg(mSocket.fd.get(), &msg);
- if (rc == ERR_NOT_ENOUGH_BUFFER) {
- // Peer is blocked, wait until it unblocks.
- // TODO: when tipc supports a send-unblocked handler,
- // save the message here in a queue and retry it asynchronously
- // when the handler gets called by the library
- uevent uevt;
- do {
- rc = ::wait(mSocket.fd.get(), &uevt, INFINITE_TIME);
- if (rc < 0) {
- return statusFromTrusty(rc);
- }
- if (uevt.event & IPC_HANDLE_POLL_HUP) {
- return DEAD_OBJECT;
- }
- } while (!(uevt.event & IPC_HANDLE_POLL_SEND_UNBLOCKED));
-
- // Retry the send, it should go through this time because
- // sending is now unblocked
- rc = send_msg(mSocket.fd.get(), &msg);
- }
- if (rc < 0) {
- return statusFromTrusty(rc);
- }
- LOG_ALWAYS_FATAL_IF(static_cast<size_t>(rc) != size,
- "Sent the wrong number of bytes %zd!=%zu", rc, size);
-
- return OK;
+ return sendTrustyMsg(&msg, size);
}
status_t interruptableReadFully(
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 1fde45b..dce7778 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -41,6 +41,7 @@
],
srcs: [
+ "FeatureOverrides.cpp",
"GpuStatsInfo.cpp",
"GraphicsEnv.cpp",
"IGpuService.cpp",
diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp
new file mode 100644
index 0000000..6974da9
--- /dev/null
+++ b/libs/graphicsenv/FeatureOverrides.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2025 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 <graphicsenv/FeatureOverrides.h>
+
+#include <android-base/stringprintf.h>
+
+namespace android {
+
+using base::StringAppendF;
+
+std::string FeatureConfig::toString() const {
+ std::string result;
+ StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str());
+ StringAppendF(&result, " Status: %s\n", mEnabled ? "enabled" : "disabled");
+
+ return result;
+}
+
+std::string FeatureOverrides::toString() const {
+ std::string result;
+ result.append("Global Features:\n");
+ for (auto& cfg : mGlobalFeatures) {
+ result.append(" " + cfg.toString());
+ }
+ result.append("\n");
+ result.append("Package Features:\n");
+ for (const auto& packageFeature : mPackageFeatures) {
+ result.append(" Package:");
+ StringAppendF(&result, " %s\n", packageFeature.first.c_str());
+ for (auto& cfg : packageFeature.second) {
+ result.append(" " + cfg.toString());
+ }
+ }
+
+ return result;
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
index 2ef54ad..2b94187 100644
--- a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
+++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
@@ -27,6 +27,7 @@
FeatureConfig() = default;
FeatureConfig(const FeatureConfig&) = default;
virtual ~FeatureConfig() = default;
+ std::string toString() const;
std::string mFeatureName;
bool mEnabled;
@@ -41,6 +42,7 @@
FeatureOverrides() = default;
FeatureOverrides(const FeatureOverrides&) = default;
virtual ~FeatureOverrides() = default;
+ std::string toString() const;
std::vector<FeatureConfig> mGlobalFeatures;
/* Key: Package Name, Value: Package's Feature Configs */
diff --git a/libs/input/rust/keyboard_classification_config.rs b/libs/input/rust/keyboard_classification_config.rs
index ab74efb..26a8d8f 100644
--- a/libs/input/rust/keyboard_classification_config.rs
+++ b/libs/input/rust/keyboard_classification_config.rs
@@ -20,6 +20,15 @@
// key events at all. (Requires setup allowing InputDevice to dynamically add/remove
// KeyboardInputMapper based on blocklist and KeyEvents in case a KeyboardType::None device ends
// up producing a key event)
+
+/// This list pre-classifies a device into Alphabetic/Non-Alphabetic keyboard and tells us whether
+/// further re-classification should be allowed or not (using is_finalized value).
+/// This list DOES NOT change the source of the device or change the input mappers associated with
+/// the device. It only changes the "KeyboardType" classification. This list should be primarily
+/// used to pre-classify devices that are NOT keyboards(like mice, game pads, etc.) but generate
+/// evdev nodes that say that they are alphabetic keyboards.
+///
+/// NOTE: Pls keep the list sorted by vendor id and product id for easy searching.
pub static CLASSIFIED_DEVICES: &[(
/* vendorId */ u16,
/* productId */ u16,
@@ -96,6 +105,8 @@
(0x056e, 0x0159, KeyboardType::NonAlphabetic, true),
// Zebra LS2208 barcode scanner
(0x05e0, 0x1200, KeyboardType::NonAlphabetic, true),
+ // Glorious O2 Wireless
+ (0x093a, 0x822d, KeyboardType::NonAlphabetic, true),
// RDing FootSwitch1F1
(0x0c45, 0x7403, KeyboardType::NonAlphabetic, true),
// SteelSeries Sensei RAW Frost Blue
@@ -108,6 +119,8 @@
(0x1050, 0x0010, KeyboardType::NonAlphabetic, true),
// Yubico.com Yubikey 4 OTP+U2F+CCID
(0x1050, 0x0407, KeyboardType::NonAlphabetic, true),
+ // Razer DeathAdder Essential
+ (0x1532, 0x0098, KeyboardType::NonAlphabetic, true),
// Lenovo USB-C Wired Compact Mouse
(0x17ef, 0x6123, KeyboardType::NonAlphabetic, true),
// Corsair Katar Pro Wireless (USB dongle)
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
index 7347ac7..4929b3f 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1 +1 @@
-bduddie@google.com
+include platform/frameworks/native:/services/sensorservice/OWNERS
\ No newline at end of file
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index c9f0761..ee38f50 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -392,10 +392,6 @@
return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
}
-std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
- return getPnpId(displayId.getManufacturerId());
-}
-
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
uint8_t port, const DisplayIdentificationData& data) {
if (data.empty()) {
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 8a14db8..9ea6cec 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -20,7 +20,6 @@
#include <ostream>
#include <string>
-#include <ftl/hash.h>
#include <ftl/optional.h>
namespace android {
@@ -31,16 +30,12 @@
// Flag indicating that the display is virtual.
static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63;
- // Flag indicating that the ID is stable across reboots.
- static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
-
// TODO(b/162612135) Remove default constructor
DisplayId() = default;
constexpr DisplayId(const DisplayId&) = default;
DisplayId& operator=(const DisplayId&) = default;
constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; }
- constexpr bool isStable() const { return value & FLAG_STABLE; }
uint64_t value;
@@ -102,10 +97,12 @@
// TODO(b/162612135) Remove default constructor
PhysicalDisplayId() = default;
- constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
private:
+ // Flag indicating that the ID is stable across reboots.
+ static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId,
uint32_t modelHash)
: DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
@@ -149,13 +146,6 @@
struct GpuVirtualDisplayId : VirtualDisplayId {
explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {}
- static constexpr std::optional<GpuVirtualDisplayId> fromUniqueId(const std::string& uniqueId) {
- if (const auto hashOpt = ftl::stable_hash(uniqueId)) {
- return GpuVirtualDisplayId(HashTag{}, *hashOpt);
- }
- return {};
- }
-
static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
if (id.isVirtual() && (id.value & FLAG_GPU)) {
return GpuVirtualDisplayId(id);
@@ -164,10 +154,6 @@
}
private:
- struct HashTag {}; // Disambiguate with BaseId constructor.
- constexpr GpuVirtualDisplayId(HashTag, uint64_t hash)
- : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {}
-
explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
};
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index cdac269..5883dba 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -85,7 +85,6 @@
bool isEdid(const DisplayIdentificationData&);
std::optional<Edid> parseEdid(const DisplayIdentificationData&);
std::optional<PnpId> getPnpId(uint16_t manufacturerId);
-std::optional<PnpId> getPnpId(PhysicalDisplayId);
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
uint8_t port, const DisplayIdentificationData&);
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index ef686df..090d2ee 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -26,7 +26,6 @@
constexpr uint32_t modelHash = 42;
const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
EXPECT_EQ(port, id.getPort());
- EXPECT_EQ(manufacturerId, id.getManufacturerId());
EXPECT_FALSE(VirtualDisplayId::tryCast(id));
EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
@@ -75,21 +74,6 @@
EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value());
}
-TEST(DisplayIdTest, createGpuVirtualIdFromUniqueId) {
- static const std::string kUniqueId("virtual:ui:DisplayId_test");
- const auto idOpt = GpuVirtualDisplayId::fromUniqueId(kUniqueId);
- ASSERT_TRUE(idOpt.has_value());
- const GpuVirtualDisplayId id = idOpt.value();
- EXPECT_TRUE(VirtualDisplayId::tryCast(id));
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
- EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
- EXPECT_FALSE(HalDisplayId::tryCast(id));
-
- EXPECT_EQ(id, DisplayId::fromValue(id.value));
- EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
-}
-
TEST(DisplayIdTest, createHalVirtualId) {
const HalVirtualDisplayId id(42);
EXPECT_TRUE(VirtualDisplayId::tryCast(id));
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index d1699e7..fdcf112 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -462,18 +462,6 @@
}
}
-TEST(DisplayIdentificationTest, fromPort) {
- // Manufacturer ID should be invalid.
- ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0)));
- ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu)));
-}
-
-TEST(DisplayIdentificationTest, getVirtualDisplayId) {
- // Manufacturer ID should be invalid.
- ASSERT_FALSE(getPnpId(getVirtualDisplayId(0)));
- ASSERT_FALSE(getPnpId(getVirtualDisplayId(0xffff'ffffu)));
-}
-
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 99360b9..8db9a78 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -35,11 +35,11 @@
{
}
- // This should never fail
virtual sp<media::IAudioManagerNative> getNativeInterface() {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
const status_t res = remote()->transact(GET_NATIVE_INTERFACE, data, &reply, 0);
+ if (res == DEAD_OBJECT) return nullptr;
LOG_ALWAYS_FATAL_IF(res != OK, "%s failed with result %d", __func__, res);
const int ex = reply.readExceptionCode();
LOG_ALWAYS_FATAL_IF(ex != binder::Status::EX_NONE, "%s failed with exception %d",
@@ -48,7 +48,7 @@
sp<IBinder> binder;
const status_t err = reply.readNullableStrongBinder(&binder);
LOG_ALWAYS_FATAL_IF(binder == nullptr, "%s failed unexpected nullptr %d", __func__, err);
- const auto iface = checked_interface_cast<media::IAudioManagerNative>(std::move(binder));
+ const auto iface = checked_interface_cast<media::IAudioManagerNative>(binder);
LOG_ALWAYS_FATAL_IF(iface == nullptr, "%s failed unexpected interface", __func__);
return iface;
}
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index ca9fe5e..689221f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -19,10 +19,16 @@
],
}
+cc_aconfig_library {
+ name: "gpuservice_flags_c_lib",
+ aconfig_declarations: "graphicsenv_flags",
+}
+
cc_defaults {
name: "libgpuservice_defaults",
defaults: [
"gpuservice_defaults",
+ "libfeatureoverride_deps",
"libgfxstats_deps",
"libgpumem_deps",
"libgpumemtracer_deps",
@@ -40,8 +46,11 @@
"libgraphicsenv",
"liblog",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
+ "gpuservice_flags_c_lib",
+ "libfeatureoverride",
"libgfxstats",
"libgpumem",
"libgpumemtracer",
diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS
index 07c681f..a3afca5 100644
--- a/services/gpuservice/OWNERS
+++ b/services/gpuservice/OWNERS
@@ -1,7 +1,4 @@
chrisforbes@google.com
-lpy@google.com
-alecmouri@google.com
-lfy@google.com
-paulthomson@google.com
-pbaiget@google.com
-kocdemir@google.com
+tomnom@google.com
+
+alecmouri@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/gpuservice/feature_override/Android.bp b/services/gpuservice/feature_override/Android.bp
new file mode 100644
index 0000000..cda67f0
--- /dev/null
+++ b/services/gpuservice/feature_override/Android.bp
@@ -0,0 +1,98 @@
+// Copyright 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_defaults {
+ name: "libfeatureoverride_deps",
+ include_dirs: [
+ "external/protobuf",
+ "external/protobuf/src",
+ ],
+ header_libs: [
+ "libbase_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ "libgraphicsenv",
+ "liblog",
+ ],
+ static_libs: [
+ "libprotobuf-cpp-lite-ndk",
+ ],
+}
+
+filegroup {
+ name: "feature_config_proto_definitions",
+ srcs: [
+ "proto/feature_config.proto",
+ ],
+}
+
+genrule {
+ name: "feature_config_proto_lite_gen_headers",
+ srcs: [
+ ":feature_config_proto_definitions",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "$(location aprotoc) " +
+ "--proto_path=frameworks/native/services/gpuservice/feature_override " +
+ "--cpp_out=lite=true:$(genDir)/frameworks/native/services/gpuservice/feature_override " +
+ "$(locations :feature_config_proto_definitions)",
+ out: [
+ "frameworks/native/services/gpuservice/feature_override/proto/feature_config.pb.h",
+ ],
+ export_include_dirs: [
+ "frameworks/native/services/gpuservice/feature_override/proto/",
+ ],
+}
+
+cc_library_static {
+ name: "libfeatureoverride",
+ defaults: [
+ "libfeatureoverride_deps",
+ ],
+ srcs: [
+ ":feature_config_proto_definitions",
+ "FeatureOverrideParser.cpp",
+ ],
+ local_include_dirs: [
+ "include",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wimplicit-fallthrough",
+ ],
+ cppflags: [
+ "-Wno-sign-compare",
+ ],
+ export_include_dirs: ["include"],
+ proto: {
+ type: "lite",
+ static: true,
+ },
+ generated_headers: [
+ "feature_config_proto_lite_gen_headers",
+ ],
+}
diff --git a/services/gpuservice/feature_override/FeatureOverrideParser.cpp b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
new file mode 100644
index 0000000..1ad637c
--- /dev/null
+++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2025 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 <feature_override/FeatureOverrideParser.h>
+
+#include <chrono>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <sys/stat.h>
+#include <vector>
+
+#include <graphicsenv/FeatureOverrides.h>
+#include <log/log.h>
+
+#include "feature_config.pb.h"
+
+namespace {
+
+void resetFeatureOverrides(android::FeatureOverrides &featureOverrides) {
+ featureOverrides.mGlobalFeatures.clear();
+ featureOverrides.mPackageFeatures.clear();
+}
+
+void initFeatureConfig(android::FeatureConfig &featureConfig,
+ const feature_override::FeatureConfig &featureConfigProto) {
+ featureConfig.mFeatureName = featureConfigProto.feature_name();
+ featureConfig.mEnabled = featureConfigProto.enabled();
+}
+
+feature_override::FeatureOverrideProtos readFeatureConfigProtos(std::string configFilePath) {
+ feature_override::FeatureOverrideProtos overridesProtos;
+
+ std::ifstream protobufBinaryFile(configFilePath.c_str());
+ if (protobufBinaryFile.fail()) {
+ ALOGE("Failed to open feature config file: `%s`.", configFilePath.c_str());
+ return overridesProtos;
+ }
+
+ std::stringstream buffer;
+ buffer << protobufBinaryFile.rdbuf();
+ std::string serializedConfig = buffer.str();
+ std::vector<uint8_t> serialized(
+ reinterpret_cast<const uint8_t *>(serializedConfig.data()),
+ reinterpret_cast<const uint8_t *>(serializedConfig.data()) +
+ serializedConfig.size());
+
+ if (!overridesProtos.ParseFromArray(serialized.data(),
+ static_cast<int>(serialized.size()))) {
+ ALOGE("Failed to parse GpuConfig protobuf data.");
+ }
+
+ return overridesProtos;
+}
+
+} // namespace
+
+namespace android {
+
+std::string FeatureOverrideParser::getFeatureOverrideFilePath() const {
+ const std::string kConfigFilePath = "/system/etc/angle/feature_config_vk.binarypb";
+
+ return kConfigFilePath;
+}
+
+bool FeatureOverrideParser::shouldReloadFeatureOverrides() const {
+ std::string configFilePath = getFeatureOverrideFilePath();
+ struct stat fileStat{};
+ if (stat(getFeatureOverrideFilePath().c_str(), &fileStat) != 0) {
+ ALOGE("Error getting file information for '%s': %s", getFeatureOverrideFilePath().c_str(),
+ strerror(errno));
+ // stat'ing the file failed, so return false since reading it will also likely fail.
+ return false;
+ }
+
+ return fileStat.st_mtime > mLastProtobufReadTime;
+}
+
+void FeatureOverrideParser::forceFileRead() {
+ resetFeatureOverrides(mFeatureOverrides);
+ mLastProtobufReadTime = 0;
+}
+
+void FeatureOverrideParser::parseFeatureOverrides() {
+ const feature_override::FeatureOverrideProtos overridesProtos = readFeatureConfigProtos(
+ getFeatureOverrideFilePath());
+
+ // Global feature overrides.
+ for (const auto &featureConfigProto: overridesProtos.global_features()) {
+ FeatureConfig featureConfig;
+ initFeatureConfig(featureConfig, featureConfigProto);
+
+ mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig);
+ }
+
+ // App-specific feature overrides.
+ for (auto const &pkgConfigProto: overridesProtos.package_features()) {
+ const std::string &packageName = pkgConfigProto.package_name();
+
+ if (mFeatureOverrides.mPackageFeatures.count(packageName)) {
+ ALOGE("Package already has feature overrides! Skipping.");
+ continue;
+ }
+
+ std::vector<FeatureConfig> featureConfigs;
+ for (const auto &featureConfigProto: pkgConfigProto.feature_configs()) {
+ FeatureConfig featureConfig;
+ initFeatureConfig(featureConfig, featureConfigProto);
+
+ featureConfigs.emplace_back(featureConfig);
+ }
+
+ mFeatureOverrides.mPackageFeatures[packageName] = featureConfigs;
+ }
+
+ mLastProtobufReadTime = std::chrono::system_clock::to_time_t(
+ std::chrono::system_clock::now());
+}
+
+FeatureOverrides FeatureOverrideParser::getFeatureOverrides() {
+ if (shouldReloadFeatureOverrides()) {
+ parseFeatureOverrides();
+ }
+
+ return mFeatureOverrides;
+}
+
+} // namespace android
diff --git a/services/gpuservice/feature_override/include/feature_override/FeatureOverrideParser.h b/services/gpuservice/feature_override/include/feature_override/FeatureOverrideParser.h
new file mode 100644
index 0000000..b1f1867
--- /dev/null
+++ b/services/gpuservice/feature_override/include/feature_override/FeatureOverrideParser.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FEATURE_OVERRIDE_PARSER_H_
+#define FEATURE_OVERRIDE_PARSER_H_
+
+#include <ctime>
+#include <string>
+#include <vector>
+
+#include <graphicsenv/FeatureOverrides.h>
+
+namespace android {
+
+class FeatureOverrideParser {
+public:
+ FeatureOverrideParser() = default;
+ FeatureOverrideParser(const FeatureOverrideParser &) = default;
+ virtual ~FeatureOverrideParser() = default;
+
+ FeatureOverrides getFeatureOverrides();
+ void forceFileRead();
+
+private:
+ bool shouldReloadFeatureOverrides() const;
+ void parseFeatureOverrides();
+ // Allow FeatureOverrideParserMock to override with the unit test file's path.
+ virtual std::string getFeatureOverrideFilePath() const;
+
+ std::time_t mLastProtobufReadTime = 0;
+ FeatureOverrides mFeatureOverrides;
+};
+
+} // namespace android
+
+#endif // FEATURE_OVERRIDE_PARSER_H_
diff --git a/services/gpuservice/feature_override/proto/feature_config.proto b/services/gpuservice/feature_override/proto/feature_config.proto
new file mode 100644
index 0000000..4d4bf28
--- /dev/null
+++ b/services/gpuservice/feature_override/proto/feature_config.proto
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 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.
+ */
+
+syntax = "proto3";
+
+package feature_override;
+
+option optimize_for = LITE_RUNTIME;
+
+/**
+ * Feature Configuration
+ * feature_name: Feature name (see external/angle/include/platform/autogen/FeaturesVk_autogen.h).
+ * enabled: Either enable or disable the feature.
+ */
+message FeatureConfig
+{
+ string feature_name = 1;
+ bool enabled = 2;
+}
+
+/**
+ * Package Configuration
+ * feature_configs: List of features configs for the package.
+ */
+message PackageConfig
+{
+ string package_name = 1;
+ repeated FeatureConfig feature_configs = 2;
+}
+
+/**
+ * Feature Overrides
+ * global_features: Features to apply globally, for every package.
+ * package_features: Features to apply for individual packages.
+ */
+message FeatureOverrideProtos
+{
+ repeated FeatureConfig global_features = 1;
+ repeated PackageConfig package_features = 2;
+}
diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 3072885..057d127 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -19,6 +19,7 @@
#include <binder/IInterface.h>
#include <cutils/compiler.h>
+#include <feature_override/FeatureOverrideParser.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <graphicsenv/IGpuService.h>
#include <serviceutils/PriorityDumper.h>
@@ -96,6 +97,7 @@
std::string mDeveloperDriverPath;
std::unique_ptr<std::thread> mGpuMemAsyncInitThread;
std::unique_ptr<std::thread> mGpuWorkAsyncInitThread;
+ FeatureOverrideParser mFeatureOverrideParser;
};
} // namespace android
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 8056a2c..d2184d8 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -21,17 +21,71 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+cc_aconfig_library {
+ name: "gpuservice_unittest_flags_c_lib",
+ aconfig_declarations: "graphicsenv_flags",
+}
+
+genrule_defaults {
+ name: "gpuservice_unittest_feature_config_pb_defaults",
+ tools: ["aprotoc"],
+ tool_files: [
+ ":feature_config_proto_definitions",
+ ],
+ cmd: "$(location aprotoc) " +
+ "--encode=feature_override.FeatureOverrideProtos " +
+ "$(locations :feature_config_proto_definitions) " +
+ "< $(in) " +
+ "> $(out) ",
+}
+
+// Main protobuf used by the unit tests.
+filegroup {
+ name: "gpuservice_unittest_feature_config_vk_prototext",
+ srcs: [
+ "data/feature_config_test.txtpb",
+ ],
+}
+
+genrule {
+ name: "gpuservice_unittest_feature_config_vk_binarypb",
+ defaults: ["gpuservice_unittest_feature_config_pb_defaults"],
+ srcs: [
+ ":gpuservice_unittest_feature_config_vk_prototext",
+ ],
+ out: ["gpuservice_unittest_feature_config_vk.binarypb"],
+}
+
+// "Updated" protobuf, used to validate forceFileRead().
+filegroup {
+ name: "gpuservice_unittest_feature_config_vk_force_read_prototext",
+ srcs: [
+ "data/feature_config_test_force_read.txtpb",
+ ],
+}
+
+genrule {
+ name: "gpuservice_unittest_feature_config_vk_force_read_binarypb",
+ defaults: ["gpuservice_unittest_feature_config_pb_defaults"],
+ srcs: [
+ ":gpuservice_unittest_feature_config_vk_force_read_prototext",
+ ],
+ out: ["gpuservice_unittest_feature_config_vk_force_read.binarypb"],
+}
+
cc_test {
name: "gpuservice_unittest",
test_suites: ["device-tests"],
defaults: [
+ "aconfig_lib_cc_static_link.defaults",
"libgpuservice_defaults",
],
srcs: [
+ "FeatureOverrideParserTest.cpp",
"GpuMemTest.cpp",
"GpuMemTracerTest.cpp",
- "GpuStatsTest.cpp",
"GpuServiceTest.cpp",
+ "GpuStatsTest.cpp",
],
header_libs: ["bpf_headers"],
shared_libs: [
@@ -48,10 +102,15 @@
"libutils",
],
static_libs: [
+ "gpuservice_unittest_flags_c_lib",
"libgmock",
"libgpuservice",
"libperfetto_client_experimental",
"perfetto_trace_protos",
],
+ data: [
+ ":gpuservice_unittest_feature_config_vk_binarypb",
+ ":gpuservice_unittest_feature_config_vk_force_read_binarypb",
+ ],
require_root: true,
}
diff --git a/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
new file mode 100644
index 0000000..65a1b58
--- /dev/null
+++ b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2025 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 "gpuservice_unittest"
+
+#include <android-base/file.h>
+#include <log/log.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <com_android_graphics_graphicsenv_flags.h>
+#include <feature_override/FeatureOverrideParser.h>
+
+using ::testing::AtLeast;
+using ::testing::Return;
+
+namespace android {
+namespace {
+
+std::string getTestBinarypbPath(const std::string &filename) {
+ std::string path = android::base::GetExecutableDirectory();
+ path.append("/");
+ path.append(filename);
+
+ return path;
+}
+
+class FeatureOverrideParserMock : public FeatureOverrideParser {
+public:
+ MOCK_METHOD(std::string, getFeatureOverrideFilePath, (), (const, override));
+};
+
+class FeatureOverrideParserTest : public testing::Test {
+public:
+ FeatureOverrideParserTest() {
+ const ::testing::TestInfo *const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~FeatureOverrideParserTest() {
+ const ::testing::TestInfo *const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(),
+ test_info->name());
+ }
+
+ void SetUp() override {
+ const std::string filename = "gpuservice_unittest_feature_config_vk.binarypb";
+
+ EXPECT_CALL(mFeatureOverrideParser, getFeatureOverrideFilePath())
+ .WillRepeatedly(Return(getTestBinarypbPath(filename)));
+ }
+
+ FeatureOverrideParserMock mFeatureOverrideParser;
+};
+
+testing::AssertionResult validateFeatureConfigTestTxtpbSizes(FeatureOverrides overrides) {
+ size_t expectedGlobalFeaturesSize = 1;
+ if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) {
+ return testing::AssertionFailure()
+ << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size()
+ << ", expected: " << expectedGlobalFeaturesSize;
+ }
+
+ size_t expectedPackageFeaturesSize = 1;
+ if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size()
+ << ", expected: " << expectedPackageFeaturesSize;
+ }
+
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult validateFeatureConfigTestForceReadTxtpbSizes(FeatureOverrides overrides) {
+ size_t expectedGlobalFeaturesSize = 1;
+ if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) {
+ return testing::AssertionFailure()
+ << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size()
+ << ", expected: " << expectedGlobalFeaturesSize;
+ }
+
+ size_t expectedPackageFeaturesSize = 0;
+ if (overrides.mPackageFeatures.size() != expectedPackageFeaturesSize) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures.size(): " << overrides.mPackageFeatures.size()
+ << ", expected: " << expectedPackageFeaturesSize;
+ }
+
+ return testing::AssertionSuccess();
+}
+
+testing::AssertionResult validateGlobalOverrides1(FeatureOverrides overrides) {
+ const int kTestFeatureIndex = 0;
+ const std::string expectedFeatureName = "globalOverrides1";
+ const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = false;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides1) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ EXPECT_TRUE(validateFeatureConfigTestTxtpbSizes(overrides));
+ EXPECT_TRUE(validateGlobalOverrides1(overrides));
+}
+
+testing::AssertionResult validatePackageOverrides1(FeatureOverrides overrides) {
+ const std::string expectedTestPackageName = "com.gpuservice_unittest.packageOverrides1";
+
+ if (!overrides.mPackageFeatures.count(expectedTestPackageName)) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures missing expected package: "
+ << expectedTestPackageName;
+ }
+
+ const std::vector<FeatureConfig>& features =
+ overrides.mPackageFeatures[expectedTestPackageName];
+
+ size_t expectedFeaturesSize = 1;
+ if (features.size() != expectedFeaturesSize) {
+ return testing::AssertionFailure()
+ << "features.size(): " << features.size()
+ << ", expectedFeaturesSize: " << expectedFeaturesSize;
+ }
+
+ const std::string expectedFeatureName = "packageOverrides1";
+ const FeatureConfig &cfg = features[0];
+
+ bool expectedEnabled = true;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides1) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ EXPECT_TRUE(validateFeatureConfigTestTxtpbSizes(overrides));
+ EXPECT_TRUE(validatePackageOverrides1(overrides));
+}
+
+testing::AssertionResult validateForceFileRead(FeatureOverrides overrides) {
+ const int kTestFeatureIndex = 0;
+ const std::string expectedFeatureName = "forceFileRead";
+
+ const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = false;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, forceFileRead) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ // Validate the "original" contents are present.
+ EXPECT_TRUE(validateFeatureConfigTestTxtpbSizes(overrides));
+ EXPECT_TRUE(validateGlobalOverrides1(overrides));
+
+ // "Update" the config file.
+ const std::string filename = "gpuservice_unittest_feature_config_vk_force_read.binarypb";
+ EXPECT_CALL(mFeatureOverrideParser, getFeatureOverrideFilePath())
+ .WillRepeatedly(Return(getTestBinarypbPath(filename)));
+
+ mFeatureOverrideParser.forceFileRead();
+
+ overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ // Validate the new file contents were read and parsed.
+ EXPECT_TRUE(validateFeatureConfigTestForceReadTxtpbSizes(overrides));
+ EXPECT_TRUE(validateForceFileRead(overrides));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/data/feature_config_test.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
new file mode 100644
index 0000000..726779e
--- /dev/null
+++ b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
@@ -0,0 +1,40 @@
+# Copyright (C) 2024 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.
+#
+# Feature Configuration Test Data
+#
+# proto-file: services/gpuservice/feature_override/proto/feature_config.proto
+# proto-message: FeatureOverrideProtos
+
+# The 'feature_name' entries correspond to the FeatureOverrideParserTest() unit test name.
+global_features [
+ {
+ feature_name: "globalOverrides1"
+ enabled: False
+ }
+]
+
+# The 'package_name' and 'feature_name' entries correspond to the
+# FeatureOverrideParserTest() unit test name.
+package_features [
+ {
+ package_name: "com.gpuservice_unittest.packageOverrides1"
+ feature_configs: [
+ {
+ feature_name: "packageOverrides1"
+ enabled: True
+ }
+ ]
+ }
+]
diff --git a/services/gpuservice/tests/unittests/data/feature_config_test_force_read.txtpb b/services/gpuservice/tests/unittests/data/feature_config_test_force_read.txtpb
new file mode 100644
index 0000000..cf6a67e
--- /dev/null
+++ b/services/gpuservice/tests/unittests/data/feature_config_test_force_read.txtpb
@@ -0,0 +1,26 @@
+# Copyright (C) 2024 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.
+#
+# Feature Configuration Test Data
+#
+# proto-file: services/gpuservice/feature_override/proto/feature_config.proto
+# proto-message: FeatureOverrideProtos
+
+# The 'feature_name' entries correspond to the FeatureOverrideParserTest() unit test name.
+global_features [
+ {
+ feature_name: "forceFileRead"
+ enabled: False
+ }
+]
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4c4182d..beb4c92 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -928,6 +928,7 @@
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
: mPolicy(policy),
+ mLooper(sp<Looper>::make(false)),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
@@ -938,6 +939,7 @@
mDispatchFrozen(false),
mInputFilterEnabled(false),
mMaximumObscuringOpacityForTouch(1.0f),
+ mConnectionManager(mLooper),
mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID),
@@ -948,7 +950,6 @@
: std::move(std::unique_ptr<InputEventTimelineProcessor>(
new LatencyAggregator()))),
mLatencyTracker(*mInputEventTimelineProcessor) {
- mLooper = sp<Looper>::make(false);
mReporter = createInputReporter();
mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
@@ -971,11 +972,6 @@
releasePendingEventLocked();
drainInboundQueueLocked();
mCommandQueue.clear();
-
- while (!mConnectionsByToken.empty()) {
- std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
- removeInputChannelLocked(connection, /*notify=*/false);
- }
}
status_t InputDispatcher::start() {
@@ -1092,9 +1088,13 @@
}
// If we reached here, we have an unresponsive connection.
- std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+ std::shared_ptr<Connection> connection =
+ mConnectionManager.getConnection(mAnrTracker.firstToken());
if (connection == nullptr) {
ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
+ // As we no longer have entry for this connection, remove it form Anr tracker to prevent
+ // samme error being logged multiple times.
+ mAnrTracker.eraseToken(mAnrTracker.firstToken());
return nextAnrCheck;
}
connection->responsive = false;
@@ -1344,7 +1344,7 @@
motionEntry.deviceId, mTouchStatesByDisplay);
for (const auto& windowHandle : touchedSpies) {
const std::shared_ptr<Connection> connection =
- getConnectionLocked(windowHandle->getToken());
+ mConnectionManager.getConnection(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.
@@ -1727,7 +1727,8 @@
void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime,
std::shared_ptr<const FocusEntry> entry) {
- std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken);
+ std::shared_ptr<Connection> connection =
+ mConnectionManager.getConnection(entry->connectionToken);
if (connection == nullptr) {
return; // Connection has gone away
}
@@ -1795,7 +1796,7 @@
}
}
- auto connection = getConnectionLocked(token);
+ auto connection = mConnectionManager.getConnection(token);
if (connection == nullptr) {
// Window has gone away, clean up Pointer Capture state.
mWindowTokenWithPointerCapture = nullptr;
@@ -1834,7 +1835,7 @@
if (token == nullptr) {
continue;
}
- std::shared_ptr<Connection> connection = getConnectionLocked(token);
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(token);
if (connection == nullptr) {
continue; // Connection has gone away
}
@@ -2127,7 +2128,8 @@
void InputDispatcher::dispatchDragLocked(nsecs_t currentTime,
std::shared_ptr<const DragEntry> entry) {
- std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken);
+ std::shared_ptr<Connection> connection =
+ mConnectionManager.getConnection(entry->connectionToken);
if (connection == nullptr) {
return; // Connection has gone away
}
@@ -2371,26 +2373,6 @@
return focusedWindowHandle;
}
-/**
- * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
- * that are currently unresponsive.
- */
-std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked(
- const std::vector<Monitor>& monitors) const {
- std::vector<Monitor> responsiveMonitors;
- std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
- [](const Monitor& monitor) REQUIRES(mLock) {
- std::shared_ptr<Connection> connection = monitor.connection;
- if (!connection->responsive) {
- ALOGW("Unresponsive monitor %s will not get the new gesture",
- connection->getInputChannelName().c_str());
- return false;
- }
- return true;
- });
- return responsiveMonitors;
-}
-
base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) {
ATRACE_CALL();
@@ -2933,7 +2915,8 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken());
+ std::shared_ptr<Connection> connection =
+ mConnectionManager.getConnection(windowHandle->getToken());
if (connection == nullptr) {
ALOGW("Not creating InputTarget for %s, no input channel",
windowHandle->getName().c_str());
@@ -2987,7 +2970,8 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken());
+ std::shared_ptr<Connection> connection =
+ mConnectionManager.getConnection(windowHandle->getToken());
if (connection == nullptr) {
ALOGW("Not creating InputTarget for %s, no input channel",
windowHandle->getName().c_str());
@@ -3022,18 +3006,30 @@
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
ui::LogicalDisplayId displayId) {
- auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId);
- if (monitorsIt == mGlobalMonitorsByDisplay.end()) return;
+ mConnectionManager
+ .forEachGlobalMonitorConnection(displayId,
+ [&](const std::shared_ptr<Connection>& connection) {
+ if (!connection->responsive) {
+ ALOGW("Ignoring unrsponsive monitor: %s",
+ connection->getInputChannelName()
+ .c_str());
+ return;
+ }
- for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
- InputTarget target{monitor.connection};
- // target.firstDownTimeInTarget is not set for global monitors. It is only required in split
- // touch and global monitoring works as intended even without setting firstDownTimeInTarget.
- // Since global monitors don't have windows, use the display transform as the raw transform.
- target.rawTransform = mWindowInfos.getDisplayTransform(displayId);
- target.setDefaultPointerTransform(target.rawTransform);
- inputTargets.push_back(target);
- }
+ InputTarget target{connection};
+ // target.firstDownTimeInTarget is not set for
+ // global monitors. It is only required in split
+ // touch and global monitoring works as intended
+ // even without setting firstDownTimeInTarget. Since
+ // global monitors don't have windows, use the
+ // display transform as the raw transform.
+ base::ScopedLockAssertion assumeLocked(mLock);
+ target.rawTransform =
+ mWindowInfos.getDisplayTransform(displayId);
+ target.setDefaultPointerTransform(
+ target.rawTransform);
+ inputTargets.push_back(target);
+ });
}
/**
@@ -3992,7 +3988,7 @@
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
std::scoped_lock _l(mLock);
- std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(connectionToken);
if (connection == nullptr) {
ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",
connectionToken.get(), events);
@@ -4100,12 +4096,12 @@
void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options) {
- for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) {
- for (const Monitor& monitor : monitors) {
- synthesizeCancelationEventsForConnectionLocked(monitor.connection, options,
- /*window=*/nullptr);
- }
- }
+ mConnectionManager.forEachGlobalMonitorConnection(
+ [&](const std::shared_ptr<Connection>& connection) {
+ base::ScopedLockAssertion assumeLocked(mLock);
+ synthesizeCancelationEventsForConnectionLocked(connection, options,
+ /*window=*/nullptr);
+ });
}
void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
@@ -4123,7 +4119,7 @@
}
std::shared_ptr<Connection> resolvedConnection =
- connection ? connection : getConnectionLocked(windowHandle->getToken());
+ connection ? connection : mConnectionManager.getConnection(windowHandle->getToken());
if (!resolvedConnection) {
LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName();
return;
@@ -5288,7 +5284,7 @@
return false;
}
- std::shared_ptr<Connection> connection = getConnectionLocked(window->getToken());
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(window->getToken());
if (connection == nullptr) {
ALOGW("Not sending touch to %s because there's no corresponding connection",
window->getName().c_str());
@@ -5351,7 +5347,7 @@
std::vector<sp<WindowInfoHandle>> newHandles;
for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
const WindowInfo* info = handle->getInfo();
- if (getConnectionLocked(handle->getToken()) == nullptr) {
+ if (mConnectionManager.getConnection(handle->getToken()) == nullptr) {
const bool noInputChannel =
info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
const bool canReceiveInput =
@@ -5857,8 +5853,8 @@
// Synthesize cancel for old window and down for new window.
ScopedSyntheticEventTracer traceContext(mTracer);
- std::shared_ptr<Connection> fromConnection = getConnectionLocked(fromToken);
- std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken);
+ std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken);
+ std::shared_ptr<Connection> toConnection = mConnectionManager.getConnection(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
@@ -6033,18 +6029,10 @@
dump += addLinePrefix(mWindowInfos.dumpDisplayAndWindowInfo(), INDENT);
- if (!mGlobalMonitorsByDisplay.empty()) {
- for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) {
- dump += StringPrintf(INDENT "Global monitors on display %s:\n",
- displayId.toString().c_str());
- dumpMonitors(dump, monitors);
- }
- } else {
- dump += INDENT "Global Monitors: <none>\n";
- }
-
const nsecs_t currentTime = now();
+ dump += addLinePrefix(mConnectionManager.dump(currentTime), INDENT);
+
// Dump recently dispatched or dropped events from oldest to newest.
if (!mRecentQueue.empty()) {
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
@@ -6086,37 +6074,6 @@
dump += INDENT "CommandQueue: <empty>\n";
}
- if (!mConnectionsByToken.empty()) {
- dump += INDENT "Connections:\n";
- for (const auto& [token, connection] : mConnectionsByToken) {
- dump += StringPrintf(INDENT2 "%i: channelName='%s', "
- "status=%s, monitor=%s, responsive=%s\n",
- connection->inputPublisher.getChannel().getFd(),
- connection->getInputChannelName().c_str(),
- ftl::enum_string(connection->status).c_str(),
- toString(connection->monitor), toString(connection->responsive));
-
- if (!connection->outboundQueue.empty()) {
- dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
- connection->outboundQueue.size());
- dump += dumpQueue(connection->outboundQueue, currentTime);
- }
-
- if (!connection->waitQueue.empty()) {
- dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
- connection->waitQueue.size());
- dump += dumpQueue(connection->waitQueue, currentTime);
- }
- std::string inputStateDump = streamableToString(connection->inputState);
- if (!inputStateDump.empty()) {
- dump += INDENT3 "InputState: ";
- dump += inputStateDump + "\n";
- }
- }
- } else {
- dump += INDENT "Connections: <none>\n";
- }
-
if (!mTouchModePerDisplay.empty()) {
dump += INDENT "TouchModePerDisplay:\n";
for (const auto& [displayId, touchMode] : mTouchModePerDisplay) {
@@ -6137,16 +6094,6 @@
dump += mTracer == nullptr ? "Disabled" : "Enabled";
}
-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];
- const std::shared_ptr<Connection>& connection = monitor.connection;
- dump += StringPrintf(INDENT2 "%zu: '%s', ", i, connection->getInputChannelName().c_str());
- dump += "\n";
- }
-}
-
class LooperEventCallback : public LooperCallback {
public:
LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
@@ -6172,21 +6119,10 @@
{ // acquire lock
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
- const int fd = serverChannel->getFd();
- std::shared_ptr<Connection> connection =
- std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false,
- mIdGenerator);
-
- auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection);
- if (!inserted) {
- ALOGE("Created a new connection, but the token %p is already known", token.get());
- }
-
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
- nullptr);
+ mConnectionManager.createConnection(std::move(serverChannel), mIdGenerator, callback);
} // release lock
// Wake the looper because some connections have changed.
@@ -6212,23 +6148,11 @@
}
const sp<IBinder>& token = serverChannel->getConnectionToken();
- const int fd = serverChannel->getFd();
- std::shared_ptr<Connection> connection =
- std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/true,
- mIdGenerator);
-
- auto [_, inserted] = mConnectionsByToken.emplace(token, connection);
- if (!inserted) {
- ALOGE("Created a new connection, but the token %p is already known", token.get());
- }
-
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
- mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid);
-
- mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback),
- nullptr);
+ mConnectionManager.createGlobalInputMonitor(displayId, std::move(serverChannel),
+ mIdGenerator, pid, callback);
}
// Wake the looper because some connections have changed.
@@ -6239,7 +6163,7 @@
status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
- std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(connectionToken);
if (connection == nullptr) {
// Connection can be removed via socket hang up or an explicit call to
// 'removeInputChannel'
@@ -6262,19 +6186,14 @@
bool notify) {
LOG_ALWAYS_FATAL_IF(connection == nullptr);
abortBrokenDispatchCycleLocked(connection, notify);
- removeConnectionLocked(connection);
- if (connection->monitor) {
- removeMonitorChannelLocked(connection->getToken());
- }
+ mAnrTracker.eraseToken(connection->getToken());
+ mConnectionManager.removeConnection(connection);
- mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
-
- connection->status = Connection::Status::ZOMBIE;
return OK;
}
-void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) {
+void InputDispatcher::ConnectionManager::removeMonitorChannel(const sp<IBinder>& connectionToken) {
for (auto it = mGlobalMonitorsByDisplay.begin(); it != mGlobalMonitorsByDisplay.end();) {
auto& [displayId, monitors] = *it;
std::erase_if(monitors, [connectionToken](const Monitor& monitor) {
@@ -6295,7 +6214,8 @@
}
status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {
- const std::shared_ptr<Connection> requestingConnection = getConnectionLocked(token);
+ const std::shared_ptr<Connection> requestingConnection =
+ mConnectionManager.getConnection(token);
if (!requestingConnection) {
LOG(WARNING)
<< "Attempted to pilfer pointers from an un-registered channel or invalid token";
@@ -6402,7 +6322,8 @@
} // release lock
}
-std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) {
+std::optional<gui::Pid> InputDispatcher::ConnectionManager::findMonitorPidByToken(
+ const sp<IBinder>& token) const {
for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) {
for (const Monitor& monitor : monitors) {
if (monitor.connection->getToken() == token) {
@@ -6413,7 +6334,7 @@
return std::nullopt;
}
-std::shared_ptr<Connection> InputDispatcher::getConnectionLocked(
+std::shared_ptr<Connection> InputDispatcher::ConnectionManager::getConnection(
const sp<IBinder>& inputConnectionToken) const {
if (inputConnectionToken == nullptr) {
return nullptr;
@@ -6428,19 +6349,6 @@
return nullptr;
}
-std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
- std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
- if (connection == nullptr) {
- return "<nullptr>";
- }
- return connection->getInputChannelName();
-}
-
-void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) {
- mAnrTracker.eraseToken(connection->getToken());
- mConnectionsByToken.erase(connection->getToken());
-}
-
void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
const std::shared_ptr<Connection>& connection,
uint32_t seq, bool handled,
@@ -6665,7 +6573,7 @@
if (connection.monitor) {
ALOGW("Monitor %s is unresponsive: %s", connection.getInputChannelName().c_str(),
reason.c_str());
- pid = findMonitorPidByTokenLocked(connectionToken);
+ pid = mConnectionManager.findMonitorPidByToken(connectionToken);
} else {
// The connection is a window
ALOGW("Window %s is unresponsive: %s", connection.getInputChannelName().c_str(),
@@ -6685,7 +6593,7 @@
const sp<IBinder>& connectionToken = connection.getToken();
std::optional<gui::Pid> pid;
if (connection.monitor) {
- pid = findMonitorPidByTokenLocked(connectionToken);
+ pid = mConnectionManager.findMonitorPidByToken(connectionToken);
} else {
// The connection is a window
const sp<WindowInfoHandle> handle = mWindowInfos.findWindowHandle(connectionToken);
@@ -7246,10 +7154,10 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
deviceId, pointers, downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
- getConnectionLocked(newWallpaper->getToken());
+ mConnectionManager.getConnection(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
std::shared_ptr<Connection> toConnection =
- getConnectionLocked(toWindowHandle->getToken());
+ mConnectionManager.getConnection(toWindowHandle->getToken());
toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, wallpaperConnection,
wallpaperFlags, traceTracker);
@@ -7313,4 +7221,132 @@
}
}
+InputDispatcher::ConnectionManager::ConnectionManager(const sp<android::Looper>& looper)
+ : mLooper(looper) {}
+
+// This destructor is required to ensure cleanup of each input connection, so that the fd is
+// removed from the looper.
+InputDispatcher::ConnectionManager::~ConnectionManager() {
+ while (!mConnectionsByToken.empty()) {
+ std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
+ removeConnection(connection);
+ }
+}
+
+void InputDispatcher::ConnectionManager::forEachGlobalMonitorConnection(
+ std::function<void(const std::shared_ptr<Connection>&)> f) const {
+ for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) {
+ for (const Monitor& monitor : monitors) {
+ f(monitor.connection);
+ }
+ }
+}
+
+void InputDispatcher::ConnectionManager::forEachGlobalMonitorConnection(
+ ui::LogicalDisplayId displayId,
+ std::function<void(const std::shared_ptr<Connection>&)> f) const {
+ auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId);
+ if (monitorsIt == mGlobalMonitorsByDisplay.end()) return;
+
+ for (const Monitor& monitor : monitorsIt->second) {
+ f(monitor.connection);
+ }
+}
+
+void InputDispatcher::ConnectionManager::createGlobalInputMonitor(
+ ui::LogicalDisplayId displayId, std::unique_ptr<InputChannel>&& inputChannel,
+ const android::IdGenerator& idGenerator, gui::Pid pid, std::function<int(int)> callback) {
+ const int fd = inputChannel->getFd();
+ std::shared_ptr<Connection> connection =
+ std::make_shared<Connection>(std::move(inputChannel), /*monitor=*/true, idGenerator);
+ sp<IBinder> token = connection->getToken();
+ auto [_, inserted] = mConnectionsByToken.emplace(token, connection);
+ if (!inserted) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+ mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid);
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr);
+}
+
+void InputDispatcher::ConnectionManager::createConnection(
+ std::unique_ptr<InputChannel>&& inputChannel, const android::IdGenerator& idGenerator,
+ std::function<int(int)> callback) {
+ const int fd = inputChannel->getFd();
+ std::shared_ptr<Connection> connection =
+ std::make_shared<Connection>(std::move(inputChannel), /*monitor=*/false, idGenerator);
+ sp<IBinder> token = connection->getToken();
+ auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection);
+ if (!inserted) {
+ ALOGE("Created a new connection, but the token %p is already known", token.get());
+ }
+
+ mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr);
+}
+
+status_t InputDispatcher::ConnectionManager::removeConnection(
+ const std::shared_ptr<Connection>& connection) {
+ mConnectionsByToken.erase(connection->getToken());
+
+ if (connection->monitor) {
+ removeMonitorChannel(connection->getToken());
+ }
+
+ mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
+
+ connection->status = Connection::Status::ZOMBIE;
+ return OK;
+}
+
+std::string InputDispatcher::ConnectionManager::dump(nsecs_t currentTime) const {
+ std::string dump;
+ if (!mGlobalMonitorsByDisplay.empty()) {
+ for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) {
+ dump += StringPrintf("Global monitors on display %s:\n", displayId.toString().c_str());
+ const size_t numMonitors = monitors.size();
+ for (size_t i = 0; i < numMonitors; i++) {
+ const Monitor& monitor = monitors[i];
+ const std::shared_ptr<Connection>& connection = monitor.connection;
+ dump += StringPrintf(INDENT "%zu: '%s', ", i,
+ connection->getInputChannelName().c_str());
+ dump += "\n";
+ }
+ }
+ } else {
+ dump += "Global Monitors: <none>\n";
+ }
+
+ if (!mConnectionsByToken.empty()) {
+ dump += "Connections:\n";
+ for (const auto& [token, connection] : mConnectionsByToken) {
+ dump += StringPrintf(INDENT "%i: channelName='%s', "
+ "status=%s, monitor=%s, responsive=%s\n",
+ connection->inputPublisher.getChannel().getFd(),
+ connection->getInputChannelName().c_str(),
+ ftl::enum_string(connection->status).c_str(),
+ toString(connection->monitor), toString(connection->responsive));
+
+ if (!connection->outboundQueue.empty()) {
+ dump += StringPrintf(INDENT2 "OutboundQueue: length=%zu\n",
+ connection->outboundQueue.size());
+ dump += dumpQueue(connection->outboundQueue, currentTime);
+ }
+
+ if (!connection->waitQueue.empty()) {
+ dump += StringPrintf(INDENT2 "WaitQueue: length=%zu\n",
+ connection->waitQueue.size());
+ dump += dumpQueue(connection->waitQueue, currentTime);
+ }
+ std::string inputStateDump = streamableToString(connection->inputState);
+ if (!inputStateDump.empty()) {
+ dump += INDENT2 "InputState: ";
+ dump += inputStateDump + "\n";
+ }
+ }
+ } else {
+ dump += "Connections: <none>\n";
+ }
+ return dump;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index bca1c67..7bfa738 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -260,13 +260,6 @@
const std::unordered_map<ui::LogicalDisplayId, TouchState>& touchStatesByDisplay,
ui::LogicalDisplayId displayId);
- 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 std::shared_ptr<Connection>& connection) REQUIRES(mLock);
-
status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);
template <typename T>
@@ -274,17 +267,6 @@
std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
};
- // All registered connections mapped by input channel token.
- 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<gui::Pid> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
- // Input channels that will receive a copy of all input events sent to the provided display.
- std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay
- GUARDED_BY(mLock);
-
const HmacKeyManager mHmacKeyManager;
const std::array<uint8_t, 32> getSignature(const MotionEntry& motionEntry,
const DispatchEntry& dispatchEntry) const;
@@ -417,6 +399,48 @@
DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
+ class ConnectionManager {
+ public:
+ ConnectionManager(const sp<Looper>& lopper);
+ ~ConnectionManager();
+
+ std::shared_ptr<Connection> getConnection(const sp<IBinder>& inputConnectionToken) const;
+
+ // Find a monitor pid by the provided token.
+ std::optional<gui::Pid> findMonitorPidByToken(const sp<IBinder>& token) const;
+ void forEachGlobalMonitorConnection(
+ std::function<void(const std::shared_ptr<Connection>&)> f) const;
+ void forEachGlobalMonitorConnection(
+ ui::LogicalDisplayId displayId,
+ std::function<void(const std::shared_ptr<Connection>&)> f) const;
+
+ void createGlobalInputMonitor(ui::LogicalDisplayId displayId,
+ std::unique_ptr<InputChannel>&& inputChannel,
+ const IdGenerator& idGenerator, gui::Pid pid,
+ std::function<int(int)> callback);
+
+ status_t removeConnection(const std::shared_ptr<Connection>& connection);
+
+ void createConnection(std::unique_ptr<InputChannel>&& inputChannel,
+ const IdGenerator& idGenerator, std::function<int(int)> callback);
+
+ std::string dump(nsecs_t currentTime) const;
+
+ private:
+ const sp<Looper> mLooper;
+
+ // All registered connections mapped by input channel token.
+ std::unordered_map<sp<IBinder>, std::shared_ptr<Connection>, StrongPointerHash<IBinder>>
+ mConnectionsByToken;
+
+ // Input channels that will receive a copy of all input events sent to the provided display.
+ std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay;
+
+ void removeMonitorChannel(const sp<IBinder>& connectionToken);
+ };
+
+ ConnectionManager mConnectionManager GUARDED_BY(mLock);
+
void setInputWindowsLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
ui::LogicalDisplayId displayId) REQUIRES(mLock);
@@ -582,8 +606,6 @@
nsecs_t& nextWakeupTime) REQUIRES(mLock);
base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry) REQUIRES(mLock);
- std::vector<Monitor> selectResponsiveMonitorsLocked(
- const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
@@ -689,12 +711,9 @@
// Dump state.
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);
status_t removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
index 881a96b..4da05c1 100644
--- a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
+++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
@@ -133,10 +133,11 @@
}
void LatencyAggregatorWithHistograms::processStatistics(const InputEventTimeline& timeline) {
- // Only gather data for Down, Move and Up motion events and Key events
+ // Only gather data for Down, Move, Up and Scroll motion events and Key events
if (!(timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN ||
timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_MOVE ||
timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_UP ||
+ timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_SCROLL ||
timeline.inputEventActionType == InputEventActionType::KEY))
return;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 24919b6..207806d 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -1085,7 +1085,7 @@
return mReader->mEventHub.get();
}
-int32_t InputReader::ContextImpl::getNextId() {
+int32_t InputReader::ContextImpl::getNextId() const {
return mIdGenerator.nextId();
}
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1403ca2..931766b 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -154,7 +154,7 @@
REQUIRES(mReader->mLock) override;
InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override;
EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override;
- int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
+ int32_t getNextId() const NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
int32_t getLedMetaState() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock)
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index e0e0ac2..20ed74f 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,7 +55,7 @@
virtual InputReaderPolicyInterface* getPolicy() = 0;
virtual EventHubInterface* getEventHub() = 0;
- virtual int32_t getNextId() = 0;
+ virtual int32_t getNextId() const = 0;
virtual void updateLedMetaState(int32_t metaState) = 0;
virtual int32_t getLedMetaState() = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 9f584a0..e21c2f9 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -419,7 +419,7 @@
}
}
-std::optional<ui::LogicalDisplayId> CursorInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> CursorInputMapper::getAssociatedDisplayId() const {
return mDisplayId;
}
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 8319922..301632f 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -63,7 +63,7 @@
virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
- virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
+ virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override;
private:
// Amount that trackball needs to move in order to generate a key event.
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index d4a86ac..630c3d9 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -66,11 +66,12 @@
virtual ~InputMapper();
- inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+ inline int32_t getDeviceId() const { return mDeviceContext.getId(); }
inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; };
inline const std::string getDeviceName() const { return mDeviceContext.getName(); }
inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
+ inline const InputReaderContext* getContext() const { return mDeviceContext.getContext(); }
inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
virtual uint32_t getSources() const = 0;
@@ -114,7 +115,9 @@
[[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
- virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; }
+ virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const {
+ return std::nullopt;
+ }
virtual void updateLedState(bool reset) {}
virtual std::optional<HardwareProperties> getTouchpadHardwareProperties();
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index fe3e4c2..400792b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -104,14 +104,14 @@
return mMapperSource;
}
-ui::Rotation KeyboardInputMapper::getOrientation() {
+ui::Rotation KeyboardInputMapper::getOrientation() const {
if (mViewport) {
return mViewport->orientation;
}
return ui::ROTATION_0;
}
-ui::LogicalDisplayId KeyboardInputMapper::getDisplayId() {
+ui::LogicalDisplayId KeyboardInputMapper::getDisplayId() const {
if (mViewport) {
return mViewport->displayId;
}
@@ -471,7 +471,7 @@
}
}
-std::optional<ui::LogicalDisplayId> KeyboardInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> KeyboardInputMapper::getAssociatedDisplayId() const {
if (mViewport) {
return std::make_optional(mViewport->displayId);
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 7d9b3e4..9e2a81b 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -47,7 +47,7 @@
int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
int32_t getMetaState() override;
- std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override;
void updateLedState(bool reset) override;
private:
@@ -96,8 +96,8 @@
void configureParameters();
void dumpParameters(std::string& dump) const;
- ui::Rotation getOrientation();
- ui::LogicalDisplayId getDisplayId();
+ ui::Rotation getOrientation() const;
+ ui::LogicalDisplayId getDisplayId() const;
[[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
int32_t scanCode, int32_t usageCode);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 6efaeca..5cfda03 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3666,7 +3666,7 @@
int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
- float yPrecision, nsecs_t downTime, MotionClassification classification) {
+ float yPrecision, nsecs_t downTime, MotionClassification classification) const {
std::vector<PointerCoords> pointerCoords;
std::vector<PointerProperties> pointerProperties;
uint32_t pointerCount = 0;
@@ -3992,7 +3992,7 @@
return true;
}
-std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() const {
return mParameters.hasAssociatedDisplay ? std::make_optional(mViewport.displayId)
: std::nullopt;
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index eb4326f..96fc61b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -185,7 +185,7 @@
[[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(
const StylusState& state) override;
- std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override;
protected:
CursorButtonAccumulator mCursorButtonAccumulator;
@@ -840,7 +840,7 @@
int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
const IdToIndexArray& idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision,
- float yPrecision, nsecs_t downTime, MotionClassification classification);
+ float yPrecision, nsecs_t downTime, MotionClassification classification) const;
// Returns if this touch device is a touch screen with an associated display.
bool isTouchScreen();
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 0df3364..18a7102 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -506,7 +506,7 @@
return out;
}
-std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() const {
return mDisplayId;
}
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 56553c9..9f53a7b 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -66,7 +66,7 @@
using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/,
uint16_t /*productId*/, uint16_t /*version*/>;
- std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() const override;
std::optional<HardwareProperties> getTouchpadHardwareProperties() override;
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 827076a..480e276 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -14,11 +14,15 @@
* limitations under the License.
*/
+#include "../Macros.h"
+
#include "gestures/GestureConverter.h"
+#include <ios>
#include <optional>
#include <sstream>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <com_android_input_flags.h>
#include <ftl/enum.h>
@@ -250,6 +254,18 @@
const Gesture& gesture) {
std::list<NotifyArgs> out = {};
+ if (mCurrentClassification != MotionClassification::NONE) {
+ // Handling button changes during an ongoing gesture would be tricky, as we'd have to avoid
+ // sending duplicate DOWN events or premature UP events (e.g. if the gesture ended but the
+ // button was still down). It would also make handling touchpad events more difficult for
+ // apps, which would have to handle cases where e.g. a scroll gesture ends (and therefore
+ // the event lose the TWO_FINGER_SWIPE classification) but there isn't an UP because the
+ // button's still down. It's unclear how one should even handle button changes during most
+ // gestures, and they're probably accidental anyway. So, instead, just ignore them.
+ LOG(INFO) << "Ignoring button change because a gesture is ongoing.";
+ return out;
+ }
+
PointerCoords coords;
coords.clear();
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
@@ -312,6 +328,15 @@
for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
if (buttonsReleased & button) {
uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ if (!(newButtonState & actionButton)) {
+ // We must have received the ButtonsChange gesture that put this button down during
+ // another gesture, and therefore dropped the BUTTON_PRESS action for it, or
+ // released the button when another gesture began during its press. Drop the
+ // BUTTON_RELEASE too to keep the stream consistent.
+ LOG(INFO) << "Dropping release event for button 0x" << std::hex << actionButton
+ << " as it wasn't in the button state.";
+ continue;
+ }
newButtonState &= ~actionButton;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
actionButton, newButtonState, /* pointerCount= */ 1,
@@ -362,7 +387,7 @@
std::list<NotifyArgs> out;
PointerCoords& coords = mFakeFingerCoords[0];
if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
- out += exitHover(when, readTime);
+ out += prepareForFakeFingerGesture(when, readTime);
mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
coords.clear();
@@ -421,7 +446,7 @@
std::list<NotifyArgs> out;
mDownTime = when;
mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
- out += exitHover(when, readTime);
+ out += prepareForFakeFingerGesture(when, readTime);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/*actionButton=*/0, /*buttonState=*/0,
/*pointerCount=*/1, &coords));
@@ -479,7 +504,7 @@
// separate swipes with an appropriate lift event between them, so we don't have to worry
// about the finger count changing mid-swipe.
- out += exitHover(when, readTime);
+ out += prepareForFakeFingerGesture(when, readTime);
mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
@@ -567,9 +592,7 @@
LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START,
"First pinch gesture does not have the START zoom state (%d instead).",
gesture.details.pinch.zoom_state);
- std::list<NotifyArgs> out;
-
- out += exitHover(when, readTime);
+ std::list<NotifyArgs> out = prepareForFakeFingerGesture(when, readTime);
mCurrentClassification = MotionClassification::PINCH;
mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX;
@@ -644,6 +667,16 @@
}
}
+std::list<NotifyArgs> GestureConverter::prepareForFakeFingerGesture(nsecs_t when,
+ nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ if (isPointerDown(mButtonState)) {
+ out += releaseAllButtons(when, readTime);
+ }
+ out += exitHover(when, readTime);
+ return out;
+}
+
NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action) {
PointerCoords coords;
coords.clear();
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 1f6acd3..ae85e3a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -92,6 +92,8 @@
[[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> prepareForFakeFingerGesture(nsecs_t when, nsecs_t readTime);
+
NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action);
NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 18e0b30..594ee3b 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -141,9 +141,9 @@
*/
class CursorInputMapperUnitTestBase : public InputMapperUnitTest {
protected:
- void SetUp() override { SetUpWithBus(BUS_USB); }
- void SetUpWithBus(int bus) override {
- InputMapperUnitTest::SetUpWithBus(bus);
+ void SetUp() override { SetUp(BUS_USB, /*isExternal=*/false); }
+ void SetUp(int bus, bool isExternal) override {
+ InputMapperUnitTest::SetUp(bus, isExternal);
// Current scan code state - all keys are UP by default
setScanCodeState(KeyState::UP,
@@ -1142,7 +1142,9 @@
class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
protected:
- void SetUp() override { SetUpWithBus(BUS_BLUETOOTH); }
+ void SetUp() override {
+ CursorInputMapperUnitTestBase::SetUp(BUS_BLUETOOTH, /*isExternal=*/true);
+ }
};
TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) {
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 8fa439d..fd9884b 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -15,11 +15,15 @@
*/
#include <memory>
+#include <tuple>
+#include <android-base/result-gmock.h>
+#include <android-base/result.h>
#include <com_android_input_flags.h>
#include <flag_macros.h>
#include <gestures/GestureConverter.h>
#include <gtest/gtest.h>
+#include <input/InputVerifier.h>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
@@ -43,8 +47,12 @@
const auto TOUCHPAD_PALM_REJECTION_V2 =
ACONFIG_FLAG(input_flags, enable_v2_touchpad_typing_palm_rejection);
+constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2;
+constexpr stime_t GESTURE_TIME = ARBITRARY_GESTURE_TIME;
+
} // namespace
+using android::base::testing::Ok;
using testing::AllOf;
using testing::Each;
using testing::ElementsAre;
@@ -55,9 +63,8 @@
protected:
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr int32_t EVENTHUB_ID = 1;
- static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2;
- void SetUp() {
+ GestureConverterTest() {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = sp<FakeInputReaderPolicy>::make();
mFakeListener = std::make_unique<TestInputListener>();
@@ -1698,4 +1705,135 @@
WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
}
+/**
+ * Tests that the event stream output by the converter remains consistent when converting sequences
+ * of Gestures interleaved with button presses in various ways. Takes tuples of three Gestures: one
+ * that starts the gesture sequence, one that continues it (which may or may not be used in a
+ * particular test case), and one that ends it.
+ */
+class GestureConverterConsistencyTest
+ : public GestureConverterTest,
+ public testing::WithParamInterface<std::tuple<Gesture, Gesture, Gesture>> {
+protected:
+ GestureConverterConsistencyTest()
+ : GestureConverterTest(),
+ mParamStartGesture(std::get<0>(GetParam())),
+ mParamContinueGesture(std::get<1>(GetParam())),
+ mParamEndGesture(std::get<2>(GetParam())),
+ mDeviceContext(*mDevice, EVENTHUB_ID),
+ mConverter(*mReader->getContext(), mDeviceContext, DEVICE_ID),
+ mVerifier("Test verifier") {
+ mConverter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+ }
+
+ base::Result<void> processMotionArgs(NotifyMotionArgs arg) {
+ return mVerifier.processMovement(arg.deviceId, arg.source, arg.action,
+ arg.getPointerCount(), arg.pointerProperties.data(),
+ arg.pointerCoords.data(), arg.flags);
+ }
+
+ void verifyArgsFromGesture(const Gesture& gesture, size_t gestureIndex) {
+ std::list<NotifyArgs> args =
+ mConverter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, gesture);
+ for (const NotifyArgs& notifyArg : args) {
+ const NotifyMotionArgs& arg = std::get<NotifyMotionArgs>(notifyArg);
+ ASSERT_THAT(processMotionArgs(arg), Ok())
+ << "when processing " << arg.dump() << "\nfrom gesture " << gestureIndex << ": "
+ << gesture.String();
+ }
+ }
+
+ void verifyArgsFromGestures(const std::vector<Gesture>& gestures) {
+ for (size_t i = 0; i < gestures.size(); i++) {
+ ASSERT_NO_FATAL_FAILURE(verifyArgsFromGesture(gestures[i], i));
+ }
+ }
+
+ Gesture mParamStartGesture;
+ Gesture mParamContinueGesture;
+ Gesture mParamEndGesture;
+
+ InputDeviceContext mDeviceContext;
+ GestureConverter mConverter;
+ InputVerifier mVerifier;
+};
+
+TEST_P(GestureConverterConsistencyTest, ButtonChangesDuringGesture) {
+ verifyArgsFromGestures({
+ mParamStartGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false),
+ mParamContinueGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false),
+ mParamEndGesture,
+ });
+}
+
+TEST_P(GestureConverterConsistencyTest, ButtonDownDuringGestureAndUpAfterEnd) {
+ verifyArgsFromGestures({
+ mParamStartGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false),
+ mParamContinueGesture,
+ mParamEndGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false),
+ });
+}
+
+TEST_P(GestureConverterConsistencyTest, GestureStartAndEndDuringButtonDown) {
+ verifyArgsFromGestures({
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false),
+ mParamStartGesture,
+ mParamContinueGesture,
+ mParamEndGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false),
+ });
+}
+
+TEST_P(GestureConverterConsistencyTest, GestureStartsWhileButtonDownAndEndsAfterUp) {
+ verifyArgsFromGestures({
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false),
+ mParamStartGesture,
+ mParamContinueGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_NONE, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false),
+ mParamEndGesture,
+ });
+}
+
+TEST_P(GestureConverterConsistencyTest, TapToClickDuringGesture) {
+ verifyArgsFromGestures({
+ mParamStartGesture,
+ Gesture(kGestureButtonsChange, GESTURE_TIME, GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_LEFT, /*is_tap=*/false),
+ mParamEndGesture,
+ });
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ GestureAndButtonInterleavings, GestureConverterConsistencyTest,
+ testing::Values(
+ std::make_tuple(Gesture(kGestureScroll, GESTURE_TIME, GESTURE_TIME, 0, -10),
+ Gesture(kGestureScroll, GESTURE_TIME, GESTURE_TIME, 0, -5),
+ Gesture(kGestureFling, GESTURE_TIME, GESTURE_TIME, 1, 1,
+ GESTURES_FLING_START)),
+ std::make_tuple(Gesture(kGestureSwipe, GESTURE_TIME, GESTURE_TIME, 0, -10),
+ Gesture(kGestureSwipe, GESTURE_TIME, GESTURE_TIME, 0, 5),
+ Gesture(kGestureSwipeLift, GESTURE_TIME, GESTURE_TIME)),
+ std::make_tuple(Gesture(kGestureFourFingerSwipe, GESTURE_TIME, GESTURE_TIME, 0,
+ -10),
+ Gesture(kGestureFourFingerSwipe, GESTURE_TIME, GESTURE_TIME, 0, 5),
+ Gesture(kGestureFourFingerSwipeLift, GESTURE_TIME, GESTURE_TIME)),
+ std::make_tuple(Gesture(kGesturePinch, GESTURE_TIME, GESTURE_TIME,
+ /*dz=*/1, GESTURES_ZOOM_START),
+ Gesture(kGesturePinch, GESTURE_TIME, GESTURE_TIME,
+ /*dz=*/0.8, GESTURES_ZOOM_UPDATE),
+ Gesture(kGesturePinch, GESTURE_TIME, GESTURE_TIME,
+ /*dz=*/1, GESTURES_ZOOM_END))));
+
} // namespace android
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 8235c90..77f42f2 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -30,7 +30,7 @@
using testing::Return;
using testing::ReturnRef;
-void InputMapperUnitTest::SetUpWithBus(int bus) {
+void InputMapperUnitTest::SetUp(int bus, bool isExternal) {
mFakePolicy = sp<FakeInputReaderPolicy>::make();
EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get()));
@@ -46,8 +46,9 @@
return mPropertyMap;
});
- mDevice = std::make_unique<NiceMock<MockInputDevice>>(&mMockInputReaderContext, DEVICE_ID,
- /*generation=*/2, mIdentifier);
+ mDevice =
+ std::make_unique<NiceMock<MockInputDevice>>(&mMockInputReaderContext, DEVICE_ID,
+ /*generation=*/2, mIdentifier, isExternal);
ON_CALL((*mDevice), getConfiguration).WillByDefault(ReturnRef(mPropertyMap));
mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
}
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 10ef6f1..edc87a3 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -40,8 +40,8 @@
protected:
static constexpr int32_t EVENTHUB_ID = 1;
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
- virtual void SetUp() override { SetUpWithBus(0); }
- virtual void SetUpWithBus(int bus);
+ virtual void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); }
+ virtual void SetUp(int bus, bool isExternal);
void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution,
int32_t flat = 0, int32_t fuzz = 0);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 470e65b..50cbedf 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -7364,35 +7364,39 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
// down when BTN_TOUCH is pressed, pressure defaults to 1
+ processPosition(mapper, 151, 251);
processKey(mapper, BTN_TOUCH, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+ // HOVER_EXIT should have the same coordinates as the previous HOVER_MOVE
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
+ // down at the new position
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
// up when BTN_TOUCH is released, hover restored
+ processPosition(mapper, 152, 252);
processKey(mapper, BTN_TOUCH, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ // UP should have the same coordinates as the previous event
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
+ toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
+ // HOVER_ENTER at the new position
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
// exit hover when pointer goes away
processId(mapper, -1);
@@ -7400,7 +7404,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
}
TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
@@ -7435,35 +7439,39 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
// down when pressure becomes non-zero
+ processPosition(mapper, 151, 251);
processPressure(mapper, RAW_PRESSURE_MAX);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+ // HOVER_EXIT should have the same coordinates as the previous HOVER_MOVE
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
-
+ // down at the new position
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
// up when pressure becomes 0, hover restored
+ processPosition(mapper, 152, 252);
processPressure(mapper, 0);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ // UP should have the same coordinates as the previous event
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
-
+ toDisplayX(151), toDisplayY(251), 1, 0, 0, 0, 0, 0, 0, 0));
+ // HOVER_ENTER at the new position
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
// exit hover when pointer goes away
processId(mapper, -1);
@@ -7471,7 +7479,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+ toDisplayX(152), toDisplayY(252), 0, 0, 0, 0, 0, 0, 0, 0));
}
/**
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index ac616d0..dce5472 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -68,7 +68,7 @@
MOCK_METHOD(InputReaderPolicyInterface*, getPolicy, (), (override));
MOCK_METHOD(EventHubInterface*, getEventHub, (), (override));
- int32_t getNextId() override { return 1; };
+ int32_t getNextId() const override { return 1; };
MOCK_METHOD(void, updateLedMetaState, (int32_t metaState), (override));
MOCK_METHOD(int32_t, getLedMetaState, (), (override));
@@ -196,14 +196,14 @@
class MockInputDevice : public InputDevice {
public:
MockInputDevice(InputReaderContext* context, int32_t id, int32_t generation,
- const InputDeviceIdentifier& identifier)
- : InputDevice(context, id, generation, identifier) {}
+ const InputDeviceIdentifier& identifier, bool isExternal)
+ : InputDevice(context, id, generation, identifier), mIsExternal(isExternal) {}
MOCK_METHOD(uint32_t, getSources, (), (const, override));
MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const));
MOCK_METHOD(KeyboardType, getKeyboardType, (), (const, override));
MOCK_METHOD(bool, isEnabled, (), ());
- MOCK_METHOD(bool, isExternal, (), (override));
+ bool isExternal() override { return mIsExternal; }
MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ());
@@ -266,5 +266,6 @@
private:
int32_t mGeneration = 0;
+ const bool mIsExternal;
};
} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 1dd32c4..17acdd4 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -1085,10 +1085,9 @@
class KeyboardInputMapperTest_ExternalAlphabeticDevice : public KeyboardInputMapperUnitTest {
protected:
void SetUp() override {
- InputMapperUnitTest::SetUp();
+ InputMapperUnitTest::SetUp(/*bus=*/0, /*isExternal=*/true);
ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
- ON_CALL((*mDevice), isExternal).WillByDefault(Return(true));
EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
.WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY |
InputDeviceClass::EXTERNAL));
@@ -1102,10 +1101,9 @@
class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public KeyboardInputMapperUnitTest {
protected:
void SetUp() override {
- InputMapperUnitTest::SetUp();
+ InputMapperUnitTest::SetUp(/*bus=*/0, /*isExternal=*/true);
ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::NON_ALPHABETIC));
- ON_CALL((*mDevice), isExternal).WillByDefault(Return(true));
EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
.WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::EXTERNAL));
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index ca0f1e8..1cfaaa8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -57,6 +57,7 @@
}
const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
+const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE);
/**
* This is a convenience method for comparing timelines that also prints the difference between
@@ -491,8 +492,13 @@
/*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::BUTTONS},
InputEventActionType::KEY);
- InputEventTimeline unknownTimeline(
+ InputEventTimeline motionScrollTimeline(
/*eventTime*/ 12, /*readTime*/ 13,
+ /*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::MOUSE},
+ InputEventActionType::MOTION_ACTION_SCROLL);
+
+ InputEventTimeline unknownTimeline(
+ /*eventTime*/ 14, /*readTime*/ 15,
/*vendorId*/ 0, /*productId*/ 0, {InputDeviceUsageSource::TOUCHSCREEN},
InputEventActionType::UNKNOWN_INPUT_EVENT);
@@ -529,8 +535,15 @@
.readTime(keyUpTimeline.readTime)
.deviceId(DEVICE_ID)
.build());
+ mTracker->trackListener(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL, AINPUT_SOURCE_MOUSE, inputEventId + 5)
+ .eventTime(motionScrollTimeline.eventTime)
+ .readTime(motionScrollTimeline.readTime)
+ .deviceId(DEVICE_ID)
+ .pointer(FIRST_MOUSE_POINTER)
+ .build());
mTracker->trackListener(MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 5)
+ AINPUT_SOURCE_TOUCHSCREEN, inputEventId + 6)
.eventTime(unknownTimeline.eventTime)
.readTime(unknownTimeline.readTime)
.deviceId(DEVICE_ID)
@@ -541,7 +554,8 @@
std::vector<InputEventTimeline> expectedTimelines = {motionDownTimeline, motionMoveTimeline,
motionUpTimeline, keyDownTimeline,
- keyUpTimeline, unknownTimeline};
+ keyUpTimeline, motionScrollTimeline,
+ unknownTimeline};
assertReceivedTimelines(expectedTimelines);
}
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index d15048d..0ea22d4 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -53,8 +53,9 @@
*/
class MultiTouchInputMapperUnitTest : public InputMapperUnitTest {
protected:
- void SetUp() override {
- InputMapperUnitTest::SetUp();
+ void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); }
+ void SetUp(int bus, bool isExternal) override {
+ InputMapperUnitTest::SetUp(bus, isExternal);
// Present scan codes
expectScanCodes(/*present=*/true,
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
index 157bee3..548df22 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -89,9 +89,9 @@
*/
class RotaryEncoderInputMapperTest : public InputMapperUnitTest {
protected:
- void SetUp() override { SetUpWithBus(BUS_USB); }
- void SetUpWithBus(int bus) override {
- InputMapperUnitTest::SetUpWithBus(bus);
+ void SetUp() override { SetUp(/*bus=*/0, /*isExternal=*/false); }
+ void SetUp(int bus, bool isExternal) override {
+ InputMapperUnitTest::SetUp(bus, isExternal);
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL))
.WillRepeatedly(Return(true));
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index a1da39a..846260a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -346,7 +346,7 @@
}
InputReaderPolicyInterface* getPolicy() override { return mPolicy.get(); }
EventHubInterface* getEventHub() override { return mEventHub.get(); }
- int32_t getNextId() override { return mFdp->ConsumeIntegral<int32_t>(); }
+ int32_t getNextId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
void updateLedMetaState(int32_t metaState) override{};
int32_t getLedMetaState() override { return mFdp->ConsumeIntegral<int32_t>(); };
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
index 7347ac7..3ccdc17 100644
--- a/services/sensorservice/OWNERS
+++ b/services/sensorservice/OWNERS
@@ -1 +1,3 @@
bduddie@google.com
+arthuri@google.com
+rockyfang@google.com
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 05f7d1b..ea7d6d7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,15 +26,15 @@
cc_defaults {
name: "surfaceflinger_defaults",
cflags: [
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
"-Wall",
+ "-Wconversion",
"-Werror",
"-Wextra",
"-Wformat",
"-Wthread-safety",
- "-Wunused",
"-Wunreachable-code",
- "-Wconversion",
- "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-Wunused",
],
}
@@ -43,39 +43,41 @@
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
"librenderengine_deps",
- "libtimestats_deps",
"libsurfaceflinger_common_deps",
- "surfaceflinger_defaults",
"libsurfaceflinger_proto_deps",
+ "libtimestats_deps",
"poweradvisor_deps",
+ "surfaceflinger_defaults",
],
cflags: [
- "-DLOG_TAG=\"SurfaceFlinger\"",
- "-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DLOG_TAG=\"SurfaceFlinger\"",
],
shared_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
"android.hardware.configstore-utils",
"android.hardware.configstore@1.0",
"android.hardware.configstore@1.1",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.common@1.2",
- "android.hardware.common-V2-ndk",
- "android.hardware.common.fmq-V1-ndk",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
"android.os.flags-aconfig-cc-host",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libSurfaceFlingerProp",
+ "libaconfig_storage_read_api_cc",
"libbase",
"libbinder",
"libbinder_ndk",
"libcutils",
- "libEGL",
"libfmq",
- "libGLESv1_CM",
- "libGLESv2",
"libgui",
"libhidlbase",
"liblog",
@@ -86,8 +88,6 @@
"libsync",
"libui",
"libutils",
- "libSurfaceFlingerProp",
- "libaconfig_storage_read_api_cc",
],
static_libs: [
"iinputflinger_aidl_lib_static",
@@ -105,11 +105,11 @@
"libtonemap",
],
header_libs: [
+ "android.hardware.graphics.composer3-command-buffer",
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
"android.hardware.graphics.composer@2.4-command-buffer",
- "android.hardware.graphics.composer3-command-buffer",
],
export_static_lib_headers: [
"libcompositionengine",
@@ -125,8 +125,8 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "libpowermanager",
"libhidlbase",
+ "libpowermanager",
],
// TODO (marissaw): this library is not used by surfaceflinger. This is here so
// the library compiled in a way that is accessible to system partition when running
@@ -177,7 +177,6 @@
filegroup {
name: "libsurfaceflinger_backend_sources",
srcs: [
- "PowerAdvisor/*.cpp",
"DisplayHardware/AidlComposerHal.cpp",
"DisplayHardware/ComposerHal.cpp",
"DisplayHardware/FramebufferSurface.cpp",
@@ -185,6 +184,7 @@
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/HidlComposerHal.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
+ "PowerAdvisor/*.cpp",
],
}
@@ -208,21 +208,20 @@
"DisplayDevice.cpp",
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
- "FrontEnd/LayerCreationArgs.cpp",
- "FrontEnd/LayerHandle.cpp",
- "FrontEnd/LayerSnapshot.cpp",
- "FrontEnd/LayerSnapshotBuilder.cpp",
- "FrontEnd/LayerHierarchy.cpp",
- "FrontEnd/LayerLifecycleManager.cpp",
- "FrontEnd/RequestedLayerState.cpp",
- "FrontEnd/TransactionHandler.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
+ "FrontEnd/LayerCreationArgs.cpp",
+ "FrontEnd/LayerHandle.cpp",
+ "FrontEnd/LayerHierarchy.cpp",
+ "FrontEnd/LayerLifecycleManager.cpp",
+ "FrontEnd/LayerSnapshot.cpp",
+ "FrontEnd/LayerSnapshotBuilder.cpp",
+ "FrontEnd/RequestedLayerState.cpp",
+ "FrontEnd/TransactionHandler.cpp",
"HdrLayerInfoReporter.cpp",
"HdrSdrRatioOverlay.cpp",
"Jank/JankTracker.cpp",
- "WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
"LayerProtoHelper.cpp",
@@ -234,10 +233,10 @@
"RenderArea.cpp",
"Scheduler/EventThread.cpp",
"Scheduler/FrameRateOverrideMappings.cpp",
- "Scheduler/OneShotTimer.cpp",
"Scheduler/LayerHistory.cpp",
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
+ "Scheduler/OneShotTimer.cpp",
"Scheduler/RefreshRateSelector.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SmallAreaDetectionAllowMappings.cpp",
@@ -253,19 +252,20 @@
"Tracing/LayerDataSource.cpp",
"Tracing/LayerTracing.cpp",
"Tracing/TransactionDataSource.cpp",
- "Tracing/TransactionTracing.cpp",
"Tracing/TransactionProtoParser.cpp",
+ "Tracing/TransactionTracing.cpp",
"Tracing/tools/LayerTraceGenerator.cpp",
"TransactionCallbackInvoker.cpp",
"TunnelModeEnabledReporter.cpp",
+ "WindowInfosListenerInvoker.cpp",
],
}
cc_defaults {
name: "libsurfaceflinger_binary",
defaults: [
- "surfaceflinger_defaults",
"libsurfaceflinger_production_defaults",
+ "surfaceflinger_defaults",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
@@ -331,9 +331,9 @@
"android.hardware.configstore@1.1",
"android.hardware.graphics.common@1.2",
"libhidlbase",
+ "liblog",
"libui",
"libutils",
- "liblog",
],
static_libs: [
"libSurfaceFlingerProperties",
@@ -354,10 +354,10 @@
generated_headers: ["statslog_surfaceflinger.h"],
export_generated_headers: ["statslog_surfaceflinger.h"],
shared_libs: [
+ "android.os.statsbootstrap_aidl-cpp",
"libbinder",
"libstatsbootstrap",
"libutils",
- "android.os.statsbootstrap_aidl-cpp",
],
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 7f94428..c47943f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -138,13 +138,13 @@
}
std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId,
- hal::Connection connection) {
- switch (connection) {
- case hal::Connection::CONNECTED:
+ HotplugEvent event) {
+ switch (event) {
+ case HotplugEvent::Connected:
return onHotplugConnect(hwcDisplayId);
- case hal::Connection::DISCONNECTED:
+ case HotplugEvent::Disconnected:
return onHotplugDisconnect(hwcDisplayId);
- case hal::Connection::INVALID:
+ case HotplugEvent::LinkUnstable:
return {};
}
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b1b997a..d60f6ff 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -231,11 +231,12 @@
// Events handling ---------------------------------------------------------
- // Returns stable display ID (and display name on connection of new or previously disconnected
- // display), or std::nullopt if hotplug event was ignored.
+ enum class HotplugEvent { Connected, Disconnected, LinkUnstable };
+
+ // Returns the stable display ID of the display for which the hotplug event was received, or
+ // std::nullopt if hotplug event was ignored.
// This function is called from SurfaceFlinger.
- virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId,
- hal::Connection) = 0;
+ virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, HotplugEvent) = 0;
// If true we'll update the DeviceProductInfo on subsequent hotplug connected events.
// TODO(b/157555476): Remove when the framework has proper support for headless mode
@@ -435,9 +436,7 @@
// Events handling ---------------------------------------------------------
- // Returns PhysicalDisplayId (and display name on connection of new or previously disconnected
- // display), or std::nullopt if hotplug event was ignored.
- std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
+ std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, HotplugEvent) override;
bool updatesDeviceProductInfoOnHotplugReconnect() const override;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 86d7388..fece312 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -611,7 +611,11 @@
mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
}
- if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ const nsecs_t presentThreshold =
+ FlagManager::getInstance().increase_missed_frame_jank_threshold()
+ ? mJankClassificationThresholds.presentThresholdExtended
+ : mJankClassificationThresholds.presentThresholdLegacy;
+ if (std::abs(presentDelta) > presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
// Jank that is missing by less than the render rate period is classified as partial jank,
@@ -629,9 +633,8 @@
} else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
// Finish on time, Present early
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= refreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold) {
+ if (deltaToVsync < presentThreshold ||
+ deltaToVsync >= refreshRate.getPeriodNsecs() - presentThreshold) {
// Delta factor of vsync
mJankType = JankType::SurfaceFlingerScheduling;
} else {
@@ -667,9 +670,8 @@
if (!(mJankType & JankType::BufferStuffing)) {
// In a stuffed state, if the app finishes on time and there is no display frame
// jank, only buffer stuffing is the root cause of the jank.
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= refreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold) {
+ if (deltaToVsync < presentThreshold ||
+ deltaToVsync >= refreshRate.getPeriodNsecs() - presentThreshold) {
// Delta factor of vsync
mJankType |= JankType::SurfaceFlingerScheduling;
} else {
@@ -1091,7 +1093,11 @@
? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
: 0;
- if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ nsecs_t presentThreshold = FlagManager::getInstance().increase_missed_frame_jank_threshold()
+ ? mJankClassificationThresholds.presentThresholdExtended
+ : mJankClassificationThresholds.presentThresholdLegacy;
+
+ if (std::abs(presentDelta) > presentThreshold) {
mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
: FramePresentMetadata::EarlyPresent;
// Jank that is missing by less than the render rate period is classified as partial jank,
@@ -1122,9 +1128,8 @@
if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
// Finish on time, Present early
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold)) {
+ if (deltaToVsync < presentThreshold ||
+ deltaToVsync >= (mRefreshRate.getPeriodNsecs() - presentThreshold)) {
// Delta is a factor of vsync if its within the presentTheshold on either side
// of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
// of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
@@ -1142,7 +1147,7 @@
}
} else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <=
- mJankClassificationThresholds.presentThreshold ||
+ presentThreshold ||
previousPresentTime > mSurfaceFlingerPredictions.presentTime) {
// The previous frame was either presented in the current frame's expected vsync or
// it was presented even later than the current frame's expected vsync.
@@ -1151,9 +1156,8 @@
if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish &&
!(mJankType & JankType::SurfaceFlingerStuffing)) {
// Finish on time, Present late
- if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
- deltaToVsync >= (mRefreshRate.getPeriodNsecs() -
- mJankClassificationThresholds.presentThreshold)) {
+ if (deltaToVsync < presentThreshold ||
+ deltaToVsync >= (mRefreshRate.getPeriodNsecs() - presentThreshold)) {
// Delta is a factor of vsync if its within the presentTheshold on either side
// of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
// of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
@@ -1165,8 +1169,7 @@
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
if (!(mJankType & JankType::SurfaceFlingerStuffing) ||
mSurfaceFlingerActuals.presentTime - previousPresentTime >
- mRefreshRate.getPeriodNsecs() +
- mJankClassificationThresholds.presentThreshold) {
+ mRefreshRate.getPeriodNsecs() + presentThreshold) {
// Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
// was presented more than a vsync late.
if (mGpuFence != FenceTime::NO_FENCE) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a47bd57..9fedb57 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -107,7 +107,10 @@
struct JankClassificationThresholds {
// The various thresholds for App and SF. If the actual timestamp falls within the threshold
// compared to prediction, we treat it as on time.
- nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ nsecs_t presentThresholdLegacy =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ nsecs_t presentThresholdExtended =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(4ms).count();
nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(0ms).count();
nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
};
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 7289e2f..86ef6ca 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -264,28 +264,21 @@
}
snapshot.isVisible = visible;
- if (FlagManager::getInstance().skip_invisible_windows_in_input()) {
- const bool visibleForInput =
- snapshot.isVisible || (snapshot.hasInputInfo() && !snapshot.isHiddenByPolicy());
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
- !visibleForInput);
- } else {
- // TODO(b/238781169) we are ignoring this compat for now, since we will have
- // to remove any optimization based on visibility.
+ // TODO(b/238781169) we are ignoring this compat for now, since we will have
+ // to remove any optimization based on visibility.
- // For compatibility reasons we let layers which can receive input
- // receive input before they have actually submitted a buffer. Because
- // of this we use canReceiveInput instead of isVisible to check the
- // policy-visibility, ignoring the buffer state. However for layers with
- // hasInputInfo()==false we can use the real visibility state.
- // We are just using these layers for occlusion detection in
- // InputDispatcher, and obviously if they aren't visible they can't occlude
- // anything.
- const bool visibleForInput =
- snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
- !visibleForInput);
- }
+ // For compatibility reasons we let layers which can receive input
+ // receive input before they have actually submitted a buffer. Because
+ // of this we use canReceiveInput instead of isVisible to check the
+ // policy-visibility, ignoring the buffer state. However for layers with
+ // hasInputInfo()==false we can use the real visibility state.
+ // We are just using these layers for occlusion detection in
+ // InputDispatcher, and obviously if they aren't visible they can't occlude
+ // anything.
+ const bool visibleForInput =
+ snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
+ snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+
LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
snapshot.getDebugString().c_str());
}
@@ -1185,7 +1178,7 @@
auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
if (!requested.hasInputInfo()) {
- snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL;
+ snapshot.inputInfo.inputConfig |= InputConfig::NO_INPUT_CHANNEL;
}
fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 591ebb2..1d53e71 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -573,7 +573,7 @@
return false;
}
- if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ if (hasBufferOrSidebandStream() || fillsColor()) {
return true;
}
@@ -586,6 +586,15 @@
windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
+bool RequestedLayerState::hasBufferOrSidebandStream() const {
+ return ((sidebandStream != nullptr) || (externalTexture != nullptr));
+}
+
+bool RequestedLayerState::fillsColor() const {
+ return !hasBufferOrSidebandStream() && color.r >= 0.0_hf && color.g >= 0.0_hf &&
+ color.b >= 0.0_hf;
+}
+
bool RequestedLayerState::hasBlur() const {
return backgroundBlurRadius > 0 || blurRegions.size() > 0;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index dd861a7..7232379 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -88,6 +88,8 @@
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
bool needsInputInfo() const;
+ bool hasBufferOrSidebandStream() const;
+ bool fillsColor() const;
bool hasBlur() const;
bool hasFrameUpdate() const;
bool hasReadyFrame() const;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5f79179..e1c5383 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -135,7 +135,6 @@
#include "DisplayDevice.h"
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/FramebufferSurface.h"
-#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
@@ -2292,12 +2291,12 @@
void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId,
DisplayHotplugEvent event) {
if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) {
- hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED)
- ? hal::Connection::CONNECTED
- : hal::Connection::DISCONNECTED;
+ const HWComposer::HotplugEvent hotplugEvent = event == DisplayHotplugEvent::CONNECTED
+ ? HWComposer::HotplugEvent::Connected
+ : HWComposer::HotplugEvent::Disconnected;
{
std::lock_guard<std::mutex> lock(mHotplugMutex);
- mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
+ mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, hotplugEvent});
}
if (mScheduler) {
@@ -3658,13 +3657,13 @@
events = std::move(mPendingHotplugEvents);
}
- for (const auto [hwcDisplayId, connection] : events) {
- if (auto info = getHwComposer().onHotplug(hwcDisplayId, connection)) {
+ for (const auto [hwcDisplayId, event] : events) {
+ if (auto info = getHwComposer().onHotplug(hwcDisplayId, event)) {
const auto displayId = info->id;
const ftl::Concat displayString("display ", displayId.value, "(HAL ID ", hwcDisplayId,
')');
- if (connection == hal::Connection::CONNECTED) {
+ if (event == HWComposer::HotplugEvent::Connected) {
const auto activeModeIdOpt =
processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
displayString.c_str());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 24de02a..a99b39a 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -75,6 +75,7 @@
#include "Display/VirtualDisplaySnapshot.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/HWComposer.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
#include "FrontEnd/DisplayInfo.h"
@@ -126,7 +127,6 @@
class FpsReporter;
class TunnelModeEnabledReporter;
class HdrLayerInfoReporter;
-class HWComposer;
class IGraphicBufferProducer;
class Layer;
class MessageBase;
@@ -1278,7 +1278,7 @@
struct HotplugEvent {
hal::HWDisplayId hwcDisplayId;
- hal::Connection connection = hal::Connection::INVALID;
+ HWComposer::HotplugEvent event;
};
bool mIsHdcpViaNegVsync = false;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 15df152..b1552e6 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -126,6 +126,7 @@
DUMP_ACONFIG_FLAG(adpf_native_session_manager);
DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
+ DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold);
DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
/// Trunk stable readonly flags ///
@@ -300,6 +301,7 @@
FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "")
FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
+FLAG_MANAGER_ACONFIG_FLAG(increase_missed_frame_jank_threshold, "");
/// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 147e79e..073302e 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -61,6 +61,7 @@
bool adpf_use_fmq_channel() const;
bool adpf_use_fmq_channel_fixed() const;
bool graphite_renderengine_preview_rollout() const;
+ bool increase_missed_frame_jank_threshold() const;
bool refresh_rate_overlay_on_external_display() const;
/// Trunk stable readonly flags ///
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index b28d269..96ab7ab 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -190,6 +190,13 @@
} # graphite_renderengine_preview_rollout
flag {
+ name: "increase_missed_frame_jank_threshold"
+ namespace: "core_graphics"
+ description: "Increase the jank threshold to 4 milliseconds"
+ bug: "342265411"
+} # increase_missed_frame_jank_threshold
+
+flag {
name: "latch_unsignaled_with_auto_refresh_changed"
namespace: "core_graphics"
description: "Ignore eAutoRefreshChanged with latch unsignaled"
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index c6cbe52..84dc5fc 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -67,7 +67,8 @@
setVsyncEnabled(kHwcDisplayId, hal::IComposerClient::Vsync::DISABLE));
EXPECT_CALL(*mComposerHal, onHotplugConnect(kHwcDisplayId));
- const auto infoOpt = mComposer->onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto infoOpt =
+ mComposer->onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(infoOpt);
mDisplayId = infoOpt->id;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 6e231aa..3fead93 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -360,9 +360,10 @@
// The HWC active configuration id
static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001;
- static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
+ static void injectPendingHotplugEvent(DisplayTransactionTest* test,
+ HWComposer::HotplugEvent event) {
test->mFlinger.mutablePendingHotplugEvents().emplace_back(
- TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection});
+ TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, event});
}
// Called by tests to inject a HWC display setup
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 08e4265..54f2259 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -202,10 +202,12 @@
uint32_t* maxDisplayFrames;
size_t maxTokens;
static constexpr pid_t kSurfaceFlingerPid = 666;
- static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count();
+ static constexpr nsecs_t kPresentThresholdLegacy = std::chrono::nanoseconds(2ns).count();
+ static constexpr nsecs_t kPresentThresholdExtended = std::chrono::nanoseconds(4ns).count();
static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(0ns).count();
static constexpr nsecs_t kStartThreshold = std::chrono::nanoseconds(2ns).count();
- static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
+ static constexpr JankClassificationThresholds kTestThresholds{kPresentThresholdLegacy,
+ kPresentThresholdExtended,
kDeadlineThreshold,
kStartThreshold};
};
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index ba2d3e2..b34de1a 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -94,7 +94,7 @@
constexpr hal::HWDisplayId kHwcDisplayId = 1;
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
ASSERT_FALSE(mHwc.isHeadless());
@@ -111,7 +111,7 @@
constexpr hal::HWDisplayId kHwcDisplayId = 1;
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
EXPECT_CALL(*mHal, getDisplayConnectionType(kHwcDisplayId, _))
@@ -133,7 +133,7 @@
constexpr hal::HWDisplayId kHwcDisplayId = 2;
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
{
@@ -164,7 +164,7 @@
constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
ASSERT_TRUE(info->preferredDetailedTimingDescriptor.has_value());
@@ -266,7 +266,7 @@
constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
@@ -364,7 +364,7 @@
constexpr hal::HWConfigId kConfigId = 42;
constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(true));
@@ -452,7 +452,7 @@
constexpr hal::HWDisplayId kHwcDisplayId = 1;
expectHotplugConnect(kHwcDisplayId);
- const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ const auto info = mHwc.onHotplug(kHwcDisplayId, HWComposer::HotplugEvent::Connected);
ASSERT_TRUE(info);
const auto physicalDisplayId = info->id;
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 976cecb..35ec536 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -619,14 +619,32 @@
}
}
-TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) {
- // By default the layer has no buffer, so it doesn't need an input info
- EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
-
- setBuffer(111);
+TEST_F(LayerLifecycleManagerTest, layerWithBufferNeedsInputInfo) {
+ // If a layer has no buffer or no color, it doesn't have an input info
+ LayerHierarchyTestBase::createRootLayer(3);
+ setColor(3, {-1._hf, -1._hf, -1._hf});
mLifecycleManager.commitChanges();
- EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+ EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 3)->needsInputInfo());
+
+ setBuffer(3);
+ mLifecycleManager.commitChanges();
+
+ EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 3)->needsInputInfo());
+}
+
+TEST_F(LayerLifecycleManagerTest, layerWithColorNeedsInputInfo) {
+ // If a layer has no buffer or no color, it doesn't have an input info
+ LayerHierarchyTestBase::createRootLayer(4);
+ setColor(4, {-1._hf, -1._hf, -1._hf});
+ mLifecycleManager.commitChanges();
+
+ EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 4)->needsInputInfo());
+
+ setColor(4, {1._hf, 0._hf, 0._hf});
+ mLifecycleManager.commitChanges();
+
+ EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 4)->needsInputInfo());
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index ab8c733..453c053 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -2045,17 +2045,17 @@
}
TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) {
- // By default the layer has no buffer, so we don't expect it to have an input info
+ // If a layer has no buffer or no color, it doesn't have an input info
+ setColor(111, {-1._hf, -1._hf, -1._hf});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 12, 121, 122, 1221, 13, 2});
EXPECT_FALSE(getSnapshot(111)->hasInputInfo());
setBuffer(111);
-
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_TRUE(getSnapshot(111)->hasInputInfo());
EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL));
- EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
}
// content dirty test
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 9bf344c..1335640 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -163,7 +163,7 @@
setupCommonPreconditions<Case>();
// A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// --------------------------------------------------------------------
// Call Expectations
@@ -197,7 +197,7 @@
setupCommonPreconditions<Case>();
// A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// --------------------------------------------------------------------
// Invocation
@@ -219,7 +219,7 @@
setupCommonPreconditions<Case>();
// A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
// The display is already completely set up.
Case::Display::injectHwcDisplay(this);
@@ -327,9 +327,10 @@
setupCommonPreconditions<Case>();
// A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// A hotplug disconnect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ Case::Display::injectPendingHotplugEvent(this,
+ HWComposer::HotplugEvent::Disconnected);
// --------------------------------------------------------------------
// Call Expectations
@@ -378,9 +379,10 @@
existing.inject();
// A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ Case::Display::injectPendingHotplugEvent(this,
+ HWComposer::HotplugEvent::Disconnected);
// A hotplug connect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ Case::Display::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// --------------------------------------------------------------------
// Call Expectations
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 4e7a174..2d986c6 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -41,9 +41,9 @@
const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
ASSERT_EQ(2u, pendingEvents.size());
EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
- EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
+ EXPECT_EQ(HWComposer::HotplugEvent::Connected, pendingEvents[0].event);
EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
- EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
+ EXPECT_EQ(HWComposer::HotplugEvent::Disconnected, pendingEvents[1].event);
}
TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
@@ -64,7 +64,7 @@
using PrimaryDisplay = InnerDisplayVariant;
PrimaryDisplay::setupHwcHotplugCallExpectations(this);
PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
- PrimaryDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// TODO(b/241286146): Remove this unnecessary call.
EXPECT_CALL(*mComposer,
@@ -80,7 +80,7 @@
using ExternalDisplay = ExternalDisplayWithIdentificationVariant;
ExternalDisplay::setupHwcHotplugCallExpectations(this);
ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// TODO(b/241286146): Remove this unnecessary call.
EXPECT_CALL(*mComposer,
@@ -123,7 +123,7 @@
using PrimaryDisplay = PrimaryDisplayVariant;
PrimaryDisplay::setupHwcHotplugCallExpectations(this);
PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
- PrimaryDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// TODO(b/241286146): Remove this unnecessary call.
EXPECT_CALL(*mComposer,
@@ -139,7 +139,7 @@
using ExternalDisplay = ExternalDisplayWithIdentificationVariant;
ExternalDisplay::setupHwcHotplugCallExpectations(this);
ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
// TODO(b/241286146): Remove this unnecessary call.
EXPECT_CALL(*mComposer,
@@ -206,15 +206,15 @@
// A single commit should be scheduled for both configure calls.
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
mFlinger.configure();
EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
// Disconnecting a display that was already disconnected should be a no-op.
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
mFlinger.configure();
// The display should be scheduled for removal during the next commit. At this point, it should
@@ -249,14 +249,14 @@
EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
mFlinger.configure();
// The hotplug should be rejected, so no HWComposer::DisplayData should be created.
EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
// Disconnecting a display that does not exist should be a no-op.
- ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
mFlinger.configure();
EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
index 7bd85cd..3fa4093 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -81,7 +81,7 @@
(PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
(override));
MOCK_METHOD(std::optional<DisplayIdentificationInfo>, onHotplug,
- (hal::HWDisplayId, hal::Connection), (override));
+ (hal::HWDisplayId, HWComposer::HotplugEvent), (override));
MOCK_METHOD(bool, updatesDeviceProductInfoOnHotplugReconnect, (), (const, override));
MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, hal::Vsync), (override));