Merge tag 'android-16.0.0_r1' of https://android.googlesource.com/platform/frameworks/native into HEAD
Android 16.0.0 release 1
Change-Id: Iacaf25353d3020d7edcd8b9b6c484a94d875105f
diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp
index d11631b..afcdf74 100644
--- a/services/audiomanager/Android.bp
+++ b/services/audiomanager/Android.bp
@@ -15,6 +15,7 @@
],
shared_libs: [
+ "av-types-aidl-cpp",
"libutils",
"libbinder",
"liblog",
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index f8a38d1..8db9a78 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -35,6 +35,24 @@
{
}
+ 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",
+ __func__,
+ ex);
+ 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>(binder);
+ LOG_ALWAYS_FATAL_IF(iface == nullptr, "%s failed unexpected interface", __func__);
+ return iface;
+ }
+
virtual audio_unique_id_t trackPlayer(player_type_t playerType, audio_usage_t usage,
audio_content_type_t content, const sp<IBinder>& player, audio_session_t sessionId) {
Parcel data, reply;
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index d205231..56c3b7d 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,10 +34,8 @@
sp<IBinder> displayToken = nullptr;
sp<SurfaceControl> surfaceControl = nullptr;
if (it == mDisplays.end()) {
- if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
- displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
- }
-
+ displayToken =
+ SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId::fromValue(id));
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
return nullptr;
@@ -67,7 +65,7 @@
std::swap(displayWidth, displayHeight);
}
- sp<android::SurfaceComposerClient> surfaceClient = new SurfaceComposerClient();
+ sp<android::SurfaceComposerClient> surfaceClient = sp<SurfaceComposerClient>::make();
err = surfaceClient->initCheck();
if (err != NO_ERROR) {
ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
@@ -160,11 +158,8 @@
HwDisplayConfig activeConfig;
HwDisplayState activeState;
- sp<IBinder> displayToken;
- if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
- displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
- }
-
+ sp<IBinder> displayToken =
+ SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId::fromValue(id));
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
} else {
@@ -197,4 +192,3 @@
} // namespace automotive
} // namespace frameworks
} // namespace android
-
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index 5c7f344..e96b17a 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -2,3 +2,4 @@
class hal
user graphics
group automotive_evs
+ disabled
diff --git a/services/displayservice/DisplayEventReceiver.cpp b/services/displayservice/DisplayEventReceiver.cpp
index 2bb74c2..9927fb6 100644
--- a/services/displayservice/DisplayEventReceiver.cpp
+++ b/services/displayservice/DisplayEventReceiver.cpp
@@ -22,6 +22,7 @@
#include <android/frameworks/displayservice/1.0/BpHwEventCallback.h>
#include <thread>
+#include <ftl/enum.h>
namespace android {
namespace frameworks {
@@ -97,11 +98,11 @@
for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
const FwkReceiver::Event &event = buf[i];
- uint32_t type = event.header.type;
+ android::DisplayEventType type = event.header.type;
uint64_t timestamp = event.header.timestamp;
switch(buf[i].header.type) {
- case FwkReceiver::DISPLAY_EVENT_VSYNC: {
+ case DisplayEventType::DISPLAY_EVENT_VSYNC: {
auto ret = mCallback->onVsync(timestamp, event.vsync.count);
if (!ret.isOk()) {
LOG(ERROR) << "AttachedEvent handleEvent fails on onVsync callback"
@@ -109,7 +110,7 @@
return 0; // remove the callback
}
} break;
- case FwkReceiver::DISPLAY_EVENT_HOTPLUG: {
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG: {
auto ret = mCallback->onHotplug(timestamp, event.hotplug.connected);
if (!ret.isOk()) {
LOG(ERROR) << "AttachedEvent handleEvent fails on onHotplug callback"
@@ -118,7 +119,8 @@
}
} break;
default: {
- LOG(ERROR) << "AttachedEvent handleEvent unknown type: " << type;
+ LOG(ERROR) << "AttachedEvent handleEvent unknown type: "
+ << ftl::to_underlying(type);
}
}
}
diff --git a/services/displayservice/OWNERS b/services/displayservice/OWNERS
index 7a3e4c2..40164aa 100644
--- a/services/displayservice/OWNERS
+++ b/services/displayservice/OWNERS
@@ -1,2 +1 @@
smoreland@google.com
-lpy@google.com
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index ca9fe5e..74e354f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -7,6 +7,13 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "gpuservice_flags",
+ package: "com.android.frameworks.gpuservice.flags",
+ container: "system",
+ srcs: ["gpuservice_flags.aconfig"],
+}
+
cc_defaults {
name: "gpuservice_defaults",
cflags: [
@@ -19,10 +26,22 @@
],
}
+cc_aconfig_library {
+ name: "gpuservice_multiuser_flags_c_lib",
+ aconfig_declarations: "gpuservice_flags",
+}
+
+cc_aconfig_library {
+ name: "gpuservice_flags_c_lib",
+ aconfig_declarations: "graphicsenv_flags",
+}
+
cc_defaults {
name: "libgpuservice_defaults",
defaults: [
+ "aconfig_lib_cc_static_link.defaults",
"gpuservice_defaults",
+ "libfeatureoverride_deps",
"libgfxstats_deps",
"libgpumem_deps",
"libgpumemtracer_deps",
@@ -40,8 +59,11 @@
"libgraphicsenv",
"liblog",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
+ "gpuservice_flags_c_lib",
+ "libfeatureoverride",
"libgfxstats",
"libgpumem",
"libgpumemtracer",
@@ -83,6 +105,9 @@
srcs: [
":libgpuservice_sources",
],
+ shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
+ ],
}
cc_defaults {
@@ -117,4 +142,7 @@
static_libs: [
"libgpuservice",
],
+ shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
+ ],
}
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index fadb1fd..46327df 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,7 +24,11 @@
#include <binder/IResultReceiver.h>
#include <binder/Parcel.h>
#include <binder/PermissionCache.h>
+#include <com_android_frameworks_gpuservice_flags.h>
+#include <com_android_graphics_graphicsenv_flags.h>
#include <cutils/properties.h>
+#include <cutils/multiuser.h>
+#include <feature_override/FeatureOverrideParser.h>
#include <gpumem/GpuMem.h>
#include <gpuwork/GpuWork.h>
#include <gpustats/GpuStats.h>
@@ -38,6 +42,9 @@
#include <thread>
#include <memory>
+namespace gpuservice_flags = com::android::frameworks::gpuservice::flags;
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
+
namespace android {
using base::StringAppendF;
@@ -113,11 +120,22 @@
// only system_server with the ACCESS_GPU_SERVICE permission is allowed to set
// persist.graphics.egl
- if (uid != AID_SYSTEM ||
- !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
- ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
+ if (gpuservice_flags::multiuser_permission_check()) {
+ // retrieve the appid of Settings app on multiuser builds
+ const int multiuserappid = multiuser_get_app_id(uid);
+ if (multiuserappid != AID_SYSTEM ||
+ !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
+ ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
+ "pid=%d, uid=%d\n, multiuserappid=%d", pid, uid, multiuserappid);
+ return;
+ }
+ } else {
+ if (uid != AID_SYSTEM ||
+ !PermissionCache::checkPermission(sAccessGpuServicePermission, pid, uid)) {
+ ALOGE("Permission Denial: can't set persist.graphics.egl from setAngleAsSystemDriver() "
"pid=%d, uid=%d\n", pid, uid);
- return;
+ return;
+ }
}
std::lock_guard<std::mutex> lock(mLock);
@@ -128,6 +146,14 @@
}
}
+FeatureOverrides GpuService::getFeatureOverrides() {
+ if (!graphicsenv_flags::angle_feature_overrides()) {
+ FeatureOverrides featureOverrides;
+ return featureOverrides;
+ }
+
+ return mFeatureOverrideParser.getFeatureOverrides();
+}
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
IPCThreadState* ipc = IPCThreadState::self();
@@ -156,7 +182,11 @@
for (size_t i = 0, n = args.size(); i < n; i++)
ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str());
- if (args.size() >= 1) {
+ if (!args.empty()) {
+ if (graphicsenv_flags::angle_feature_overrides()) {
+ if (args[0] == String16("featureOverrides"))
+ return cmdFeatureOverrides(out, err);
+ }
if (args[0] == String16("vkjson")) return cmdVkjson(out, err);
if (args[0] == String16("vkprofiles")) return cmdVkprofiles(out, err);
if (args[0] == String16("help")) return cmdHelp(out);
@@ -220,6 +250,11 @@
return NO_ERROR;
}
+status_t GpuService::cmdFeatureOverrides(int out, int /*err*/) {
+ dprintf(out, "%s\n", mFeatureOverrideParser.getFeatureOverrides().toString().c_str());
+ return NO_ERROR;
+}
+
namespace {
status_t cmdHelp(int out) {
@@ -232,6 +267,10 @@
"GPU Service commands:\n"
" vkjson dump Vulkan properties as JSON\n"
" vkprofiles print support for select Vulkan profiles\n");
+ if (graphicsenv_flags::angle_feature_overrides()) {
+ fprintf(outs,
+ " featureOverrides update and output gpuservice's feature overrides\n");
+ }
fclose(outs);
return NO_ERROR;
}
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..842a0c4
--- /dev/null
+++ b/services/gpuservice/feature_override/Android.bp
@@ -0,0 +1,100 @@
+// 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",
+ "libprotobuf-cpp-lite",
+ ],
+}
+
+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",
+ "libvkjson_deps",
+ ],
+ srcs: [
+ ":feature_config_proto_definitions",
+ "FeatureOverrideParser.cpp",
+ ],
+ local_include_dirs: [
+ "include",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wimplicit-fallthrough",
+ ],
+ cppflags: [
+ "-Wno-sign-compare",
+ ],
+ static_libs: [
+ "libvkjson",
+ ],
+ 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..26ff84a
--- /dev/null
+++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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 <android-base/macros.h>
+#include <graphicsenv/FeatureOverrides.h>
+#include <log/log.h>
+#include <vkjson.h>
+
+#include "feature_config.pb.h"
+
+namespace {
+
+void resetFeatureOverrides(android::FeatureOverrides &featureOverrides) {
+ featureOverrides.mGlobalFeatures.clear();
+ featureOverrides.mPackageFeatures.clear();
+}
+
+bool
+gpuVendorIdMatches(const VkJsonInstance &vkJsonInstance,
+ const uint32_t &configVendorId) {
+ if (vkJsonInstance.devices.empty()) {
+ return false;
+ }
+
+ // Always match the TEST Vendor ID
+ if (configVendorId == feature_override::GpuVendorID::VENDOR_ID_TEST) {
+ return true;
+ }
+
+ // Always assume one GPU device.
+ uint32_t vendorID = vkJsonInstance.devices.front().properties.vendorID;
+
+ return vendorID == configVendorId;
+}
+
+bool
+conditionsMet(const VkJsonInstance &vkJsonInstance,
+ const android::FeatureConfig &featureConfig) {
+ bool gpuVendorIdMatch = false;
+
+ if (featureConfig.mGpuVendorIDs.empty()) {
+ gpuVendorIdMatch = true;
+ } else {
+ for (const auto &gpuVendorID: featureConfig.mGpuVendorIDs) {
+ if (gpuVendorIdMatches(vkJsonInstance, gpuVendorID)) {
+ gpuVendorIdMatch = true;
+ break;
+ }
+ }
+ }
+
+ return gpuVendorIdMatch;
+}
+
+void initFeatureConfig(android::FeatureConfig &featureConfig,
+ const feature_override::FeatureConfig &featureConfigProto) {
+ featureConfig.mFeatureName = featureConfigProto.feature_name();
+ featureConfig.mEnabled = featureConfigProto.enabled();
+ for (const auto &gpuVendorIdProto: featureConfigProto.gpu_vendor_ids()) {
+ featureConfig.mGpuVendorIDs.emplace_back(static_cast<uint32_t>(gpuVendorIdProto));
+ }
+}
+
+feature_override::FeatureOverrideProtos readFeatureConfigProtos(const 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();
+
+ std::ifstream configFile(configFilePath);
+ if (!configFile.good()) {
+ return false;
+ }
+
+ struct stat fileStat{};
+ if (stat(configFilePath.c_str(), &fileStat) != 0) {
+ ALOGE("Error getting file information for '%s': %s", configFilePath.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() {
+ mLastProtobufReadTime = 0;
+}
+
+void FeatureOverrideParser::parseFeatureOverrides() {
+ const feature_override::FeatureOverrideProtos overridesProtos = readFeatureConfigProtos(
+ getFeatureOverrideFilePath());
+
+ // Clear out the stale values before adding the newly parsed data.
+ resetFeatureOverrides(mFeatureOverrides);
+
+ if (overridesProtos.global_features().empty() &&
+ overridesProtos.package_features().empty()) {
+ // No overrides to parse.
+ return;
+ }
+
+ const VkJsonInstance vkJsonInstance = VkJsonGetInstance();
+
+ // Global feature overrides.
+ for (const auto &featureConfigProto: overridesProtos.global_features()) {
+ FeatureConfig featureConfig;
+ initFeatureConfig(featureConfig, featureConfigProto);
+
+ if (conditionsMet(vkJsonInstance, featureConfig)) {
+ 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);
+
+ if (conditionsMet(vkJsonInstance, featureConfig)) {
+ 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..f285187
--- /dev/null
+++ b/services/gpuservice/feature_override/proto/feature_config.proto
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+/**
+ * GPU Vendor IDs.
+ * Taken from: external/angle/src/libANGLE/renderer/driver_utils.h
+ */
+enum GpuVendorID
+{
+ // Test ID matches every GPU Vendor ID.
+ VENDOR_ID_TEST = 0;
+ VENDOR_ID_AMD = 0x1002;
+ VENDOR_ID_ARM = 0x13B5;
+ // Broadcom devices won't use PCI, but this is their Vulkan vendor id.
+ VENDOR_ID_BROADCOM = 0x14E4;
+ VENDOR_ID_GOOGLE = 0x1AE0;
+ VENDOR_ID_INTEL = 0x8086;
+ VENDOR_ID_MESA = 0x10005;
+ VENDOR_ID_MICROSOFT = 0x1414;
+ VENDOR_ID_NVIDIA = 0x10DE;
+ VENDOR_ID_POWERVR = 0x1010;
+ // This is Qualcomm PCI Vendor ID.
+ // Android doesn't have a PCI bus, but all we need is a unique id.
+ VENDOR_ID_QUALCOMM = 0x5143;
+ VENDOR_ID_SAMSUNG = 0x144D;
+ VENDOR_ID_VIVANTE = 0x9999;
+ VENDOR_ID_VMWARE = 0x15AD;
+ VENDOR_ID_VIRTIO = 0x1AF4;
+}
+
+/**
+ * Feature Configuration
+ * feature_name: Feature name (see external/angle/include/platform/autogen/FeaturesVk_autogen.h).
+ * enabled: Either enable or disable the feature.
+ * gpu_vendor_ids: The GPU architectures this FeatureConfig applies to, if any.
+ */
+message FeatureConfig
+{
+ string feature_name = 1;
+ bool enabled = 2;
+ repeated GpuVendorID gpu_vendor_ids = 3;
+}
+
+/**
+ * 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/gpuservice_flags.aconfig b/services/gpuservice/gpuservice_flags.aconfig
new file mode 100644
index 0000000..be6a7bb
--- /dev/null
+++ b/services/gpuservice/gpuservice_flags.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.frameworks.gpuservice.flags"
+container: "system"
+
+flag {
+ name: "multiuser_permission_check"
+ namespace: "gpu"
+ description: "Whether to consider headless system user mode/multiuser when checking toggleAngleAsSystemDriver permission."
+ bug: "389867658"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 3072885..22be9a7 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -19,6 +19,8 @@
#include <binder/IInterface.h>
#include <cutils/compiler.h>
+#include <feature_override/FeatureOverrideParser.h>
+#include <graphicsenv/FeatureOverrides.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <graphicsenv/IGpuService.h>
#include <serviceutils/PriorityDumper.h>
@@ -63,6 +65,7 @@
const uint64_t* values, const uint32_t valueCount) override;
void setUpdatableDriverPath(const std::string& driverPath) override;
std::string getUpdatableDriverPath() override;
+ FeatureOverrides getFeatureOverrides() override;
void toggleAngleAsSystemDriver(bool enabled) override;
void addVulkanEngineName(const std::string& appPackageName, const uint64_t driverVersionCode,
const char *engineName) override;
@@ -85,6 +88,8 @@
status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+ status_t cmdFeatureOverrides(int out, int /*err*/);
+
/*
* Attributes
*/
@@ -96,6 +101,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/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp
index d4d48c4..7be3253 100644
--- a/services/gpuservice/tests/fuzzers/Android.bp
+++ b/services/gpuservice/tests/fuzzers/Android.bp
@@ -13,6 +13,9 @@
"libgpuservice",
"liblog",
],
+ shared_libs: [
+ "gpuservice_multiuser_flags_c_lib",
+ ],
fuzz_config: {
cc: [
"paulthomson@google.com",
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 8056a2c..0dac24d 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -21,20 +21,75 @@
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: [
+ "gpuservice_multiuser_flags_c_lib",
"libbase",
"libbinder",
"libbpf_bcc",
@@ -48,10 +103,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..66556cd
--- /dev/null
+++ b/services/gpuservice/tests/unittests/FeatureOverrideParserTest.cpp
@@ -0,0 +1,468 @@
+/*
+ * 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 = 3;
+ if (overrides.mGlobalFeatures.size() != expectedGlobalFeaturesSize) {
+ return testing::AssertionFailure()
+ << "overrides.mGlobalFeatures.size(): " << overrides.mGlobalFeatures.size()
+ << ", expected: " << expectedGlobalFeaturesSize;
+ }
+
+ size_t expectedPackageFeaturesSize = 3;
+ 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 validateGlobalOverrides2(FeatureOverrides overrides) {
+ const int kTestFeatureIndex = 1;
+ const std::string expectedFeatureName = "globalOverrides2";
+ const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = true;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x13B5, // GpuVendorID::VENDOR_ID_ARM
+ };
+ if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides2) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ EXPECT_TRUE(validateGlobalOverrides2(overrides));
+}
+
+testing::AssertionResult validateGlobalOverrides3(FeatureOverrides overrides) {
+ const int kTestFeatureIndex = 2;
+ const std::string expectedFeatureName = "globalOverrides3";
+ const FeatureConfig &cfg = overrides.mGlobalFeatures[kTestFeatureIndex];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = true;
+ if (cfg.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x8086, // GpuVendorID::VENDOR_ID_INTEL
+ };
+ if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, globalOverrides3) {
+FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+EXPECT_TRUE(validateGlobalOverrides3(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];
+
+ if (cfg.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ 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();
+}
+
+testing::AssertionResult validatePackageOverrides2(FeatureOverrides overrides) {
+ const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides2";
+
+ if (!overrides.mPackageFeatures.count(expectedPackageName)) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures missing expected package: " << expectedPackageName;
+ }
+
+ const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName];
+
+ size_t expectedFeaturesSize = 1;
+ if (features.size() != expectedFeaturesSize) {
+ return testing::AssertionFailure()
+ << "features.size(): " << features.size()
+ << ", expectedFeaturesSize: " << expectedFeaturesSize;
+ }
+
+ const std::string expectedFeatureName = "packageOverrides2";
+ const FeatureConfig &cfg = features[0];
+
+ 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;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x8086, // GpuVendorID::VENDOR_ID_INTEL
+ };
+ if (cfg.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides2) {
+ FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+ EXPECT_TRUE(validatePackageOverrides2(overrides));
+}
+
+testing::AssertionResult validatePackageOverrides3(FeatureOverrides overrides) {
+ const std::string expectedPackageName = "com.gpuservice_unittest.packageOverrides3";
+
+ if (!overrides.mPackageFeatures.count(expectedPackageName)) {
+ return testing::AssertionFailure()
+ << "overrides.mPackageFeatures missing expected package: " << expectedPackageName;
+ }
+
+ const std::vector<FeatureConfig>& features = overrides.mPackageFeatures[expectedPackageName];
+
+ size_t expectedFeaturesSize = 2;
+ if (features.size() != expectedFeaturesSize) {
+ return testing::AssertionFailure()
+ << "features.size(): " << features.size()
+ << ", expectedFeaturesSize: " << expectedFeaturesSize;
+ }
+
+ std::string expectedFeatureName = "packageOverrides3_1";
+ const FeatureConfig &cfg_1 = features[0];
+
+ if (cfg_1.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg_1.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ bool expectedEnabled = false;
+ if (cfg_1.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg_1.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ std::vector<uint32_t> expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x13B5, // GpuVendorID::VENDOR_ID_ARM
+ };
+ if (cfg_1.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg_1.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg_1.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_1.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ expectedFeatureName = "packageOverrides3_2";
+ const FeatureConfig &cfg_2 = features[1];
+
+ if (cfg_2.mFeatureName != expectedFeatureName) {
+ return testing::AssertionFailure()
+ << "cfg.mFeatureName: " << cfg_2.mFeatureName
+ << ", expected: " << expectedFeatureName;
+ }
+
+ expectedEnabled = true;
+ if (cfg_2.mEnabled != expectedEnabled) {
+ return testing::AssertionFailure()
+ << "cfg.mEnabled: " << cfg_2.mEnabled
+ << ", expected: " << expectedEnabled;
+ }
+
+ expectedGpuVendorIDs = {
+ 0, // GpuVendorID::VENDOR_ID_TEST
+ 0x8086, // GpuVendorID::VENDOR_ID_INTEL
+ };
+ if (cfg_2.mGpuVendorIDs.size() != expectedGpuVendorIDs.size()) {
+ return testing::AssertionFailure()
+ << "cfg.mGpuVendorIDs.size(): " << cfg_2.mGpuVendorIDs.size()
+ << ", expected: " << expectedGpuVendorIDs.size();
+ }
+ for (int i = 0; i < expectedGpuVendorIDs.size(); i++) {
+ if (cfg_2.mGpuVendorIDs[i] != expectedGpuVendorIDs[i]) {
+ std::stringstream msg;
+ msg << "cfg.mGpuVendorIDs[" << i << "]: 0x" << std::hex << cfg_2.mGpuVendorIDs[i]
+ << ", expected: 0x" << std::hex << expectedGpuVendorIDs[i];
+ return testing::AssertionFailure() << msg.str();
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+TEST_F(FeatureOverrideParserTest, packageOverrides3) {
+FeatureOverrides overrides = mFeatureOverrideParser.getFeatureOverrides();
+
+EXPECT_TRUE(validatePackageOverrides3(overrides));
+}
+
+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..44a6f78
--- /dev/null
+++ b/services/gpuservice/tests/unittests/data/feature_config_test.txtpb
@@ -0,0 +1,90 @@
+# 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
+ },
+ {
+ feature_name: "globalOverrides2"
+ enabled: True
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_ARM
+ ]
+ },
+ {
+ feature_name: "globalOverrides3"
+ enabled: True
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_INTEL
+ ]
+ }
+]
+
+# 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
+ }
+ ]
+ },
+ {
+ package_name: "com.gpuservice_unittest.packageOverrides2"
+ feature_configs: [
+ {
+ feature_name: "packageOverrides2"
+ enabled: False
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_INTEL
+ ]
+ }
+ ]
+ },
+ {
+ package_name: "com.gpuservice_unittest.packageOverrides3"
+ feature_configs: [
+ {
+ feature_name: "packageOverrides3_1"
+ enabled: False
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_ARM
+ ]
+ },
+ {
+ feature_name: "packageOverrides3_2"
+ enabled: True
+ gpu_vendor_ids: [
+ VENDOR_ID_TEST, # Match every GPU Vendor ID, so the feature isn't dropped when parsed.
+ VENDOR_ID_INTEL
+ ]
+ }
+ ]
+ }
+]
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/gpuservice/vts/OWNERS b/services/gpuservice/vts/OWNERS
index a63de1c..13a089f 100644
--- a/services/gpuservice/vts/OWNERS
+++ b/services/gpuservice/vts/OWNERS
@@ -1,8 +1,5 @@
# Bug component: 653544
kocdemir@google.com
paulthomson@google.com
-pbaiget@google.com
-lfy@google.com
chrisforbes@google.com
-lpy@google.com
alecmouri@google.com
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 5c12323..fc0aef4 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -39,29 +39,9 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public class GpuWorkTracepointTest extends BaseHostJUnit4Test {
- private static final String CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH =
- "/sys/kernel/tracing/events/power/cpu_frequency/format";
private static final String GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH =
"/sys/kernel/tracing/events/power/gpu_work_period/format";
- @Test
- public void testReadTracingEvents() throws Exception {
- // Test |testGpuWorkPeriodTracepointFormat| is dependent on whether certain tracepoint
- // paths exist. This means the test will vacuously pass if the tracepoint file system is
- // inaccessible. Thus, as a basic check, we make sure the CPU frequency tracepoint format
- // is accessible. If not, something is probably fundamentally broken about the tracing
- // file system.
- CommandResult commandResult = getDevice().executeShellV2Command(
- String.format("cat %s", CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH));
-
- assertEquals(String.format(
- "Failed to read \"%s\". This probably means that the tracing file system "
- + "is fundamentally broken in some way, possibly due to bad "
- + "permissions.",
- CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH),
- commandResult.getStatus(), CommandStatus.SUCCESS);
- }
-
@VsrTest(requirements={"VSR-3.3-004"})
@RequiresDevice
@Test
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index ca92ab5..107fd20 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -32,15 +32,15 @@
host_supported: true,
cpp_std: "c++20",
cflags: [
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
"-Wall",
- "-Wextra",
"-Werror",
+ "-Wextra",
"-Wno-unused-parameter",
- "-Wthread-safety",
"-Wshadow",
"-Wshadow-field-in-constructor-modified",
"-Wshadow-uncaptured-local",
- "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-Wthread-safety",
],
sanitize: {
misc_undefined: [
@@ -62,8 +62,8 @@
memtag_heap: true,
undefined: true,
misc_undefined: [
- "bounds",
"all",
+ "bounds",
],
},
},
@@ -114,16 +114,16 @@
"liblog",
"libprotobuf-cpp-lite",
"libstatslog",
- "libutils",
"libstatspull",
"libstatssocket",
+ "libutils",
"packagemanager_aidl-cpp",
"server_configurable_flags",
],
static_libs: [
"libattestation",
- "libperfetto_client_experimental",
"libpalmrejection",
+ "libperfetto_client_experimental",
"libui-types",
],
generated_headers: [
@@ -161,10 +161,10 @@
shared_libs: [
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
+ "libPlatformProperties",
"libinputflinger_base",
"libinputreader",
"libinputreporter",
- "libPlatformProperties",
],
static_libs: [
"libinputdispatcher",
@@ -185,8 +185,8 @@
name: "libinputflinger_headers",
host_supported: true,
export_include_dirs: [
- "include",
".",
+ "include",
],
header_libs: [
"libchrome-gestures_headers",
@@ -247,49 +247,40 @@
phony {
name: "checkinput",
required: [
- // native targets
- "libgui_test",
- "libinput",
- "libinputreader_static",
- "libinputflinger",
- "inputflinger_tests",
- "inputflinger_benchmarks",
- "libinput_tests",
- "libpalmrejection_test",
- "libandroid_runtime",
- "libinputservice_test",
"Bug-115739809",
- "StructLayout_test",
-
- // jni
- "libservices.core",
-
- // rust targets
- "libinput_rust_test",
-
- // native fuzzers
- "inputflinger_latencytracker_fuzzer",
- "inputflinger_cursor_input_fuzzer",
- "inputflinger_keyboard_input_fuzzer",
- "inputflinger_multitouch_input_fuzzer",
- "inputflinger_switch_input_fuzzer",
- "inputflinger_touchpad_input_fuzzer",
- "inputflinger_input_reader_fuzzer",
- "inputflinger_blocking_queue_fuzzer",
- "inputflinger_input_classifier_fuzzer",
- "inputflinger_input_dispatcher_fuzzer",
-
- // Java/Kotlin targets
- "CtsWindowManagerDeviceWindow",
- "InputTests",
"CtsHardwareTestCases",
"CtsInputTestCases",
+ "CtsSecurityBulletinHostTestCases",
+ "CtsSecurityTestCases",
"CtsViewTestCases",
"CtsWidgetTestCases",
+ "CtsWindowManagerDeviceWindow",
"FrameworksCoreTests",
"FrameworksServicesTests",
- "CtsSecurityTestCases",
- "CtsSecurityBulletinHostTestCases",
+ "InputTests",
+ "StructLayout_test",
+ "inputflinger_benchmarks",
+ "inputflinger_blocking_queue_fuzzer",
+ "inputflinger_cursor_input_fuzzer",
+ "inputflinger_input_classifier_fuzzer",
+ "inputflinger_input_dispatcher_fuzzer",
+ "inputflinger_input_reader_fuzzer",
+ "inputflinger_keyboard_input_fuzzer",
+ "inputflinger_latencytracker_fuzzer",
+ "inputflinger_multitouch_input_fuzzer",
+ "inputflinger_switch_input_fuzzer",
+ "inputflinger_tests",
+ "inputflinger_touchpad_input_fuzzer",
+ "libandroid_runtime",
+ "libgui_test",
+ "libinput",
+ "libinput_rust_test",
+ "libinput_tests",
+ "libinputflinger",
+ "libinputreader_static",
+ "libinputservice_test",
+ "libpalmrejection_test",
+ "libservices.core",
"monkey_test",
],
}
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 2ef94fb..bb4e617 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -56,7 +56,7 @@
void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
mDeviceInfos.clear();
mDeviceInfos.reserve(args.inputDeviceInfos.size());
- for (auto info : args.inputDeviceInfos) {
+ for (const auto& info : args.inputDeviceInfos) {
AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back();
aidlInfo.deviceId = info.getId();
aidlInfo.external = info.isExternal();
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index b155122..7d62ed9 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -41,8 +41,6 @@
const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
-const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
-
int32_t exceptionCodeFromStatusT(status_t status) {
switch (status) {
case OK:
@@ -134,12 +132,10 @@
mTracingStages.emplace_back(
std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
- if (ENABLE_INPUT_FILTER_RUST) {
- mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust,
- inputFilterPolicy);
- mTracingStages.emplace_back(
- std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
- }
+ mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust,
+ inputFilterPolicy);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("InputFilter", *mInputFilter));
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
@@ -250,10 +246,8 @@
mCollector->dump(dump);
dump += '\n';
}
- if (ENABLE_INPUT_FILTER_RUST) {
- mInputFilter->dump(dump);
- dump += '\n';
- }
+ mInputFilter->dump(dump);
+ dump += '\n';
mDispatcher->dump(dump);
dump += '\n';
}
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index b2680a2..3de639f 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -206,4 +206,9 @@
return std::visit(toStringVisitor, args);
}
+std::ostream& operator<<(std::ostream& out, const NotifyArgs& args) {
+ out << toString(args);
+ return out;
+}
+
} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 811692f..98f0f34 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -17,10 +17,13 @@
#define LOG_TAG "PointerChoreographer"
#include <android-base/logging.h>
+#include <android/configuration.h>
#include <com_android_input_flags.h>
+#include <algorithm>
#if defined(__ANDROID__)
#include <gui/SurfaceComposerClient.h>
#endif
+#include <input/InputFlags.h>
#include <input/Keyboard.h>
#include <input/PrintTools.h>
#include <unordered_set>
@@ -28,42 +31,12 @@
#include "PointerChoreographer.h"
#define INDENT " "
+#define INDENT2 " "
namespace android {
namespace {
-bool isFromMouse(const NotifyMotionArgs& args) {
- return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
- args.pointerProperties[0].toolType == ToolType::MOUSE;
-}
-
-bool isFromTouchpad(const NotifyMotionArgs& args) {
- return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
- args.pointerProperties[0].toolType == ToolType::FINGER;
-}
-
-bool isFromDrawingTablet(const NotifyMotionArgs& args) {
- return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
- isStylusToolType(args.pointerProperties[0].toolType);
-}
-
-bool isHoverAction(int32_t action) {
- return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
-}
-
-bool isStylusHoverEvent(const NotifyMotionArgs& args) {
- return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
-}
-
-bool isMouseOrTouchpad(uint32_t sources) {
- // Check if this is a mouse or touchpad, but not a drawing tablet.
- return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
- (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
- !isFromSource(sources, AINPUT_SOURCE_STYLUS));
-}
-
inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change,
PointerChoreographerPolicyInterface& policy) {
if (!change) {
@@ -98,6 +71,33 @@
return privacySensitiveDisplays;
}
+vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationViewport,
+ float pointerOffset,
+ DisplayTopologyPosition sourceBoundary) {
+ // destination is opposite of the source boundary
+ switch (sourceBoundary) {
+ case DisplayTopologyPosition::RIGHT:
+ return {0, pointerOffset}; // left edge
+ case DisplayTopologyPosition::TOP:
+ return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
+ case DisplayTopologyPosition::LEFT:
+ return {destinationViewport.logicalRight, pointerOffset}; // right edge
+ case DisplayTopologyPosition::BOTTOM:
+ return {pointerOffset, 0}; // top edge
+ }
+}
+
+// The standardised medium display density for which 1 px = 1 dp
+constexpr int32_t DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
+
+inline float pxToDp(int px, int dpi) {
+ return static_cast<float>(px * DENSITY_MEDIUM) / static_cast<float>(dpi);
+}
+
+inline int dpToPx(float dp, int dpi) {
+ return static_cast<int>((dp * dpi) / DENSITY_MEDIUM);
+}
+
} // namespace
// --- PointerChoreographer ---
@@ -133,10 +133,11 @@
}),
mNextListener(listener),
mPolicy(policy),
- mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
+ mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID),
mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false),
+ mPointerMotionFilterEnabled(false),
mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
mIsWindowInfoListenerRegistered(false),
mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
@@ -208,15 +209,16 @@
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
std::scoped_lock _l(getLock());
- if (isFromMouse(args)) {
+ if (isFromMouse(args.source, args.pointerProperties[0].toolType)) {
newArgs = processMouseEventLocked(args);
pointerDisplayChange = calculatePointerDisplayChangeToNotify();
- } else if (isFromTouchpad(args)) {
+ } else if (isFromTouchpad(args.source, args.pointerProperties[0].toolType)) {
newArgs = processTouchpadEventLocked(args);
pointerDisplayChange = calculatePointerDisplayChangeToNotify();
- } else if (isFromDrawingTablet(args)) {
+ } else if (isFromDrawingTablet(args.source, args.pointerProperties[0].toolType)) {
processDrawingTabletEventLocked(args);
- } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+ } else if (mStylusPointerIconEnabled &&
+ isStylusHoverEvent(args.source, args.pointerProperties, args.action)) {
processStylusHoverEventLocked(args);
} else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
processTouchscreenAndStylusEventLocked(args);
@@ -294,9 +296,11 @@
PointerControllerInterface& pc) {
const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-
- vec2 unconsumedDelta = pc.move(deltaX, deltaY);
- if (com::android::input::flags::connected_displays_cursor() &&
+ vec2 filteredDelta =
+ filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY},
+ newArgs.displayId);
+ vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y);
+ if (InputFlags::connectedDisplaysCursorEnabled() &&
(std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
handleUnconsumedDeltaLocked(pc, unconsumedDelta);
// pointer may have moved to a different viewport
@@ -327,19 +331,19 @@
// except sometimes near the corners.
// In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
// the destination display for the same reason.
- DisplayPosition sourceBoundary;
+ DisplayTopologyPosition sourceBoundary;
float cursorOffset = 0.0f;
if (rotatedUnconsumedDelta.x > 0) {
- sourceBoundary = DisplayPosition::RIGHT;
+ sourceBoundary = DisplayTopologyPosition::RIGHT;
cursorOffset = rotatedCursorPosition.y;
} else if (rotatedUnconsumedDelta.x < 0) {
- sourceBoundary = DisplayPosition::LEFT;
+ sourceBoundary = DisplayTopologyPosition::LEFT;
cursorOffset = rotatedCursorPosition.y;
} else if (rotatedUnconsumedDelta.y > 0) {
- sourceBoundary = DisplayPosition::BOTTOM;
+ sourceBoundary = DisplayTopologyPosition::BOTTOM;
cursorOffset = rotatedCursorPosition.x;
} else {
- sourceBoundary = DisplayPosition::TOP;
+ sourceBoundary = DisplayTopologyPosition::TOP;
cursorOffset = rotatedCursorPosition.x;
}
@@ -358,7 +362,7 @@
LOG(FATAL) << "A cursor already exists on destination display"
<< destinationViewport.displayId;
}
- mDefaultMouseDisplayId = destinationViewport.displayId;
+ mCurrentMouseDisplayId = destinationViewport.displayId;
auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
pcNode.key() = destinationViewport.displayId;
mMousePointersByDisplay.insert(std::move(pcNode));
@@ -369,8 +373,8 @@
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
pc.setDisplayViewport(destinationViewport);
vec2 destinationPosition =
- calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset,
- sourceBoundary);
+ calculatePositionOnDestinationViewport(destinationViewport, destinationOffset,
+ sourceBoundary);
// Transform position back to un-rotated coordinate space before sending it to controller
destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
@@ -379,22 +383,6 @@
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
-vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport,
- float pointerOffset,
- DisplayPosition sourceBoundary) {
- // destination is opposite of the source boundary
- switch (sourceBoundary) {
- case DisplayPosition::RIGHT:
- return {0, pointerOffset}; // left edge
- case DisplayPosition::TOP:
- return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
- case DisplayPosition::LEFT:
- return {destinationViewport.logicalRight, pointerOffset}; // right edge
- case DisplayPosition::BOTTOM:
- return {pointerOffset, 0}; // top edge
- }
-}
-
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
if (args.displayId == ui::LogicalDisplayId::INVALID) {
return;
@@ -489,6 +477,14 @@
<< args.dump();
}
+ // Fade the mouse pointer on the display if there is one when the stylus starts hovering.
+ if (args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) {
+ if (const auto it = mMousePointersByDisplay.find(args.displayId);
+ it != mMousePointersByDisplay.end()) {
+ it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+ }
+
// Get the stylus pointer controller for the device, or create one if it doesn't exist.
auto [it, controllerAdded] =
mStylusPointersByDevice.try_emplace(args.deviceId,
@@ -540,10 +536,6 @@
}
void PointerChoreographer::onControllerAddedOrRemovedLocked() {
- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() &&
- !com::android::input::flags::connected_displays_cursor()) {
- return;
- }
bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
!mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
@@ -607,11 +599,22 @@
mNextListener.notify(args);
}
-void PointerChoreographer::setDisplayTopology(
- const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
- displayTopology) {
- std::scoped_lock _l(getLock());
- mTopology = displayTopology;
+void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
+ PointerDisplayChange pointerDisplayChange;
+ { // acquire lock
+ std::scoped_lock _l(getLock());
+ mTopology = displayTopologyGraph;
+
+ // make primary display default mouse display, if it was not set or
+ // the existing display was removed
+ if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID ||
+ mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) {
+ mCurrentMouseDisplayId = mTopology.primaryDisplayId;
+ pointerDisplayChange = updatePointerControllersLocked();
+ }
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
void PointerChoreographer::dump(std::string& dump) {
@@ -622,6 +625,8 @@
mShowTouchesEnabled ? "true" : "false");
dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
mStylusPointerIconEnabled ? "true" : "false");
+ dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n",
+ mPointerMotionFilterEnabled ? "true" : "false");
dump += INDENT "MousePointerControllers:\n";
for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
@@ -643,6 +648,8 @@
std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
}
+ dump += INDENT "DisplayTopologyGraph:\n";
+ dump += addLinePrefix(mTopology.dump(), INDENT2);
dump += "\n";
}
@@ -658,7 +665,25 @@
ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
ui::LogicalDisplayId associatedDisplayId) const {
- return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
+ if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) {
+ if (associatedDisplayId.isValid()) {
+ return associatedDisplayId;
+ }
+ return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId
+ : ui::LogicalDisplayId::DEFAULT;
+ }
+ // Associated display is not included in the topology, return this associated display.
+ if (associatedDisplayId.isValid() &&
+ mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) {
+ return associatedDisplayId;
+ }
+ if (mCurrentMouseDisplayId.isValid()) {
+ return mCurrentMouseDisplayId;
+ }
+ if (mTopology.primaryDisplayId.isValid()) {
+ return mTopology.primaryDisplayId;
+ }
+ return ui::LogicalDisplayId::DEFAULT;
}
std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
@@ -767,7 +792,8 @@
PointerChoreographer::calculatePointerDisplayChangeToNotify() {
ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
vec2 cursorPosition = {0, 0};
- if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
+ if (const auto it =
+ mMousePointersByDisplay.find(getTargetMouseDisplayLocked(mCurrentMouseDisplayId));
it != mMousePointersByDisplay.end()) {
const auto& pointerController = it->second;
// Use the displayId from the pointerController, because it accurately reflects whether
@@ -784,12 +810,16 @@
}
void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
+ if (InputFlags::connectedDisplaysCursorEnabled()) {
+ // In connected displays scenario, default mouse display will only be updated from topology.
+ return;
+ }
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
std::scoped_lock _l(getLock());
- mDefaultMouseDisplayId = displayId;
+ mCurrentMouseDisplayId = displayId;
pointerDisplayChange = updatePointerControllersLocked();
} // release lock
@@ -957,6 +987,11 @@
mCurrentFocusedDisplay = displayId;
}
+void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) {
+ std::scoped_lock _l(getLock());
+ mPointerMotionFilterEnabled = enabled;
+}
+
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
@@ -985,97 +1020,66 @@
return ConstructorDelegate(std::move(ctor));
}
-void PointerChoreographer::populateFakeDisplayTopologyLocked(
- const std::vector<gui::DisplayInfo>& displayInfos) {
- if (!com::android::input::flags::connected_displays_cursor()) {
- return;
- }
-
- if (displayInfos.size() == mTopology.size()) {
- bool displaysChanged = false;
- for (const auto& displayInfo : displayInfos) {
- if (mTopology.find(displayInfo.displayId) == mTopology.end()) {
- displaysChanged = true;
- break;
- }
- }
-
- if (!displaysChanged) {
- return;
- }
- }
-
- // create a fake topology assuming following order
- // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
- // This also adds a 100px offset on corresponding edge for better manual testing
- // ┌────────┐
- // │ next ├─────────┐
- // ┌─└───────┐┤ next 2 │ ...
- // │ default │└─────────┘
- // └─────────┘
- mTopology.clear();
-
- // treat default display as base, in real topology it should be the primary-display
- ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
- for (const auto& displayInfo : displayInfos) {
- if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) {
- continue;
- }
- if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
- mTopology[previousDisplay].push_back(
- {displayInfo.displayId, DisplayPosition::TOP, 100});
- mTopology[displayInfo.displayId].push_back(
- {previousDisplay, DisplayPosition::BOTTOM, -100});
- } else {
- mTopology[previousDisplay].push_back(
- {displayInfo.displayId, DisplayPosition::RIGHT, 100});
- mTopology[displayInfo.displayId].push_back(
- {previousDisplay, DisplayPosition::LEFT, -100});
- }
- previousDisplay = displayInfo.displayId;
- }
-
- // update default pointer display. In real topology it should be the primary-display
- if (mTopology.find(mDefaultMouseDisplayId) == mTopology.end()) {
- mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT;
- }
-}
-
-std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
+std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
- const DisplayPosition sourceBoundary,
- float cursorOffset) const {
- const auto& sourceNode = mTopology.find(sourceDisplayId);
- if (sourceNode == mTopology.end()) {
+ const DisplayTopologyPosition sourceBoundary,
+ int32_t sourceCursorOffsetPx) const {
+ const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
+ if (sourceNode == mTopology.graph.end()) {
// Topology is likely out of sync with viewport info, wait for it to be updated
LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
return std::nullopt;
}
- for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) {
+ for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : sourceNode->second) {
if (adjacentDisplay.position != sourceBoundary) {
continue;
}
- const DisplayViewport* destinationViewport =
- findViewportByIdLocked(adjacentDisplay.displayId);
- if (destinationViewport == nullptr) {
+ const DisplayViewport* adjacentViewport = findViewportByIdLocked(adjacentDisplay.displayId);
+ if (adjacentViewport == nullptr) {
// Topology is likely out of sync with viewport info, wait for them to be updated
LOG(WARNING) << "Cannot find viewport for adjacent display "
<< adjacentDisplay.displayId << "of source display " << sourceDisplayId;
continue;
}
- // target position must be within target display boundary
- const int32_t edgeSize =
- sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM
- ? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
- : (destinationViewport->logicalBottom - destinationViewport->logicalTop);
- if (cursorOffset >= adjacentDisplay.offsetPx &&
- cursorOffset <= adjacentDisplay.offsetPx + edgeSize) {
- return std::make_pair(destinationViewport, adjacentDisplay.offsetPx);
+ // As displays can have different densities we need to do all calculations in
+ // density-independent-pixels a.k.a. dp values.
+ const int sourceDensity = mTopology.displaysDensity.at(sourceDisplayId);
+ const int adjacentDisplayDensity = mTopology.displaysDensity.at(adjacentDisplay.displayId);
+ const float sourceCursorOffsetDp = pxToDp(sourceCursorOffsetPx, sourceDensity);
+ const int32_t edgeSizePx = sourceBoundary == DisplayTopologyPosition::TOP ||
+ sourceBoundary == DisplayTopologyPosition::BOTTOM
+ ? (adjacentViewport->logicalRight - adjacentViewport->logicalLeft)
+ : (adjacentViewport->logicalBottom - adjacentViewport->logicalTop);
+ const float adjacentEdgeSizeDp = pxToDp(edgeSizePx, adjacentDisplayDensity);
+ // Target position must be within target display boundary.
+ // Cursor should also be able to cross displays when only display corners are touching and
+ // there may be zero overlapping pixels. To accommodate this we have margin of one pixel
+ // around the end of the overlapping edge.
+ if (sourceCursorOffsetDp >= adjacentDisplay.offsetDp &&
+ sourceCursorOffsetDp <= adjacentDisplay.offsetDp + adjacentEdgeSizeDp) {
+ const int destinationOffsetPx =
+ dpToPx(sourceCursorOffsetDp - adjacentDisplay.offsetDp, adjacentDisplayDensity);
+ return std::make_pair(adjacentViewport, destinationOffsetPx);
}
}
return std::nullopt;
}
+vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked(
+ const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
+ if (!mPointerMotionFilterEnabled) {
+ return delta;
+ }
+ std::optional<vec2> filterResult =
+ mPolicy.filterPointerMotionForAccessibility(current, delta, displayId);
+ if (!filterResult.has_value()) {
+ // Disable filter when there's any error.
+ mPointerMotionFilterEnabled = false;
+ return delta;
+ }
+ return *filterResult;
+}
+
// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
@@ -1093,7 +1097,6 @@
mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
}
- mPointerChoreographer->populateFakeDisplayTopologyLocked(windowInfosUpdate.displayInfos);
}
void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 939529f..67bdca1 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -22,6 +22,7 @@
#include <android-base/thread_annotations.h>
#include <gui/WindowInfosListener.h>
+#include <input/DisplayTopologyGraph.h>
#include <type_traits>
#include <unordered_set>
@@ -80,10 +81,20 @@
*/
virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0;
+ /*
+ * Used by InputManager to notify changes in the DisplayTopology
+ */
+ virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0;
+
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
*/
virtual void dump(std::string& dump) = 0;
+
+ /**
+ * Enables motion event filter before pointer coordinates are determined.
+ */
+ virtual void setAccessibilityPointerMotionFilterEnabled(bool enabled) = 0;
};
class PointerChoreographer : public PointerChoreographerInterface {
@@ -103,6 +114,8 @@
ui::LogicalDisplayId displayId, DeviceId deviceId) override;
void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
+ void setAccessibilityPointerMotionFilterEnabled(bool enabled) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyKey(const NotifyKeyArgs& args) override;
@@ -113,24 +126,6 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
- // TODO(b/362719483) remove these when real topology is available
- enum class DisplayPosition {
- RIGHT,
- TOP,
- LEFT,
- BOTTOM,
- ftl_last = BOTTOM,
- };
-
- struct AdjacentDisplay {
- ui::LogicalDisplayId displayId;
- DisplayPosition position;
- float offsetPx;
- };
- void setDisplayTopology(
- const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>&
- displayTopology);
-
void dump(std::string& dump) override;
private:
@@ -174,18 +169,20 @@
void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
REQUIRES(getLock());
- void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+ std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
+ findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
+ const DisplayTopologyPosition sourceBoundary,
+ int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
+
+ vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta,
+ const ui::LogicalDisplayId& displayId)
REQUIRES(getLock());
- std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
- const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary,
- float cursorOffset) const REQUIRES(getLock());
-
- static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport,
- float pointerOffset, DisplayPosition sourceBoundary);
-
- std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology
- GUARDED_BY(getLock());
+ /* Topology is initialized with default-constructed value, which is an empty topology. Till we
+ * receive setDisplayTopology call.
+ * Meanwhile Choreographer will treat every display as independent disconnected display.
+ */
+ DisplayTopologyGraph mTopology GUARDED_BY(getLock());
/* This listener keeps tracks of visible privacy sensitive displays and updates the
* choreographer if there are any changes.
@@ -234,13 +231,19 @@
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
GUARDED_BY(getLock());
- ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
+ // In connected displays scenario, this tracks the latest display the cursor is at, within the
+ // DisplayTopology. By default, this will be set to topology primary display, and updated when
+ // mouse crossed to another display.
+ // In non-connected displays scenario, this will be treated as the default display cursor
+ // will be on, when mouse doesn't have associated display.
+ ui::LogicalDisplayId mCurrentMouseDisplayId GUARDED_BY(getLock());
ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
bool mShowTouchesEnabled GUARDED_BY(getLock());
bool mStylusPointerIconEnabled GUARDED_BY(getLock());
+ bool mPointerMotionFilterEnabled GUARDED_BY(getLock());
std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index d9d0450..6864947 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -216,10 +216,10 @@
std::string PreferStylusOverTouchBlocker::dump() const {
std::string out;
- out += "mActiveStyli: " + dumpSet(mActiveStyli) + "\n";
+ out += "mActiveStyli: " + dumpContainer(mActiveStyli) + "\n";
out += "mLastTouchEvents: " + dumpMap(mLastTouchEvents, constToString, dumpArgs) + "\n";
- out += "mDevicesWithMixedToolType: " + dumpSet(mDevicesWithMixedToolType) + "\n";
- out += "mCanceledDevices: " + dumpSet(mCanceledDevices) + "\n";
+ out += "mDevicesWithMixedToolType: " + dumpContainer(mDevicesWithMixedToolType) + "\n";
+ out += "mCanceledDevices: " + dumpContainer(mCanceledDevices) + "\n";
return out;
}
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index 0e9ec91..29de635 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -727,7 +727,7 @@
if (!std::includes(oldSuppressedIds.begin(), oldSuppressedIds.end(),
mSuppressedPointerIds.begin(), mSuppressedPointerIds.end())) {
ALOGI("Palm detected, removing pointer ids %s after %" PRId64 "ms from %s",
- dumpSet(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime),
+ dumpContainer(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime),
args.dump().c_str());
}
@@ -748,7 +748,7 @@
out += "mSlotState:\n";
out += addLinePrefix(mSlotState.dump(), " ");
out += "mSuppressedPointerIds: ";
- out += dumpSet(mSuppressedPointerIds) + "\n";
+ out += dumpContainer(mSuppressedPointerIds) + "\n";
std::stringstream state;
state << *mSharedPalmState;
out += "mSharedPalmState: " + state.str() + "\n";
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 8b2b843..1aa8b2b 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -49,8 +49,8 @@
"LatencyAggregatorWithHistograms.cpp",
"LatencyTracker.cpp",
"Monitor.cpp",
- "TouchedWindow.cpp",
"TouchState.cpp",
+ "TouchedWindow.cpp",
"trace/*.cpp",
],
}
@@ -71,9 +71,9 @@
"liblog",
"libprotobuf-cpp-lite",
"libstatslog",
- "libutils",
"libstatspull",
"libstatssocket",
+ "libutils",
"packagemanager_aidl-cpp",
"server_configurable_flags",
],
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index becfb05..9cd76c7 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -125,10 +125,17 @@
bool syntheticRepeat; // set to true for synthetic key repeats
enum class InterceptKeyResult {
+ // The interception result is unknown.
UNKNOWN,
+ // The event should be skipped and not sent to the application.
SKIP,
+ // The event should be sent to the application.
CONTINUE,
+ // The event should eventually be sent to the application, after a delay.
TRY_AGAIN_LATER,
+ // The event should not be initially sent to the application, but instead go through
+ // post-processing to generate a fallback key event and then sent to the application.
+ FALLBACK,
};
// These are special fields that may need to be modified while the event is being dispatched.
mutable InterceptKeyResult interceptKeyResult; // set based on the interception result
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index cd4ed5c..2908c61 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -32,6 +32,7 @@
#include <gui/SurfaceComposerClient.h>
#endif
#include <input/InputDevice.h>
+#include <input/InputFlags.h>
#include <input/PrintTools.h>
#include <input/TraceTools.h>
#include <openssl/mem.h>
@@ -46,6 +47,7 @@
#include <ctime>
#include <queue>
#include <sstream>
+#include <variant>
#include "../InputDeviceMetricsSource.h"
@@ -417,7 +419,7 @@
if (inputTarget.useDefaultPointerTransform() && !zeroCoords) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.displayTransform,
+ inputTarget.rawTransform,
inputTarget.globalScaleFactor, uid, vsyncId,
windowId);
}
@@ -438,7 +440,7 @@
transform =
&inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds()));
const ui::Transform inverseTransform = transform->inverse();
- displayTransform = &inputTarget.displayTransform;
+ displayTransform = &inputTarget.rawTransform;
// Iterate through all pointers in the event to normalize against the first.
for (size_t i = 0; i < motionEntry.getPointerCount(); i++) {
@@ -787,52 +789,14 @@
});
}
-/**
- * In general, touch should be always split between windows. Some exceptions:
- * 1. Don't split touch if all of the below is true:
- * (a) we have an active pointer down *and*
- * (b) a new pointer is going down that's from the same device *and*
- * (c) the window that's receiving the current pointer does not support split touch.
- * 2. Don't split mouse events
- */
-bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) {
- if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
- // We should never split mouse events
- return false;
- }
- for (const TouchedWindow& touchedWindow : touchState.windows) {
- if (touchedWindow.windowHandle->getInfo()->isSpy()) {
- // Spy windows should not affect whether or not touch is split.
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
- continue;
- }
- if (touchedWindow.windowHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
- // Wallpaper window should not affect whether or not touch is split
- continue;
- }
-
- if (touchedWindow.hasTouchingPointers(entry.deviceId)) {
- return false;
- }
- }
- return true;
-}
-
-/**
- * Return true if stylus is currently down anywhere on the specified display, and false otherwise.
- */
-bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId,
- const std::unordered_map<ui::LogicalDisplayId /*displayId*/,
- TouchState>& touchStatesByDisplay) {
- const auto it = touchStatesByDisplay.find(displayId);
- if (it == touchStatesByDisplay.end()) {
- return false;
- }
- const TouchState& state = it->second;
- return state.hasActiveStylus();
+bool shouldSplitTouch(int32_t source) {
+ // We should never split mouse events. This is because the events that are produced by touchpad
+ // are sent to InputDispatcher as two fingers (for example, pinch zoom), but they need to be
+ // dispatched to the same window. In those cases, the behaviour is also slightly different from
+ // default because the events should be sent to the cursor position rather than the x,y values
+ // of each of the fingers.
+ // The "normal" (uncaptured) events produced by touchpad and by mouse have SOURCE_MOUSE.
+ return !isFromSource(source, AINPUT_SOURCE_MOUSE);
}
Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) {
@@ -924,6 +888,38 @@
std::forward<InputEventInjectionResult>(e));
}
+InputTarget createInputTarget(const std::shared_ptr<Connection>& connection,
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ const ui::Transform& rawTransform,
+ std::optional<nsecs_t> firstDownTimeInTarget) {
+ LOG_ALWAYS_FATAL_IF(connection == nullptr);
+ InputTarget inputTarget{connection};
+ inputTarget.windowHandle = windowHandle;
+ inputTarget.dispatchMode = dispatchMode;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
+ inputTarget.rawTransform = rawTransform;
+ inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
+ return inputTarget;
+}
+
+std::string dumpWindowForTouchOcclusion(const WindowInfo& info, bool isTouchedWindow) {
+ return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window={%s}, inputConfig={%s}, "
+ "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
+ isTouchedWindow ? "[TOUCHED] " : "", info.packageName.c_str(),
+ info.ownerUid.toString().c_str(), info.id,
+ toString(info.touchOcclusionMode).c_str(), info.alpha, info.frame.left,
+ info.frame.top, info.frame.right, info.frame.bottom,
+ dumpRegion(info.touchableRegion).c_str(), info.name.c_str(),
+ info.inputConfig.string().c_str(), toString(info.token != nullptr),
+ info.applicationInfo.name.c_str(),
+ binderToString(info.applicationInfo.token).c_str());
+}
+
} // namespace
// --- InputDispatcher ---
@@ -934,16 +930,19 @@
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),
+ mWindowInfosVsyncId(-1),
mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
+ mConnectionManager(mLooper),
+ mTouchStates(mWindowInfos, mConnectionManager),
mNextUnblockedEvent(nullptr),
mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
mDispatchFrozen(false),
mInputFilterEnabled(false),
- mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID),
@@ -953,8 +952,7 @@
new LatencyAggregatorWithHistograms()))
: std::move(std::unique_ptr<InputEventTimelineProcessor>(
new LatencyAggregator()))),
- mLatencyTracker(*mInputEventTimelineProcessor) {
- mLooper = sp<Looper>::make(false);
+ mLatencyTracker(*mInputEventTimelineProcessor, mInputDevices) {
mReporter = createInputReporter();
mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
@@ -976,12 +974,10 @@
resetKeyRepeatLocked();
releasePendingEventLocked();
drainInboundQueueLocked();
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(mWindowInfoListener);
+#endif
mCommandQueue.clear();
-
- while (!mConnectionsByToken.empty()) {
- std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second;
- removeInputChannelLocked(connection->getToken(), /*notify=*/false);
- }
}
status_t InputDispatcher::start() {
@@ -1098,9 +1094,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;
@@ -1128,7 +1128,7 @@
if (connection->monitor) {
return mMonitorDispatchingTimeout;
}
- const sp<WindowInfoHandle> window = getWindowHandleLocked(connection->getToken());
+ const sp<WindowInfoHandle> window = mWindowInfos.findWindowHandle(connection->getToken());
if (window != nullptr) {
return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
}
@@ -1147,9 +1147,7 @@
// If dispatching is frozen, do not process timeouts or try to deliver any new events.
if (mDispatchFrozen) {
- if (DEBUG_FOCUS) {
- ALOGD("Dispatch frozen. Waiting some more.");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Dispatch frozen. Waiting some more.";
return;
}
@@ -1259,13 +1257,9 @@
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
// The event is stale. However, only drop stale events if there isn't an ongoing
// gesture. That would allow us to complete the processing of the current stroke.
- const auto touchStateIt = mTouchStatesByDisplay.find(motionEntry->displayId);
- if (touchStateIt != mTouchStatesByDisplay.end()) {
- const TouchState& touchState = touchStateIt->second;
- if (!touchState.hasTouchingPointers(motionEntry->deviceId) &&
- !touchState.hasHoveringPointers(motionEntry->deviceId)) {
- dropReason = DropReason::STALE;
- }
+ if (!mTouchStates.hasTouchingOrHoveringPointers(motionEntry->displayId,
+ motionEntry->deviceId)) {
+ dropReason = DropReason::STALE;
}
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
@@ -1333,7 +1327,7 @@
const bool isStylus = isPointerFromStylus(motionEntry, /*pointerIndex=*/0);
sp<WindowInfoHandle> touchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
if (touchedWindowHandle != nullptr &&
touchedWindowHandle->getApplicationToken() !=
mAwaitedFocusedApplication->getApplicationToken()) {
@@ -1346,10 +1340,11 @@
// Alternatively, maybe there's a spy window that could handle this event.
const std::vector<sp<WindowInfoHandle>> touchedSpies =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, motionEntry.deviceId);
+ findTouchedSpyWindowsAt(displayId, x, y, isStylus, motionEntry.deviceId,
+ mWindowInfos);
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.
@@ -1452,34 +1447,34 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(ui::LogicalDisplayId displayId,
- float x, float y, bool isStylus,
- bool ignoreDragWindow) const {
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findTouchedWindowAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus,
+ const sp<android::gui::WindowInfoHandle> ignoreWindow) const {
// Traverse windows from front to back to find touched window.
- const auto& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
- if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
+ if (ignoreWindow && haveSameToken(windowHandle, ignoreWindow)) {
continue;
}
const WindowInfo& info = *windowHandle->getInfo();
if (!info.isSpy() &&
- windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
+ windowAcceptsTouchAt(info, displayId, x, y, isStylus, getDisplayTransform(displayId))) {
return windowHandle;
}
}
return nullptr;
}
-std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
- ui::LogicalDisplayId displayId, const sp<WindowInfoHandle>& touchedWindow,
- int32_t pointerId) const {
+std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTargets(
+ ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
+ int32_t pointerId, std::function<void()> dump) {
if (touchedWindow == nullptr) {
return {};
}
// Traverse windows from front to back until we encounter the touched window.
std::vector<InputTarget> outsideTargets;
- const auto& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = mWindowInfos.getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (windowHandle == touchedWindow) {
// Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window
@@ -1491,36 +1486,27 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- addPointerWindowTargetLocked(windowHandle, InputTarget::DispatchMode::OUTSIDE,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- /*firstDownTimeInTarget=*/std::nullopt, outsideTargets);
+ addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::OUTSIDE,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt,
+ /*pointerDisplayId=*/std::nullopt, dump, outsideTargets);
}
}
return outsideTargets;
}
-std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId) const {
+std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId,
+ const DispatcherWindowInfo& windowInfos) {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
- const auto& windowHandles = getWindowHandlesLocked(displayId);
+ const ui::Transform displayTransform = windowInfos.getDisplayTransform(displayId);
+ const auto& windowHandles = windowInfos.getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
- if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
- // Generally, we would skip any pointer that's outside of the window. However, if the
- // spy prevents splitting, and already has some of the pointers from this device, then
- // it should get more pointers from the same device, even if they are outside of that
- // window
- if (info.supportsSplitTouch()) {
- continue;
- }
-
- // We know that split touch is not supported. Skip this window only if it doesn't have
- // any touching pointers for this device already.
- if (!windowHasTouchingPointersLocked(windowHandle, deviceId)) {
- continue;
- }
- // If it already has pointers down for this device, then give it this pointer, too.
+ if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, displayTransform)) {
+ // Skip if the pointer is outside of the window.
+ continue;
}
if (!info.isSpy()) {
// The first touched non-spy window was found, so return the spy windows touched so far.
@@ -1535,9 +1521,7 @@
const char* reason;
switch (dropReason) {
case DropReason::POLICY:
- if (debugInboundEventDetails()) {
- ALOGD("Dropped event because policy consumed it.");
- }
+ LOG_IF(INFO, debugInboundEventDetails()) << "Dropped event because policy consumed it.";
reason = "inbound event was dropped because the policy consumed it";
break;
case DropReason::DISABLED:
@@ -1651,9 +1635,7 @@
void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<const EventEntry> entry) {
const std::shared_ptr<InjectionState>& injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Injected inbound event was dropped.");
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "Injected inbound event was dropped.";
setInjectionResult(*entry, InputEventInjectionResult::FAILED);
}
if (entry == mNextUnblockedEvent) {
@@ -1693,10 +1675,9 @@
bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
const DeviceResetEntry& entry) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
- entry.deviceId);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "dispatchDeviceReset - eventTime=" << entry.eventTime
+ << ", deviceId=" << entry.deviceId;
// Reset key repeating in case a keyboard device was disabled or enabled.
if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) {
@@ -1710,9 +1691,7 @@
synthesizeCancelationEventsForAllConnectionsLocked(options);
// Remove all active pointers from this device
- for (auto& [_, touchState] : mTouchStatesByDisplay) {
- touchState.removeAllPointersForDevice(entry.deviceId);
- }
+ mTouchStates.removeAllPointersForDevice(entry.deviceId);
return true;
}
@@ -1742,7 +1721,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
}
@@ -1810,7 +1790,7 @@
}
}
- auto connection = getConnectionLocked(token);
+ auto connection = mConnectionManager.getConnection(token);
if (connection == nullptr) {
// Window has gone away, clean up Pointer Capture state.
mWindowTokenWithPointerCapture = nullptr;
@@ -1828,7 +1808,7 @@
void InputDispatcher::dispatchTouchModeChangeLocked(
nsecs_t currentTime, const std::shared_ptr<const TouchModeEntry>& entry) {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
- getWindowHandlesLocked(entry->displayId);
+ mWindowInfos.getWindowHandlesForDisplay(entry->displayId);
if (windowHandles.empty()) {
return;
}
@@ -1849,7 +1829,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
}
@@ -1889,9 +1869,8 @@
} else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
// The key on device 'deviceId' is still down, do not stop key repeat
- if (debugInboundEventDetails()) {
- ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "deviceId=" << entry->deviceId << " got KEY_UP as stale";
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -1982,31 +1961,93 @@
}
}
+ if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::FALLBACK) {
+ findAndDispatchFallbackEvent(currentTime, entry, inputTargets);
+ // Drop the key.
+ return true;
+ }
+
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
-void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%s, "
- "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
- "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source,
- entry.displayId.toString().c_str(), entry.policyFlags, entry.action, entry.flags,
- entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime);
+void InputDispatcher::findAndDispatchFallbackEvent(nsecs_t currentTime,
+ std::shared_ptr<const KeyEntry> entry,
+ std::vector<InputTarget>& inputTargets) {
+ // Find the fallback associated with the incoming key event and dispatch it.
+ KeyEvent event = createKeyEvent(*entry);
+ const int32_t originalKeyCode = entry->keyCode;
+
+ // Fetch the fallback event.
+ KeyCharacterMap::FallbackAction fallback;
+ for (const InputDeviceInfo& deviceInfo : mInputDevices) {
+ if (deviceInfo.getId() == entry->deviceId) {
+ const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap();
+
+ LOG_ALWAYS_FATAL_IF(map == nullptr, "No KeyCharacterMap for device %d",
+ entry->deviceId);
+ map->getFallbackAction(entry->keyCode, entry->metaState, &fallback);
+ break;
+ }
}
+
+ if (fallback.keyCode == AKEYCODE_UNKNOWN) {
+ // No fallback detected.
+ return;
+ }
+
+ std::unique_ptr<KeyEntry> fallbackKeyEntry =
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), entry->injectionState,
+ event.getEventTime(), event.getDeviceId(), event.getSource(),
+ event.getDisplayId(), entry->policyFlags, entry->action,
+ event.getFlags() | AKEY_EVENT_FLAG_FALLBACK,
+ fallback.keyCode, event.getScanCode(), /*metaState=*/0,
+ event.getRepeatCount(), event.getDownTime());
+
+ if (mTracer) {
+ fallbackKeyEntry->traceTracker =
+ mTracer->traceDerivedEvent(*fallbackKeyEntry, *entry->traceTracker);
+ }
+
+ for (const InputTarget& inputTarget : inputTargets) {
+ std::shared_ptr<Connection> connection = inputTarget.connection;
+ if (!connection->responsive || (connection->status != Connection::Status::NORMAL)) {
+ return;
+ }
+
+ connection->inputState.setFallbackKey(originalKeyCode, fallback.keyCode);
+ if (entry->action == AKEY_EVENT_ACTION_UP) {
+ connection->inputState.removeFallbackKey(originalKeyCode);
+ }
+
+ if (mTracer) {
+ mTracer->dispatchToTargetHint(*fallbackKeyEntry->traceTracker, inputTarget);
+ }
+ enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection,
+ std::move(fallbackKeyEntry), inputTarget);
+ }
+}
+
+void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId
+ << ", source=0x" << std::hex << entry.source
+ << ", displayId=" << entry.displayId.toString() << ", policyFlags=0x"
+ << entry.policyFlags << ", action=0x" << entry.action << ", flags=0x" << entry.flags
+ << ", keyCode=0x" << entry.keyCode << ", scanCode=0x" << entry.scanCode
+ << ", metaState=0x" << entry.metaState << ", repeatCount=" << std::dec
+ << entry.repeatCount << ", downTime=" << entry.downTime;
}
void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
const std::shared_ptr<const SensorEntry>& entry,
DropReason* dropReason, nsecs_t& nextWakeupTime) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
- "source=0x%x, sensorType=%s",
- entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
- ftl::enum_string(entry->sensorType).c_str());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "notifySensorEvent eventTime=" << entry->eventTime
+ << ", hwTimestamp=" << entry->hwTimestamp << ", deviceId=" << entry->deviceId
+ << ", source=0x" << std::hex << entry->source << std::dec
+ << ", sensorType=" << ftl::enum_string(entry->sensorType);
auto command = [this, entry]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -2020,10 +2061,9 @@
}
bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
- ftl::enum_string(sensorType).c_str());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "flushSensor deviceId=" << deviceId
+ << ", sensorType=" << ftl::enum_string(sensorType).c_str();
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -2073,7 +2113,15 @@
}
Result<std::vector<InputTarget>, InputEventInjectionResult> result =
- findTouchedWindowTargetsLocked(currentTime, *entry);
+ mTouchStates
+ .findTouchedWindowTargets(currentTime, *entry,
+ mDragState ? mDragState->dragWindow : nullptr,
+ std::bind_front(&InputDispatcher::
+ addDragEventLocked,
+ this),
+ std::bind_front(&InputDispatcher::
+ logDispatchStateLocked,
+ this));
if (result.ok()) {
inputTargets = std::move(*result);
@@ -2142,7 +2190,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
}
@@ -2183,9 +2232,7 @@
std::shared_ptr<const EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("dispatchEventToCurrentInputTargets");
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "dispatchEventToCurrentInputTargets";
processInteractionsLocked(*eventEntry, inputTargets);
@@ -2216,7 +2263,7 @@
sp<WindowInfoHandle> windowHandle;
if (!connection->monitor) {
- windowHandle = getWindowHandleLocked(connection->getToken());
+ windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
if (windowHandle == nullptr) {
// The window that is receiving this ANR was removed, so there is no need to generate
// cancellations, because the cancellations would have already been generated when
@@ -2228,9 +2275,7 @@
}
void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
- if (DEBUG_FOCUS) {
- ALOGD("Resetting ANR timeouts.");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Resetting ANR timeouts.";
// Reset input target wait timeout.
mNoFocusedWindowTimeoutTime = std::nullopt;
@@ -2317,7 +2362,8 @@
}
// Drop key events if requested by input feature
- if (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {
+ if (focusedWindowHandle != nullptr &&
+ shouldDropInput(entry, focusedWindowHandle, mWindowInfos)) {
return injectionError(InputEventInjectionResult::FAILED);
}
@@ -2386,28 +2432,11 @@
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) {
+base::Result<std::vector<InputTarget>, os::InputEventInjectionResult>
+InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
+ nsecs_t currentTime, const MotionEntry& entry,
+ const sp<android::gui::WindowInfoHandle> dragWindow,
+ std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump) {
ATRACE_CALL();
std::vector<InputTarget> targets;
@@ -2418,16 +2447,15 @@
const int32_t maskedAction = MotionEvent::getActionMasked(action);
// Copy current touch state into tempTouchState.
- // This state will be used to update mTouchStatesByDisplay at the end of this function.
+ // This state will be used to update saved touch state at the end of this function.
// If no state for the specified display exists, then our initial state will be empty.
- const TouchState* oldState = nullptr;
+ const TouchState* oldState = getTouchStateForMotionEntry(entry);
TouchState tempTouchState;
- if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
- oldState = &(it->second);
+ if (oldState != nullptr) {
tempTouchState = *oldState;
}
- bool isSplit = shouldSplitTouch(tempTouchState, entry);
+ const bool isSplit = shouldSplitTouch(entry.source);
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2440,11 +2468,6 @@
const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;
- const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
-
- if (newGesture) {
- isSplit = false;
- }
if (isDown && tempTouchState.hasHoveringPointers(entry.deviceId)) {
// Compatibility behaviour: ACTION_DOWN causes HOVER_EXIT to get generated.
@@ -2472,25 +2495,14 @@
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
sp<WindowInfoHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
if (isDown) {
- targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
+ targets += findOutsideTargets(displayId, newTouchedWindowHandle, pointer.id, dump);
}
LOG_IF(INFO, newTouchedWindowHandle == nullptr)
<< "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
<< ") in display " << displayId;
- // Handle the case where we did not find a window.
- if (!input_flags::split_all_touches()) {
- // If we are force splitting all touches, then touches outside of the window should
- // be dropped, even if this device already has pointers down in another window.
- if (newTouchedWindowHandle == nullptr) {
- // Try to assign the pointer to the first foreground window we find, if there is
- // one.
- newTouchedWindowHandle =
- tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
- }
- }
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2498,27 +2510,26 @@
return injectionError(InputEventInjectionResult::TARGET_MISMATCH);
}
- // Figure out whether splitting will be allowed for this window.
- if (newTouchedWindowHandle != nullptr) {
- if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- // New window supports splitting, but we should never split mouse events.
- isSplit = !isFromMouse;
- } else if (isSplit) {
- // New window does not support splitting but we have already split events.
- // Ignore the new window.
- LOG(INFO) << "Skipping " << newTouchedWindowHandle->getName()
- << " because it doesn't support split touch";
- newTouchedWindowHandle = nullptr;
+ if (newTouchedWindowHandle != nullptr &&
+ maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // Check if this should be redirected to another window, in case this window previously
+ // called 'transferTouch' for this gesture.
+ const auto it =
+ std::find_if(tempTouchState.windows.begin(), tempTouchState.windows.end(),
+ [&](const TouchedWindow& touchedWindow) {
+ return touchedWindow.forwardingWindowToken ==
+ newTouchedWindowHandle->getToken() &&
+ touchedWindow.hasTouchingPointers(entry.deviceId);
+ });
+ if (it != tempTouchState.windows.end()) {
+ LOG(INFO) << "Forwarding pointer from " << newTouchedWindowHandle->getName()
+ << " to " << it->windowHandle->getName();
+ newTouchedWindowHandle = it->windowHandle;
}
- } else {
- // No window is touched, so set split to true. This will allow the next pointer down to
- // be delivered to a new window which supports split touch. Pointers from a mouse device
- // should never be split.
- isSplit = !isFromMouse;
}
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, entry.deviceId);
+ findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos);
if (newTouchedWindowHandle != nullptr) {
// Process the foreground window first so that it is the first to receive the event.
newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -2531,7 +2542,7 @@
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
- if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
+ if (!canWindowReceiveMotion(windowHandle, entry)) {
continue;
}
@@ -2542,21 +2553,8 @@
}
// Set target flags.
- ftl::Flags<InputTarget::Flags> targetFlags;
-
- if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
- // There should only be one touched window that can be "foreground" for the pointer.
- targetFlags |= InputTarget::Flags::FOREGROUND;
- }
-
- if (isSplit) {
- targetFlags |= InputTarget::Flags::SPLIT;
- }
- if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
- targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
- } else if (isWindowObscuredLocked(windowHandle)) {
- targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
- }
+ ftl::Flags<InputTarget::Flags> targetFlags =
+ getTargetFlags(windowHandle, {x, y}, isSplit);
// Update the temporary touch state.
@@ -2570,11 +2568,12 @@
isDownOrPointerDown
? std::make_optional(
entry.eventTime)
- : std::nullopt);
+ : std::nullopt,
+ /*forwardingWindowToken=*/nullptr);
if (!addResult.ok()) {
LOG(ERROR) << "Error while processing " << entry << " for "
<< windowHandle->getName();
- logDispatchStateLocked();
+ dump();
}
// If this is the pointer going down and the touched window has a wallpaper
// then also add the touched wallpaper windows so they are locked in for the
@@ -2585,7 +2584,8 @@
if (isDownOrPointerDown && targetFlags.test(InputTarget::Flags::FOREGROUND) &&
windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
- sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
+ sp<WindowInfoHandle> wallpaper =
+ mWindowInfos.findWallpaperWindowBelow(windowHandle);
if (wallpaper != nullptr) {
ftl::Flags<InputTarget::Flags> wallpaperFlags =
InputTarget::Flags::WINDOW_IS_OBSCURED |
@@ -2596,7 +2596,8 @@
tempTouchState.addOrUpdateWindow(wallpaper,
InputTarget::DispatchMode::AS_IS,
wallpaperFlags, entry.deviceId, {pointer},
- entry.eventTime);
+ entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
}
}
}
@@ -2623,11 +2624,10 @@
// If the pointer is not currently down, then ignore the event.
if (!tempTouchState.isDown(entry.deviceId) &&
maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
- if (DEBUG_DROPPED_EVENTS_VERBOSE) {
- LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId
- << " is not down or we previously dropped the pointer down event in "
- << "display " << displayId << ": " << entry.getDescription();
- }
+ LOG_IF(INFO, DEBUG_DROPPED_EVENTS_VERBOSE)
+ << "Dropping event because the pointer for device " << entry.deviceId
+ << " is not down or we previously dropped the pointer down event in display "
+ << displayId << ": " << entry.getDescription();
return injectionError(InputEventInjectionResult::FAILED);
}
@@ -2644,7 +2644,7 @@
tempTouchState.removeHoveringPointer(entry.deviceId, pointerId);
}
- addDragEventLocked(entry);
+ addDragEvent(entry);
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.getPointerCount() == 1 &&
@@ -2655,7 +2655,7 @@
tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
sp<WindowInfoHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2665,7 +2665,7 @@
// Do not slide events to the window which can not receive motion event
if (newTouchedWindowHandle != nullptr &&
- !canWindowReceiveMotionLocked(newTouchedWindowHandle, entry)) {
+ !canWindowReceiveMotion(newTouchedWindowHandle, entry)) {
newTouchedWindowHandle = nullptr;
}
@@ -2682,38 +2682,26 @@
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
- addPointerWindowTargetLocked(oldTouchedWindowHandle,
- InputTarget::DispatchMode::SLIPPERY_EXIT,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- touchedWindow.getDownTimeInTarget(entry.deviceId),
- targets);
+ addPointerWindowTarget(oldTouchedWindowHandle,
+ InputTarget::DispatchMode::SLIPPERY_EXIT,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
// Make a slippery entrance into the new window.
- if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- isSplit = !isFromMouse;
- }
- ftl::Flags<InputTarget::Flags> targetFlags;
- if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
- targetFlags |= InputTarget::Flags::FOREGROUND;
- }
- if (isSplit) {
- targetFlags |= InputTarget::Flags::SPLIT;
- }
- if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
- } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
- targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
- }
+ ftl::Flags<InputTarget::Flags> targetFlags =
+ getTargetFlags(newTouchedWindowHandle, {x, y}, isSplit);
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
InputTarget::DispatchMode::SLIPPERY_ENTER,
targetFlags, entry.deviceId, {pointer},
- entry.eventTime);
+ entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry, targets);
+ tempTouchState, entry, targets, dump);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -2726,7 +2714,7 @@
std::vector<PointerProperties> touchingPointers{entry.pointerProperties[pointerIndex]};
for (TouchedWindow& touchedWindow : tempTouchState.windows) {
// Ignore drag window for it should just track one pointer.
- if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
+ if (dragWindow == touchedWindow.windowHandle) {
continue;
}
if (!touchedWindow.hasTouchingPointers(entry.deviceId)) {
@@ -2740,17 +2728,15 @@
// Update dispatching for hover enter and exit.
{
std::vector<TouchedWindow> hoveringWindows =
- getHoveringWindowsLocked(oldState, tempTouchState, entry,
- std::bind_front(&InputDispatcher::logDispatchStateLocked,
- this));
+ getHoveringWindowsLocked(oldState, tempTouchState, entry, dump);
// Hardcode to single hovering pointer for now.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(entry.pointerProperties[0].id);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
- addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
- touchedWindow.targetFlags, pointerIds,
- touchedWindow.getDownTimeInTarget(entry.deviceId),
- targets);
+ addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
}
}
@@ -2779,7 +2765,7 @@
for (InputTarget& target : targets) {
if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
sp<WindowInfoHandle> targetWindow =
- getWindowHandleLocked(target.connection->getToken());
+ mWindowInfos.findWindowHandle(target.connection->getToken());
if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
target.flags |= InputTarget::Flags::ZERO_COORDS;
}
@@ -2803,9 +2789,10 @@
if (touchingPointers.empty()) {
continue;
}
- addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
- touchedWindow.targetFlags, getPointerIds(touchingPointers),
- touchedWindow.getDownTimeInTarget(entry.deviceId), targets);
+ addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, getPointerIds(touchingPointers),
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/displayId, dump, targets);
}
// During targeted injection, only allow owned targets to receive events
@@ -2860,16 +2847,12 @@
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
if (displayId >= ui::LogicalDisplayId::DEFAULT) {
tempTouchState.clearWindowsWithoutPointers();
- mTouchStatesByDisplay[displayId] = tempTouchState;
+ saveTouchStateForMotionEntry(entry, std::move(tempTouchState));
} else {
- mTouchStatesByDisplay.erase(displayId);
+ eraseTouchStateForMotionEntry(entry);
}
}
- if (tempTouchState.windows.empty()) {
- mTouchStatesByDisplay.erase(displayId);
- }
-
return targets;
}
@@ -2879,7 +2862,8 @@
constexpr bool isStylus = false;
sp<WindowInfoHandle> dropWindow =
- findTouchedWindowAtLocked(displayId, x, y, isStylus, /*ignoreDragWindow=*/true);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus, /*ignoreWindow=*/
+ mDragState->dragWindow);
if (dropWindow) {
vec2 local = dropWindow->getInfo()->transform.transform(x, y);
sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
@@ -2891,8 +2875,9 @@
}
void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
- if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
- mDragState->deviceId != entry.deviceId) {
+ if (!mDragState || mDragState->deviceId != entry.deviceId ||
+ !mWindowInfos.areDisplaysConnected(mDragState->dragWindow->getInfo()->displayId,
+ entry.displayId)) {
return;
}
@@ -2934,8 +2919,8 @@
constexpr bool isStylus = false;
sp<WindowInfoHandle> hoverWindowHandle =
- findTouchedWindowAtLocked(entry.displayId, x, y, isStylus,
- /*ignoreDragWindow=*/true);
+ mWindowInfos.findTouchedWindowAt(entry.displayId, x, y, isStylus,
+ /*ignoreWindow=*/mDragState->dragWindow);
// enqueue drag exit if needed.
if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
!haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -2970,31 +2955,6 @@
}
}
-std::optional<InputTarget> InputDispatcher::createInputTargetLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
- std::optional<nsecs_t> firstDownTimeInTarget) const {
- std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken());
- if (connection == nullptr) {
- ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str());
- return {};
- }
- InputTarget inputTarget{connection};
- inputTarget.windowHandle = windowHandle;
- inputTarget.dispatchMode = dispatchMode;
- inputTarget.flags = targetFlags;
- inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
- inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
- const auto& displayInfoIt = mDisplayInfos.find(windowHandle->getInfo()->displayId);
- if (displayInfoIt != mDisplayInfos.end()) {
- inputTarget.displayTransform = displayInfoIt->second.transform;
- } else {
- // DisplayInfo not found for this window on display windowHandle->getInfo()->displayId.
- // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
- }
- return inputTarget;
-}
-
void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
@@ -3009,13 +2969,17 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::optional<InputTarget> target =
- createInputTargetLocked(windowHandle, dispatchMode, targetFlags,
- firstDownTimeInTarget);
- if (!target) {
+ 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());
return;
}
- inputTargets.push_back(*target);
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo()),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3028,11 +2992,12 @@
}
}
-void InputDispatcher::addPointerWindowTargetLocked(
+void InputDispatcher::DispatcherTouchState::addPointerWindowTarget(
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
std::bitset<MAX_POINTER_ID + 1> pointerIds, std::optional<nsecs_t> firstDownTimeInTarget,
- std::vector<InputTarget>& inputTargets) const REQUIRES(mLock) {
+ std::optional<ui::LogicalDisplayId> pointerDisplayId, std::function<void()> dump,
+ std::vector<InputTarget>& inputTargets) {
if (pointerIds.none()) {
for (const auto& target : inputTargets) {
LOG(INFO) << "Target: " << target;
@@ -3057,16 +3022,19 @@
it = inputTargets.end();
}
- const WindowInfo* windowInfo = windowHandle->getInfo();
+ const WindowInfo& windowInfo = *windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::optional<InputTarget> target =
- createInputTargetLocked(windowHandle, dispatchMode, targetFlags,
- firstDownTimeInTarget);
- if (!target) {
+ std::shared_ptr<Connection> connection = mConnectionManager.getConnection(windowInfo.token);
+ if (connection == nullptr) {
+ ALOGW("Not creating InputTarget for %s, no input channel", windowInfo.name.c_str());
return;
}
- inputTargets.push_back(*target);
+ inputTargets.push_back(
+ createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
+ mWindowInfos.getRawTransform(*windowHandle->getInfo(),
+ pointerDisplayId),
+ firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -3078,33 +3046,44 @@
LOG(ERROR) << __func__ << ": Flags don't match! new targetFlags=" << targetFlags.string()
<< ", it=" << *it;
}
- if (it->globalScaleFactor != windowInfo->globalScaleFactor) {
+ if (it->globalScaleFactor != windowInfo.globalScaleFactor) {
LOG(ERROR) << __func__ << ": Mismatch! it->globalScaleFactor=" << it->globalScaleFactor
- << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
+ << ", windowInfo->globalScaleFactor=" << windowInfo.globalScaleFactor;
}
- Result<void> result = it->addPointers(pointerIds, windowInfo->transform);
+ Result<void> result = it->addPointers(pointerIds, windowInfo.transform);
if (!result.ok()) {
- logDispatchStateLocked();
+ dump();
LOG(FATAL) << result.error().message();
}
}
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
- if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
- target.displayTransform = it->second.transform;
- }
- target.setDefaultPointerTransform(target.displayTransform);
- 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);
+ });
}
/**
@@ -3160,11 +3139,12 @@
*
* If neither of those is true, then it means the touch can be allowed.
*/
-InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+InputDispatcher::DispatcherWindowInfo::TouchOcclusionInfo
+InputDispatcher::DispatcherWindowInfo::computeTouchOcclusionInfo(
const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
const WindowInfo* windowInfo = windowHandle->getInfo();
ui::LogicalDisplayId displayId = windowInfo->displayId;
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId);
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
info.obscuringOpacity = 0;
@@ -3176,11 +3156,11 @@
}
const WindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) &&
+ windowOccludesTouchAt(*otherInfo, displayId, x, y, getDisplayTransform(displayId)) &&
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
- dumpWindowForTouchOcclusion(otherInfo, /*isTouchedWindow=*/false));
+ dumpWindowForTouchOcclusion(*otherInfo, /*isTouchedWindow=*/false));
}
// canBeObscuredBy() has returned true above, which means this window is untrusted, so
// we perform the checks below to see if the touch can be propagated or not based on the
@@ -3208,28 +3188,14 @@
}
}
if (DEBUG_TOUCH_OCCLUSION) {
- info.debugInfo.push_back(dumpWindowForTouchOcclusion(windowInfo, /*isTouchedWindow=*/true));
+ info.debugInfo.push_back(
+ dumpWindowForTouchOcclusion(*windowInfo, /*isTouchedWindow=*/true));
}
return info;
}
-std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info,
- bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %spackage=%s/%s, id=%" PRId32 ", mode=%s, alpha=%.2f, "
- "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
- "], touchableRegion=%s, window={%s}, inputConfig={%s}, "
- "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
- isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
- info->ownerUid.toString().c_str(), info->id,
- toString(info->touchOcclusionMode).c_str(), info->alpha, info->frame.left,
- info->frame.top, info->frame.right, info->frame.bottom,
- dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
- info->inputConfig.string().c_str(), toString(info->token != nullptr),
- info->applicationInfo.name.c_str(),
- binderToString(info->applicationInfo.token).c_str());
-}
-
-bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+bool InputDispatcher::DispatcherWindowInfo::isTouchTrusted(
+ const TouchOcclusionInfo& occlusionInfo) const {
if (occlusionInfo.hasBlockingOcclusion) {
ALOGW("Untrusted touch due to occlusion by %s/%s", occlusionInfo.obscuringPackage.c_str(),
occlusionInfo.obscuringUid.toString().c_str());
@@ -3245,26 +3211,27 @@
return true;
}
-bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
- float x, float y) const {
+bool InputDispatcher::DispatcherWindowInfo::isWindowObscuredAtPoint(
+ const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId);
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
const WindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) {
+ windowOccludesTouchAt(*otherInfo, displayId, x, y, getDisplayTransform(displayId))) {
return true;
}
}
return false;
}
-bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const {
+bool InputDispatcher::DispatcherWindowInfo::isWindowObscured(
+ const sp<WindowInfoHandle>& windowHandle) const {
ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesForDisplay(displayId);
const WindowInfo* windowInfo = windowHandle->getInfo();
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
@@ -3330,10 +3297,9 @@
return;
}
if (windowDisablingUserActivityInfo != nullptr) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Not poking user activity: disabled by window '%s'.",
- windowDisablingUserActivityInfo->name.c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "Not poking user activity: disabled by window '"
+ << windowDisablingUserActivityInfo->name << "'.";
return;
}
break;
@@ -3347,10 +3313,9 @@
// the apps, like system shortcuts
if (windowDisablingUserActivityInfo != nullptr &&
keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("Not poking user activity: disabled by window '%s'.",
- windowDisablingUserActivityInfo->name.c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "Not poking user activity: disabled by window '"
+ << windowDisablingUserActivityInfo->name << "'.";
return;
}
break;
@@ -3378,22 +3343,19 @@
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
connection->getInputChannelName().c_str(), eventEntry->id));
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
- "globalScaleFactor=%f, pointerIds=%s %s",
- connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(),
- inputTarget.globalScaleFactor, bitsetToString(inputTarget.getPointerIds()).c_str(),
- inputTarget.getPointerInfoString().c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName()
+ << "' ~ prepareDispatchCycle - flags=" << inputTarget.flags.string()
+ << ", globalScaleFactor=" << inputTarget.globalScaleFactor
+ << ", pointerIds=" << bitsetToString(inputTarget.getPointerIds()) << " "
+ << inputTarget.getPointerInfoString();
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
if (connection->status != Connection::Status::NORMAL) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
- connection->getInputChannelName().c_str(),
- ftl::enum_string(connection->status).c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "channel '" << connection->getInputChannelName()
+ << "' ~ Dropping event because the channel status is "
+ << ftl::enum_string(connection->status);
return;
}
@@ -3512,11 +3474,10 @@
if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
!connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
motionEntry.displayId)) {
- if (DEBUG_DISPATCH_CYCLE) {
- LOG(DEBUG) << "channel '" << connection->getInputChannelName().c_str()
- << "' ~ enqueueDispatchEntryLocked: filling in missing hover "
- "enter event";
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName().c_str()
+ << "' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
+ "event";
resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
}
@@ -3810,9 +3771,8 @@
ATRACE_NAME_IF(ATRACE_ENABLED(),
StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
connection->getInputChannelName().c_str()));
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName() << "' ~ startDispatchCycle";
while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
std::unique_ptr<DispatchEntry>& dispatchEntry = connection->outboundQueue.front();
@@ -3827,10 +3787,9 @@
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(INFO) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
// Publish the key event.
status = connection->inputPublisher
@@ -3849,10 +3808,9 @@
}
case EventEntry::Type::MOTION: {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- LOG(INFO) << "Publishing " << *dispatchEntry << " to "
- << connection->getInputChannelName();
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
status = publishMotionEvent(*connection, *dispatchEntry);
if (status == BAD_VALUE) {
@@ -3921,22 +3879,21 @@
"event to it, status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
- abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true);
+ abortBrokenDispatchCycleLocked(connection, /*notify=*/true);
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
- "waiting for the application to catch up",
- connection->getInputChannelName().c_str());
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName()
+ << "' ~ Could not publish event because the pipe is full, waiting for "
+ "the application to catch up";
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
"status=%s(%d)",
connection->getInputChannelName().c_str(), statusToString(status).c_str(),
status);
- abortBrokenDispatchCycleLocked(currentTime, connection, /*notify=*/true);
+ abortBrokenDispatchCycleLocked(connection, /*notify=*/true);
}
return;
}
@@ -3995,10 +3952,9 @@
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection,
uint32_t seq, bool handled, nsecs_t consumeTime) {
- if (DEBUG_DISPATCH_CYCLE) {
- ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
- connection->getInputChannelName().c_str(), seq, toString(handled));
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE)
+ << "channel '" << connection->getInputChannelName()
+ << "' ~ finishDispatchCycle - seq=" << seq << ", handled=" << toString(handled);
if (connection->status != Connection::Status::NORMAL) {
return;
@@ -4011,13 +3967,10 @@
postCommandLocked(std::move(command));
}
-void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection,
+void InputDispatcher::abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
- if (DEBUG_DISPATCH_CYCLE) {
- LOG(INFO) << "channel '" << connection->getInputChannelName() << "'~ " << __func__
- << " - notify=" << toString(notify);
- }
+ LOG_IF(INFO, DEBUG_DISPATCH_CYCLE) << "channel '" << connection->getInputChannelName() << "'~ "
+ << __func__ << " - notify=" << toString(notify);
// Clear the dispatch queues.
drainDispatchQueue(connection->outboundQueue);
@@ -4059,7 +4012,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);
@@ -4118,7 +4071,8 @@
} else {
// Monitor channels are never explicitly unregistered.
// We do it automatically when the remote endpoint is closed so don't warn about them.
- const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr;
+ const bool stillHaveWindowHandle =
+ mWindowInfos.findWindowHandle(connection->getToken()) != nullptr;
notify = !connection->monitor && stillHaveWindowHandle;
if (notify) {
ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",
@@ -4127,7 +4081,7 @@
}
// Remove the channel.
- removeInputChannelLocked(connection->getToken(), notify);
+ removeInputChannelLocked(connection, notify);
return 0; // remove the callback
}
@@ -4141,23 +4095,27 @@
// Generate cancellations for touched windows first. This is to avoid generating cancellations
// through a non-touched window if there are more than one window for an input channel.
if (cancelPointers) {
- for (const auto& [displayId, touchState] : mTouchStatesByDisplay) {
- if (options.displayId.has_value() && options.displayId != displayId) {
- continue;
- }
- for (const auto& touchedWindow : touchState.windows) {
- synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
- }
+ if (options.displayId.has_value()) {
+ mTouchStates.forAllTouchedWindowsOnDisplay(
+ options.displayId.value(), [&](const sp<gui::WindowInfoHandle>& windowHandle) {
+ base::ScopedLockAssertion assumeLocked(mLock);
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
+ });
+ } else {
+ mTouchStates.forAllTouchedWindows([&](const sp<gui::WindowInfoHandle>& windowHandle) {
+ base::ScopedLockAssertion assumeLocked(mLock);
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
+ });
}
}
// Follow up by generating cancellations for all windows, because we don't explicitly track
// the windows that have an ongoing focus event stream.
if (cancelNonPointers) {
- for (const auto& [_, handles] : mWindowHandlesByDisplay) {
- for (const auto& windowHandle : handles) {
- synthesizeCancelationEventsForWindowLocked(windowHandle, options);
- }
- }
+ mWindowInfos.forEachWindowHandle(
+ [&](const sp<android::gui::WindowInfoHandle>& windowHandle) {
+ base::ScopedLockAssertion assumeLocked(mLock);
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
+ });
}
// Cancel monitors.
@@ -4166,12 +4124,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(
@@ -4189,7 +4147,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;
@@ -4218,12 +4176,11 @@
return;
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
- "with reality: %s, mode=%s.",
- connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
- ftl::enum_string(options.mode).c_str());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "channel '" << connection->getInputChannelName() << "' ~ Synthesized "
+ << cancelationEvents.size()
+ << " cancelation events to bring channel back in sync with reality: " << options.reason
+ << ", mode=" << ftl::enum_string(options.mode) << ".";
std::string reason = std::string("reason=").append(options.reason);
android_log_event_list(LOGTAG_INPUT_CANCEL)
@@ -4276,16 +4233,22 @@
sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
mDragState.reset();
}
- addPointerWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- motionEntry.downTime, targets);
+ mTouchStates
+ .addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ motionEntry.downTime,
+ /*pointerDisplayId=*/std::nullopt,
+ std::bind_front(&InputDispatcher::
+ logDispatchStateLocked,
+ this),
+ targets);
} else {
targets.emplace_back(fallbackTarget);
- const auto it = mDisplayInfos.find(motionEntry.displayId);
- if (it != mDisplayInfos.end()) {
- targets.back().displayTransform = it->second.transform;
- targets.back().setDefaultPointerTransform(it->second.transform);
- }
+ // Since we don't have a window, use the display transform as the raw transform.
+ const ui::Transform displayTransform =
+ mWindowInfos.getDisplayTransform(motionEntry.displayId);
+ targets.back().rawTransform = displayTransform;
+ targets.back().setDefaultPointerTransform(displayTransform);
}
logOutboundMotionDetails("cancel - ", motionEntry);
break;
@@ -4334,17 +4297,12 @@
return;
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
- connection->getInputChannelName().c_str(), downEvents.size());
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "channel '" << connection->getInputChannelName() << "' ~ Synthesized "
+ << downEvents.size() << " down events to ensure consistent event stream.";
- const auto [_, touchedWindowState, displayId] =
- findTouchStateWindowAndDisplayLocked(connection->getToken());
- if (touchedWindowState == nullptr) {
- LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token";
- }
- const auto& windowHandle = touchedWindowState->windowHandle;
+ const auto [windowHandle, displayId] =
+ mTouchStates.findExistingTouchedWindowHandleAndDisplay(connection->getToken());
const bool wasEmpty = connection->outboundQueue.empty();
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
@@ -4362,16 +4320,21 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- addPointerWindowTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
- targetFlags, pointerIds, motionEntry.downTime,
- targets);
+ mTouchStates
+ .addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
+ targetFlags, pointerIds, motionEntry.downTime,
+ /*pointerDisplayId=*/std::nullopt,
+ std::bind_front(&InputDispatcher::
+ logDispatchStateLocked,
+ this),
+ targets);
} else {
targets.emplace_back(connection, targetFlags);
- const auto it = mDisplayInfos.find(motionEntry.displayId);
- if (it != mDisplayInfos.end()) {
- targets.back().displayTransform = it->second.transform;
- targets.back().setDefaultPointerTransform(it->second.transform);
- }
+ // Since we don't have a window, use the display transform as the raw transform.
+ const ui::Transform displayTransform =
+ mWindowInfos.getDisplayTransform(motionEntry.displayId);
+ targets.back().rawTransform = displayTransform;
+ targets.back().setDefaultPointerTransform(displayTransform);
}
logOutboundMotionDetails("down - ", motionEntry);
break;
@@ -4467,19 +4430,19 @@
std::scoped_lock _l(mLock);
// Reset key repeating in case a keyboard device was added or removed or something.
resetKeyRepeatLocked();
- mLatencyTracker.setInputDevices(args.inputDeviceInfos);
+ mInputDevices = args.inputDeviceInfos;
}
void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
- ALOGD_IF(debugInboundEventDetails(),
- "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
- ", deviceId=%d, source=%s, displayId=%s, policyFlags=0x%x, action=%s, flags=0x%x, "
- "keyCode=%s, scanCode=0x%x, metaState=0x%x, "
- "downTime=%" PRId64,
- args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
- args.displayId.toString().c_str(), args.policyFlags,
- KeyEvent::actionToString(args.action), args.flags, KeyEvent::getLabel(args.keyCode),
- args.scanCode, args.metaState, args.downTime);
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyKey - id=" << args.id << ", eventTime=" << args.eventTime
+ << ", deviceId=" << args.deviceId
+ << ", source=" << inputEventSourceToString(args.source)
+ << ", displayId=" << args.displayId.toString() << ", policyFlags=0x" << std::hex
+ << args.policyFlags << ", action=" << KeyEvent::actionToString(args.action)
+ << ", flags=0x" << args.flags << ", keyCode=" << KeyEvent::getLabel(args.keyCode)
+ << ", scanCode=0x" << args.scanCode << ", metaState=0x" << args.metaState
+ << ", downTime=" << std::dec << args.downTime;
Result<void> keyCheck = validateKeyEvent(args.action);
if (!keyCheck.ok()) {
LOG(ERROR) << "invalid key event: " << keyCheck.error();
@@ -4607,8 +4570,9 @@
args.displayId.toString().c_str()));
Result<void> result =
it->second.processMovement(args.deviceId, args.source, args.action,
- args.getPointerCount(), args.pointerProperties.data(),
- args.pointerCoords.data(), args.flags);
+ args.actionButton, args.getPointerCount(),
+ args.pointerProperties.data(), args.pointerCoords.data(),
+ args.flags, args.buttonState);
if (!result.ok()) {
LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump();
}
@@ -4631,21 +4595,13 @@
if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
// Set the flag anyway if we already have an ongoing gesture. That would allow us to
// complete the processing of the current stroke.
- const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId);
- if (touchStateIt != mTouchStatesByDisplay.end()) {
- const TouchState& touchState = touchStateIt->second;
- if (touchState.hasTouchingPointers(args.deviceId) ||
- touchState.hasHoveringPointers(args.deviceId)) {
- policyFlags |= POLICY_FLAG_PASS_TO_USER;
- }
+ if (mTouchStates.hasTouchingOrHoveringPointers(args.displayId, args.deviceId)) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
if (shouldSendMotionToInputFilterLocked(args)) {
- ui::Transform displayTransform;
- if (const auto it = mDisplayInfos.find(args.displayId); it != mDisplayInfos.end()) {
- displayTransform = it->second.transform;
- }
+ ui::Transform displayTransform = mWindowInfos.getDisplayTransform(args.displayId);
mLock.unlock();
@@ -4696,12 +4652,10 @@
}
void InputDispatcher::notifySensor(const NotifySensorArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
- " sensorType=%s",
- args.id, args.eventTime, args.deviceId, args.source,
- ftl::enum_string(args.sensorType).c_str());
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifySensor - id=" << args.id << " eventTime=" << args.eventTime
+ << ", deviceId=" << args.deviceId << ", source=0x" << std::hex << args.source
+ << std::dec << ", sensorType=" << ftl::enum_string(args.sensorType);
bool needWake = false;
{ // acquire lock
@@ -4723,10 +4677,9 @@
}
void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args.eventTime,
- args.deviceId, args.isOn);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyVibratorState - eventTime=" << args.eventTime << ", device=" << args.deviceId
+ << ", isOn=" << args.isOn;
mPolicy.notifyVibratorState(args.deviceId, args.isOn);
}
@@ -4735,11 +4688,10 @@
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
- "switchMask=0x%08x",
- args.eventTime, args.policyFlags, args.switchValues, args.switchMask);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifySwitch - eventTime=" << args.eventTime << ", policyFlags=0x" << std::hex
+ << args.policyFlags << ", switchValues=0x" << std::setfill('0') << std::setw(8)
+ << args.switchValues << ", switchMask=0x" << std::setw(8) << args.switchMask;
uint32_t policyFlags = args.policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4748,10 +4700,8 @@
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
// TODO(b/308677868) Remove device reset from the InputListener interface
- if (debugInboundEventDetails()) {
- ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime,
- args.deviceId);
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyDeviceReset - eventTime=" << args.eventTime << ", deviceId=" << args.deviceId;
bool needWake = false;
{ // acquire lock
@@ -4772,10 +4722,9 @@
}
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
- if (debugInboundEventDetails()) {
- ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args.eventTime,
- args.request.isEnable() ? "true" : "false");
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << "notifyPointerCaptureChanged - eventTime=%" << args.eventTime
+ << ", enabled=" << toString(args.request.isEnable());
bool needWake = false;
{ // acquire lock
@@ -4812,9 +4761,10 @@
Result<void> result =
verifier.processMovement(deviceId, motionEvent.getSource(), motionEvent.getAction(),
- motionEvent.getPointerCount(),
+ motionEvent.getActionButton(), motionEvent.getPointerCount(),
motionEvent.getPointerProperties(),
- motionEvent.getSamplePointerCoords(), flags);
+ motionEvent.getSamplePointerCoords(), flags,
+ motionEvent.getButtonState());
if (!result.ok()) {
logDispatchStateLocked();
LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error();
@@ -4834,12 +4784,10 @@
return InputEventInjectionResult::FAILED;
}
- if (debugInboundEventDetails()) {
- LOG(INFO) << __func__ << ": targetUid=" << toString(targetUid, &uidString)
- << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
- << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec
- << ", event=" << *event;
- }
+ LOG_IF(INFO, debugInboundEventDetails())
+ << __func__ << ": targetUid=" << toString(targetUid, &uidString)
+ << ", syncMode=" << ftl::enum_string(syncMode) << ", timeout=" << timeout.count()
+ << "ms, policyFlags=0x" << std::hex << policyFlags << std::dec << ", event=" << *event;
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
policyFlags |= POLICY_FLAG_INJECTED | POLICY_FLAG_TRUSTED;
@@ -4931,6 +4879,10 @@
flags |= AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
}
+ if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL) {
+ flags |= AMOTION_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
+ }
+
mLock.lock();
if (shouldRejectInjectedMotionLocked(motionEvent, resolvedDeviceId, displayId,
@@ -4939,6 +4891,14 @@
return InputEventInjectionResult::FAILED;
}
+ if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
+ // Set the flag anyway if we already have an ongoing motion gesture. That
+ // would allow us to complete the processing of the current stroke.
+ if (mTouchStates.hasTouchingOrHoveringPointers(displayId, resolvedDeviceId)) {
+ policyFlags |= POLICY_FLAG_PASS_TO_USER;
+ }
+ }
+
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
const size_t pointerCount = motionEvent.getPointerCount();
const std::vector<PointerProperties>
@@ -5004,9 +4964,7 @@
bool needWake = false;
while (!injectedEntries.empty()) {
- if (DEBUG_INJECTION) {
- LOG(INFO) << "Injecting " << injectedEntries.front()->getDescription();
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "Injecting " << injectedEntries.front()->getDescription();
needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
}
@@ -5032,10 +4990,8 @@
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
- if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Timed out waiting for injection result "
- "to become available.");
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "injectInputEvent - Timed out waiting for "
+ "injection result to become available.";
injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -5046,16 +5002,14 @@
if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
while (injectionState->pendingForegroundDispatches != 0) {
- if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
- injectionState->pendingForegroundDispatches);
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "injectInputEvent - Waiting for "
+ << injectionState->pendingForegroundDispatches
+ << " pending foreground dispatches.";
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
- if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Timed out waiting for pending foreground "
- "dispatches to finish.");
- }
+ LOG_IF(INFO, DEBUG_INJECTION)
+ << "injectInputEvent - Timed out waiting for pending foreground "
+ "dispatches to finish.";
injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -5066,10 +5020,8 @@
}
} // release lock
- if (DEBUG_INJECTION) {
- LOG(INFO) << "injectInputEvent - Finished with result "
- << ftl::enum_string(injectionResult);
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "injectInputEvent - Finished with result "
+ << ftl::enum_string(injectionResult);
return injectionResult;
}
@@ -5115,10 +5067,8 @@
}
InjectionState& injectionState = *entry.injectionState;
- if (DEBUG_INJECTION) {
- LOG(INFO) << "Setting input event injection result to "
- << ftl::enum_string(injectionResult);
- }
+ LOG_IF(INFO, DEBUG_INJECTION) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
if (injectionState.injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
@@ -5149,9 +5099,8 @@
MotionEntry& entry, const ui::Transform& injectedTransform) const {
// Input injection works in the logical display coordinate space, but the input pipeline works
// display space, so we need to transform the injected events accordingly.
- const auto it = mDisplayInfos.find(entry.displayId);
- if (it == mDisplayInfos.end()) return;
- const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform;
+ const ui::Transform displayTransform = mWindowInfos.getDisplayTransform(entry.displayId);
+ const auto& transformToDisplay = displayTransform.inverse() * injectedTransform;
if (entry.xCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
entry.yCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
@@ -5184,14 +5133,7 @@
}
}
-const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked(
- ui::LogicalDisplayId displayId) const {
- static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
- auto it = mWindowHandlesByDisplay.find(displayId);
- return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
-}
-
-sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandle(
const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId) const {
if (windowHandleToken == nullptr) {
return nullptr;
@@ -5210,15 +5152,26 @@
}
// Only look through the requested display.
- for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesLocked(*displayId)) {
- if (windowHandle->getToken() == windowHandleToken) {
+ return findWindowHandleOnDisplay(windowHandleToken, *displayId);
+}
+
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const {
+ if (windowHandleToken == nullptr) {
+ return nullptr;
+ }
+
+ sp<WindowInfoHandle> windowHandle;
+ for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) {
+ windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId);
+ if (windowHandle != nullptr) {
return windowHandle;
}
}
return nullptr;
}
-sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
+bool InputDispatcher::DispatcherWindowInfo::isWindowPresent(
const sp<WindowInfoHandle>& windowHandle) const {
for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
for (const sp<WindowInfoHandle>& handle : windowHandles) {
@@ -5230,27 +5183,164 @@
windowHandle->getName().c_str(), displayId.toString().c_str(),
windowHandle->getInfo()->displayId.toString().c_str());
}
- return handle;
+ return true;
}
}
}
- return nullptr;
+ return false;
}
sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(
ui::LogicalDisplayId displayId) const {
sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
- return getWindowHandleLocked(focusedToken, displayId);
+ return mWindowInfos.findWindowHandle(focusedToken, displayId);
}
-ui::Transform InputDispatcher::getTransformLocked(ui::LogicalDisplayId displayId) const {
+void InputDispatcher::DispatcherWindowInfo::setWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId, std::vector<sp<WindowInfoHandle>>&& windowHandles) {
+ // Insert or replace
+ mWindowHandlesByDisplay[displayId] = std::move(windowHandles);
+}
+
+void InputDispatcher::DispatcherWindowInfo::setDisplayInfos(
+ const std::vector<android::gui::DisplayInfo>& displayInfos) {
+ mDisplayInfos.clear();
+ for (const auto& displayInfo : displayInfos) {
+ mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
+ }
+}
+
+void InputDispatcher::DispatcherWindowInfo::removeDisplay(ui::LogicalDisplayId displayId) {
+ mWindowHandlesByDisplay.erase(displayId);
+}
+
+const std::vector<sp<android::gui::WindowInfoHandle>>&
+InputDispatcher::DispatcherWindowInfo::getWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId) const {
+ static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
+ const auto it = mWindowHandlesByDisplay.find(displayId);
+ return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
+}
+
+void InputDispatcher::DispatcherWindowInfo::forEachWindowHandle(
+ std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const {
+ for (const auto& [_, windowHandles] : mWindowHandlesByDisplay) {
+ for (const auto& windowHandle : windowHandles) {
+ f(windowHandle);
+ }
+ }
+}
+
+void InputDispatcher::DispatcherWindowInfo::forEachDisplayId(
+ std::function<void(ui::LogicalDisplayId)> f) const {
+ for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
+ f(displayId);
+ }
+}
+
+ui::Transform InputDispatcher::DispatcherWindowInfo::getDisplayTransform(
+ ui::LogicalDisplayId displayId) const {
auto displayInfoIt = mDisplayInfos.find(displayId);
return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
: kIdentityTransform;
}
-bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window,
- const MotionEntry& motionEntry) const {
+ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
+ const android::gui::WindowInfo& windowInfo,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId) const {
+ // TODO(b/383092013): Handle TOPOLOGY_AWARE window flag.
+ // For now, we assume all windows are topology-aware and can handle cross-display streams.
+ if (com::android::input::flags::connected_displays_cursor() && pointerDisplayId.has_value() &&
+ *pointerDisplayId != windowInfo.displayId) {
+ // Sending pointer to a different display than the window. This is a
+ // cross-display drag gesture, so always use the new display's transform.
+ return getDisplayTransform(*pointerDisplayId);
+ }
+ // If the window has a cloneLayerStackTransform, always use it as the transform for the "getRaw"
+ // APIs. If not, fall back to using the DisplayInfo transform of the window's display
+ bool useClonedScreenCoordinates = (input_flags::use_cloned_screen_coordinates_as_raw() &&
+ windowInfo.cloneLayerStackTransform);
+ if (useClonedScreenCoordinates) {
+ return *windowInfo.cloneLayerStackTransform;
+ }
+ return getDisplayTransform(windowInfo.displayId);
+}
+
+ui::LogicalDisplayId InputDispatcher::DispatcherWindowInfo::getPrimaryDisplayId(
+ ui::LogicalDisplayId displayId) const {
+ if (mTopology.graph.contains(displayId)) {
+ return mTopology.primaryDisplayId;
+ }
+ return displayId;
+}
+
+bool InputDispatcher::DispatcherWindowInfo::areDisplaysConnected(
+ ui::LogicalDisplayId display1, ui::LogicalDisplayId display2) const {
+ return display1 == display2 ||
+ (mTopology.graph.contains(display1) && mTopology.graph.contains(display2));
+}
+
+std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
+ std::string dump;
+ if (!mWindowHandlesByDisplay.empty()) {
+ for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
+ dump += StringPrintf("Display: %s\n", displayId.toString().c_str());
+ if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+ const auto& displayInfo = it->second;
+ dump += StringPrintf(INDENT "logicalSize=%dx%d\n", displayInfo.logicalWidth,
+ displayInfo.logicalHeight);
+ displayInfo.transform.dump(dump, "transform", INDENT3);
+ } else {
+ dump += INDENT "No DisplayInfo found!\n";
+ }
+
+ if (!windowHandles.empty()) {
+ dump += INDENT "Windows:\n";
+ for (size_t i = 0; i < windowHandles.size(); i++) {
+ dump += StringPrintf(INDENT2 "%zu: %s", i,
+ streamableToString(*windowHandles[i]).c_str());
+ }
+ } else {
+ dump += INDENT "Windows: <none>\n";
+ }
+ }
+ } else {
+ dump += "Displays: <none>\n";
+ }
+ dump += StringPrintf("mMaximumObscuringOpacityForTouch: %f\n",
+ mMaximumObscuringOpacityForTouch);
+ dump += "DisplayTopologyGraph:\n";
+ dump += addLinePrefix(mTopology.dump(), INDENT);
+ dump += "\n";
+ return dump;
+}
+
+std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays(
+ ui::LogicalDisplayId displayId) const {
+ if (!mTopology.graph.contains(displayId)) {
+ return {displayId};
+ }
+
+ std::vector<ui::LogicalDisplayId> connectedDisplays;
+ for (auto it : mTopology.graph) {
+ connectedDisplays.push_back(it.first);
+ }
+ return connectedDisplays;
+}
+
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const {
+ for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) {
+ if (windowHandle->getToken() == windowHandleToken) {
+ return windowHandle;
+ }
+ }
+ return nullptr;
+}
+
+bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion(
+ const sp<android::gui::WindowInfoHandle>& window,
+ const android::inputdispatcher::MotionEntry& motionEntry) const {
const WindowInfo& info = *window->getInfo();
// Skip spy window targets that are not valid for targeted injection.
@@ -5269,7 +5359,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());
@@ -5283,8 +5373,9 @@
// Drop events that can't be trusted due to occlusion
const auto [x, y] = resolveTouchedPosition(motionEntry);
- TouchOcclusionInfo occlusionInfo = computeTouchOcclusionInfoLocked(window, x, y);
- if (!isTouchTrustedLocked(occlusionInfo)) {
+ DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo =
+ mWindowInfos.computeTouchOcclusionInfo(window, x, y);
+ if (!mWindowInfos.isTouchTrusted(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y);
for (const auto& log : occlusionInfo.debugInfo) {
@@ -5297,13 +5388,13 @@
}
// Drop touch events if requested by input feature
- if (shouldDropInput(motionEntry, window)) {
+ if (shouldDropInput(motionEntry, window, mWindowInfos)) {
return false;
}
// Ignore touches if stylus is down anywhere on screen
if (info.inputConfig.test(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH) &&
- isStylusActiveInDisplay(info.displayId, mTouchStatesByDisplay)) {
+ isStylusActiveInDisplay(info.displayId)) {
LOG(INFO) << "Dropping touch from " << window->getName() << " because stylus is active";
return false;
}
@@ -5316,13 +5407,14 @@
ui::LogicalDisplayId displayId) {
if (windowInfoHandles.empty()) {
// Remove all handles on a display if there are no windows left.
- mWindowHandlesByDisplay.erase(displayId);
+ mWindowInfos.removeDisplay(displayId);
return;
}
// Since we compare the pointer of input window handles across window updates, we need
// to make sure the handle object for the same window stays unchanged across updates.
- const std::vector<sp<WindowInfoHandle>>& oldHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& oldHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
std::unordered_map<int32_t /*id*/, sp<WindowInfoHandle>> oldHandlesById;
for (const sp<WindowInfoHandle>& handle : oldHandles) {
oldHandlesById[handle->getId()] = handle;
@@ -5331,7 +5423,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 =
@@ -5361,8 +5453,7 @@
}
}
- // Insert or replace
- mWindowHandlesByDisplay[displayId] = newHandles;
+ mWindowInfos.setWindowHandlesForDisplay(displayId, std::move(newHandles));
}
/**
@@ -5412,12 +5503,14 @@
}
// Copy old handles for release if they are no longer present.
- const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>> oldWindowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
const sp<WindowInfoHandle> removedFocusedWindowHandle = getFocusedWindowHandleLocked(displayId);
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ mWindowInfos.getWindowHandlesForDisplay(displayId);
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
@@ -5425,69 +5518,38 @@
onFocusChangedLocked(*changes, traceContext.getTracker(), removedFocusedWindowHandle);
}
- if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
- TouchState& state = it->second;
- for (size_t i = 0; i < state.windows.size();) {
- TouchedWindow& touchedWindow = state.windows[i];
- if (getWindowHandleLocked(touchedWindow.windowHandle) != nullptr) {
- i++;
- continue;
- }
- LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
- << " in display %" << displayId;
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "touched window was removed", traceContext.getTracker());
- synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
- // Since we are about to drop the touch, cancel the events for the wallpaper as
- // well.
- if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
- touchedWindow.windowHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
- for (const DeviceId deviceId : touchedWindow.getTouchingDeviceIds()) {
- if (const auto& ww = state.getWallpaperWindow(deviceId); ww != nullptr) {
- options.deviceId = deviceId;
- synthesizeCancelationEventsForWindowLocked(ww, options);
- }
- }
- }
- state.windows.erase(state.windows.begin() + i);
- }
-
- // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
- // could just clear the state here.
- if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId &&
- std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
- windowHandles.end()) {
- ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str());
- sendDropWindowCommandLocked(nullptr, 0, 0);
- mDragState.reset();
+ CancelationOptions pointerCancellationOptions(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "touched window was removed",
+ traceContext.getTracker());
+ CancelationOptions hoverCancellationOptions(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
+ "WindowInfo changed", traceContext.getTracker());
+ const std::list<DispatcherTouchState::CancellationArgs> cancellations =
+ mTouchStates.updateFromWindowInfo(displayId);
+ for (const auto& cancellationArgs : cancellations) {
+ switch (cancellationArgs.mode) {
+ case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
+ pointerCancellationOptions.deviceId = cancellationArgs.deviceId;
+ synthesizeCancelationEventsForWindowLocked(cancellationArgs.windowHandle,
+ pointerCancellationOptions);
+ break;
+ case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+ hoverCancellationOptions.deviceId = cancellationArgs.deviceId;
+ synthesizeCancelationEventsForWindowLocked(cancellationArgs.windowHandle,
+ hoverCancellationOptions);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unexpected cancellation Mode");
}
}
- // Check if the hovering should stop because the window is no longer eligible to receive it
- // (for example, if the touchable region changed)
- if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
- TouchState& state = it->second;
- for (TouchedWindow& touchedWindow : state.windows) {
- std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
- [this, displayId, &touchedWindow](const PointerProperties& properties, float x,
- float y) REQUIRES(mLock) {
- const bool isStylus = properties.toolType == ToolType::STYLUS;
- const ui::Transform displayTransform = getTransformLocked(displayId);
- const bool stillAcceptsTouch =
- windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(),
- displayId, x, y, isStylus, displayTransform);
- return !stillAcceptsTouch;
- });
-
- for (DeviceId deviceId : erasedDevices) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
- "WindowInfo changed",
- traceContext.getTracker());
- options.deviceId = deviceId;
- synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
- }
- }
+ // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
+ // could just clear the state here.
+ if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId &&
+ std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
+ windowHandles.end()) {
+ ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str());
+ sendDropWindowCommandLocked(nullptr, 0, 0);
+ mDragState.reset();
}
// Release information for windows that are no longer present.
@@ -5495,22 +5557,87 @@
// Otherwise, they might stick around until the window handle is destroyed
// which might not happen until the next GC.
for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
- if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
- if (DEBUG_FOCUS) {
- ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
- }
+ if (!mWindowInfos.isWindowPresent(oldWindowHandle)) {
+ LOG_IF(INFO, DEBUG_FOCUS) << "Window went away: " << oldWindowHandle->getName();
oldWindowHandle->releaseChannel();
}
}
}
+std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
+InputDispatcher::DispatcherTouchState::updateFromWindowInfo(ui::LogicalDisplayId displayId) {
+ std::list<CancellationArgs> cancellations;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](TouchState& state) {
+ cancellations.splice(cancellations.end(),
+ eraseRemovedWindowsFromWindowInfo(state, displayId));
+ cancellations.splice(cancellations.end(),
+ updateHoveringStateFromWindowInfo(state, displayId));
+ return false;
+ });
+ return cancellations;
+}
+
+std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
+InputDispatcher::DispatcherTouchState::eraseRemovedWindowsFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId) {
+ std::list<CancellationArgs> cancellations;
+ for (auto it = state.windows.begin(); it != state.windows.end();) {
+ TouchedWindow& touchedWindow = *it;
+ if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) {
+ it++;
+ continue;
+ }
+ LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
+ << " in display %" << displayId;
+ cancellations.emplace_back(touchedWindow.windowHandle,
+ CancelationOptions::Mode::CANCEL_POINTER_EVENTS);
+ // Since we are about to drop the touch, cancel the events for the wallpaper as well.
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
+ touchedWindow.windowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
+ for (const DeviceId deviceId : touchedWindow.getTouchingDeviceIds()) {
+ if (const auto& ww = state.getWallpaperWindow(deviceId); ww != nullptr) {
+ cancellations.emplace_back(ww, CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ deviceId);
+ }
+ }
+ }
+ it = state.windows.erase(it);
+ }
+ return cancellations;
+}
+
+std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
+InputDispatcher::DispatcherTouchState::updateHoveringStateFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId) {
+ std::list<CancellationArgs> cancellations;
+ // Check if the hovering should stop because the window is no longer eligible to receive it
+ // (for example, if the touchable region changed)
+ ui::Transform displayTransform = mWindowInfos.getDisplayTransform(displayId);
+ for (TouchedWindow& touchedWindow : state.windows) {
+ std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
+ [&](const PointerProperties& properties, float x, float y) {
+ const bool isStylus = properties.toolType == ToolType::STYLUS;
+ const bool stillAcceptsTouch =
+ windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(), displayId,
+ x, y, isStylus, displayTransform);
+ return !stillAcceptsTouch;
+ });
+
+ for (DeviceId deviceId : erasedDevices) {
+ cancellations.emplace_back(touchedWindow.windowHandle,
+ CancelationOptions::Mode::CANCEL_HOVER_EVENTS, deviceId);
+ }
+ }
+ return cancellations;
+}
+
void InputDispatcher::setFocusedApplication(
ui::LogicalDisplayId displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
- if (DEBUG_FOCUS) {
- ALOGD("setFocusedApplication displayId=%s %s", displayId.toString().c_str(),
- inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setFocusedApplication displayId=" << displayId.toString() << " "
+ << (inputApplicationHandle ? inputApplicationHandle->getName()
+ : "<nullptr>");
{ // acquire lock
std::scoped_lock _l(mLock);
setFocusedApplicationLocked(displayId, inputApplicationHandle);
@@ -5560,9 +5687,7 @@
* display. The display-specified events won't be affected.
*/
void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) {
- if (DEBUG_FOCUS) {
- ALOGD("setFocusedDisplay displayId=%s", displayId.toString().c_str());
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setFocusedDisplay displayId=" << displayId.toString();
{ // acquire lock
std::scoped_lock _l(mLock);
ScopedSyntheticEventTracer traceContext(mTracer);
@@ -5572,7 +5697,7 @@
mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (oldFocusedWindowToken != nullptr) {
const auto windowHandle =
- getWindowHandleLocked(oldFocusedWindowToken, mFocusedDisplayId);
+ mWindowInfos.findWindowHandle(oldFocusedWindowToken, mFocusedDisplayId);
if (windowHandle == nullptr) {
LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
@@ -5616,9 +5741,8 @@
}
void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
- if (DEBUG_FOCUS) {
- ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setInputDispatchMode: enabled=" << enabled
+ << ", frozen=" << frozen;
bool changed;
{ // acquire lock
@@ -5648,9 +5772,7 @@
}
void InputDispatcher::setInputFilterEnabled(bool enabled) {
- if (DEBUG_FOCUS) {
- ALOGD("setInputFilterEnabled: enabled=%d", enabled);
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "setInputFilterEnabled: enabled=" << enabled;
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -5672,14 +5794,16 @@
bool needWake = false;
{
std::scoped_lock lock(mLock);
- ALOGD_IF(DEBUG_TOUCH_MODE,
- "Request to change touch mode to %s (calling pid=%s, uid=%s, "
- "hasPermission=%s, target displayId=%s, mTouchModePerDisplay[displayId]=%s)",
- toString(inTouchMode), pid.toString().c_str(), uid.toString().c_str(),
- toString(hasPermission), displayId.toString().c_str(),
- mTouchModePerDisplay.count(displayId) == 0
- ? "not set"
- : std::to_string(mTouchModePerDisplay[displayId]).c_str());
+ LOG_IF(INFO, DEBUG_TOUCH_MODE)
+ << "Request to change touch mode to " << toString(inTouchMode)
+ << " (calling pid=" << pid.toString() << ", uid=" << uid.toString()
+ << ", hasPermission=" << toString(hasPermission)
+ << ", target displayId=" << displayId.toString()
+ << ", mTouchModePerDisplay[displayId]="
+ << (mTouchModePerDisplay.count(displayId) == 0
+ ? "not set"
+ : std::to_string(mTouchModePerDisplay[displayId]))
+ << ")";
auto touchModeIt = mTouchModePerDisplay.find(displayId);
if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) {
@@ -5711,7 +5835,7 @@
if (focusedToken == nullptr) {
return false;
}
- sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken);
+ sp<WindowInfoHandle> windowHandle = mWindowInfos.findWindowHandle(focusedToken);
return isWindowOwnedBy(windowHandle, pid, uid);
}
@@ -5719,105 +5843,52 @@
return std::find_if(mInteractionConnectionTokens.begin(), mInteractionConnectionTokens.end(),
[&](const sp<IBinder>& connectionToken) REQUIRES(mLock) {
const sp<WindowInfoHandle> windowHandle =
- getWindowHandleLocked(connectionToken);
+ mWindowInfos.findWindowHandle(connectionToken);
return isWindowOwnedBy(windowHandle, pid, uid);
}) != mInteractionConnectionTokens.end();
}
void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
- if (opacity < 0 || opacity > 1) {
- LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
- return;
- }
-
std::scoped_lock lock(mLock);
- mMaximumObscuringOpacityForTouch = opacity;
-}
-
-std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
-InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) {
- for (auto& [displayId, state] : mTouchStatesByDisplay) {
- for (TouchedWindow& w : state.windows) {
- if (w.windowHandle->getToken() == token) {
- return std::make_tuple(&state, &w, displayId);
- }
- }
- }
- return std::make_tuple(nullptr, nullptr, ui::LogicalDisplayId::DEFAULT);
-}
-
-std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
-InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const {
- return const_cast<InputDispatcher*>(this)->findTouchStateWindowAndDisplayLocked(token);
-}
-
-bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle,
- DeviceId deviceId) const {
- const auto& [touchState, touchedWindow, _] =
- findTouchStateWindowAndDisplayLocked(windowHandle->getToken());
- if (touchState == nullptr) {
- // No touching pointers at all
- return false;
- }
- return touchState->hasTouchingPointers(deviceId);
+ mWindowInfos.setMaximumObscuringOpacityForTouch(opacity);
}
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop) {
+ bool isDragDrop, bool transferEntireGesture) {
if (fromToken == toToken) {
- if (DEBUG_FOCUS) {
- ALOGD("Trivial transfer to same window.");
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Trivial transfer to same window.";
return true;
}
{ // acquire lock
std::scoped_lock _l(mLock);
- // Find the target touch state and touched window by fromToken.
- auto [state, touchedWindow, displayId] = findTouchStateWindowAndDisplayLocked(fromToken);
+ ScopedSyntheticEventTracer traceContext(mTracer);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "transferring touch from this window to another window",
+ traceContext.getTracker());
- if (state == nullptr || touchedWindow == nullptr) {
- ALOGD("Touch transfer failed because from window is not being touched.");
- return false;
- }
- std::set<DeviceId> deviceIds = touchedWindow->getTouchingDeviceIds();
- if (deviceIds.size() != 1) {
- LOG(INFO) << "Can't transfer touch. Currently touching devices: " << dumpSet(deviceIds)
- << " for window: " << touchedWindow->dump();
- return false;
- }
- const DeviceId deviceId = *deviceIds.begin();
-
- const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
- const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
- if (!toWindowHandle) {
- ALOGW("Cannot transfer touch because the transfer target window was not found.");
+ auto result = mTouchStates.transferTouchGesture(fromToken, toToken, transferEntireGesture);
+ if (!result.has_value()) {
return false;
}
- if (DEBUG_FOCUS) {
- ALOGD("%s: fromWindowHandle=%s, toWindowHandle=%s", __func__,
- touchedWindow->windowHandle->getName().c_str(),
- toWindowHandle->getName().c_str());
+ const auto& [toWindowHandle, deviceId, pointers, cancellations, pointerDowns] =
+ result.value();
+
+ for (const auto& cancellationArgs : cancellations) {
+ LOG_ALWAYS_FATAL_IF(cancellationArgs.mode !=
+ CancelationOptions::Mode::CANCEL_POINTER_EVENTS);
+ LOG_ALWAYS_FATAL_IF(cancellationArgs.deviceId.has_value());
+ synthesizeCancelationEventsForWindowLocked(cancellationArgs.windowHandle, options);
}
- // Erase old window.
- ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
- std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId);
- state->removeWindowByToken(fromToken);
-
- // Add new window.
- nsecs_t downTimeInTarget = now();
- ftl::Flags<InputTarget::Flags> newTargetFlags =
- oldTargetFlags & (InputTarget::Flags::SPLIT);
- if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
- newTargetFlags |= InputTarget::Flags::FOREGROUND;
+ for (const auto& pointerDownArgs : pointerDowns) {
+ synthesizePointerDownEventsForConnectionLocked(pointerDownArgs.downTimeInTarget,
+ pointerDownArgs.connection,
+ pointerDownArgs.targetFlags,
+ traceContext.getTracker());
}
- // Transferring touch focus using this API should not effect the focused window.
- newTargetFlags |= InputTarget::Flags::NO_FOCUS_CHANGE;
- state->addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags,
- deviceId, pointers, downTimeInTarget);
// Store the dragging window.
if (isDragDrop) {
@@ -5830,30 +5901,6 @@
const size_t id = pointers.begin()->id;
mDragState = std::make_unique<DragState>(toWindowHandle, deviceId, id);
}
-
- // 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);
- if (fromConnection != nullptr && toConnection != nullptr) {
- fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "transferring touch from this window to another window",
- traceContext.getTracker());
- synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection);
-
- // Check if the wallpaper window should deliver the corresponding event.
- transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
- *state, deviceId, pointers, traceContext.getTracker());
-
- // Because new window may have a wallpaper window, it will merge input state from it
- // parent window, after this the firstNewPointerIdx in input state will be reset, then
- // it will cause new move event be thought inconsistent, so we should synthesize the
- // down event after it reset.
- synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
- newTargetFlags,
- traceContext.getTracker());
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -5861,33 +5908,115 @@
return true;
}
+std::optional<std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>,
+ std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>,
+ std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>>
+InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken,
+ const sp<android::IBinder>& toToken,
+ bool transferEntireGesture) {
+ // Find the target touch state and touched window by fromToken.
+ auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken);
+ if (!touchStateWindowAndDisplay.has_value()) {
+ ALOGD("Touch transfer failed because from window is not being touched.");
+ return std::nullopt;
+ }
+
+ auto [state, touchedWindow, displayId] = touchStateWindowAndDisplay.value();
+ std::set<DeviceId> deviceIds = touchedWindow.getTouchingDeviceIds();
+ if (deviceIds.size() != 1) {
+ LOG(INFO) << "Can't transfer touch. Currently touching devices: "
+ << dumpContainer(deviceIds) << " for window: " << touchedWindow.dump();
+ return std::nullopt;
+ }
+ const DeviceId deviceId = *deviceIds.begin();
+
+ const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle;
+ // TouchState displayId may not be same as window displayId, we need to lookup for toToken on
+ // all connected displays.
+ const sp<WindowInfoHandle> toWindowHandle =
+ mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId);
+ if (!toWindowHandle) {
+ ALOGW("Cannot transfer touch because the transfer target window was not found.");
+ return std::nullopt;
+ }
+
+ LOG_IF(INFO, DEBUG_FOCUS) << __func__ << ": fromWindowHandle=" << fromWindowHandle->getName()
+ << ", toWindowHandle=" << toWindowHandle->getName();
+
+ // Erase old window.
+ ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow.targetFlags;
+ std::vector<PointerProperties> pointers = touchedWindow.getTouchingPointers(deviceId);
+ state.removeWindowByToken(fromToken);
+
+ // Add new window.
+ nsecs_t downTimeInTarget = now();
+ ftl::Flags<InputTarget::Flags> newTargetFlags = oldTargetFlags & (InputTarget::Flags::SPLIT);
+ if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) {
+ newTargetFlags |= InputTarget::Flags::FOREGROUND;
+ }
+ // Transferring touch focus using this API should not effect the focused window.
+ newTargetFlags |= InputTarget::Flags::NO_FOCUS_CHANGE;
+ sp<IBinder> forwardingWindowToken;
+ if (transferEntireGesture && com::android::input::flags::allow_transfer_of_entire_gesture()) {
+ forwardingWindowToken = fromToken;
+ }
+ state.addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags,
+ deviceId, pointers, downTimeInTarget, forwardingWindowToken);
+
+ // Synthesize cancel for old window and down for new window.
+ std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken);
+ std::shared_ptr<Connection> toConnection = mConnectionManager.getConnection(toToken);
+ std::list<CancellationArgs> cancellations;
+ std::list<PointerDownArgs> pointerDowns;
+ if (fromConnection != nullptr && toConnection != nullptr) {
+ fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
+ cancellations.emplace_back(fromWindowHandle,
+ CancelationOptions::Mode::CANCEL_POINTER_EVENTS);
+
+ // Check if the wallpaper window should deliver the corresponding event.
+ auto [wallpaperCancellations, wallpaperPointerDowns] =
+ transferWallpaperTouch(fromWindowHandle, toWindowHandle, state, deviceId, pointers,
+ oldTargetFlags, newTargetFlags);
+
+ cancellations.splice(cancellations.end(), wallpaperCancellations);
+ pointerDowns.splice(pointerDowns.end(), wallpaperPointerDowns);
+
+ // Because new window may have a wallpaper window, it will merge input state from it
+ // parent window, after this the firstNewPointerIdx in input state will be reset, then
+ // it will cause new move event be thought inconsistent, so we should synthesize the
+ // down event after it reset.
+ pointerDowns.emplace_back(downTimeInTarget, toConnection, newTargetFlags);
+ }
+
+ return std::make_tuple(toWindowHandle, deviceId, pointers, cancellations, pointerDowns);
+}
+
/**
* Get the touched foreground window on the given display.
* Return null if there are no windows touched on that display, or if more than one foreground
* window is being touched.
*/
-sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(
+sp<WindowInfoHandle> InputDispatcher::DispatcherTouchState::findTouchedForegroundWindow(
ui::LogicalDisplayId displayId) const {
- auto stateIt = mTouchStatesByDisplay.find(displayId);
- if (stateIt == mTouchStatesByDisplay.end()) {
- ALOGI("No touch state on display %s", displayId.toString().c_str());
- return nullptr;
- }
-
- const TouchState& state = stateIt->second;
sp<WindowInfoHandle> touchedForegroundWindow;
- // If multiple foreground windows are touched, return nullptr
- for (const TouchedWindow& window : state.windows) {
- if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
- if (touchedForegroundWindow != nullptr) {
- ALOGI("Two or more foreground windows: %s and %s",
- touchedForegroundWindow->getName().c_str(),
- window.windowHandle->getName().c_str());
- return nullptr;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ // If multiple foreground windows are touched, return nullptr
+ for (const TouchedWindow& window : state.windows) {
+ if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
+ if (touchedForegroundWindow != nullptr) {
+ ALOGI("Two or more foreground windows: %s and %s",
+ touchedForegroundWindow->getName().c_str(),
+ window.windowHandle->getName().c_str());
+ touchedForegroundWindow = nullptr;
+ return true;
+ }
+ touchedForegroundWindow = window.windowHandle;
}
- touchedForegroundWindow = window.windowHandle;
}
- }
+ return false;
+ });
+ ALOGI_IF(touchedForegroundWindow == nullptr,
+ "No touch state or no touched foreground window on display %d", displayId.val());
return touchedForegroundWindow;
}
@@ -5897,14 +6026,15 @@
sp<IBinder> fromToken;
{ // acquire lock
std::scoped_lock _l(mLock);
- sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
+ sp<WindowInfoHandle> toWindowHandle =
+ mWindowInfos.findWindowHandle(destChannelToken, displayId);
if (toWindowHandle == nullptr) {
ALOGW("Could not find window associated with token=%p on display %s",
destChannelToken.get(), displayId.toString().c_str());
return false;
}
- sp<WindowInfoHandle> from = findTouchedForegroundWindowLocked(displayId);
+ sp<WindowInfoHandle> from = mTouchStates.findTouchedForegroundWindow(displayId);
if (from == nullptr) {
ALOGE("Could not find a source window in %s for %p", __func__, destChannelToken.get());
return false;
@@ -5913,13 +6043,12 @@
fromToken = from->getToken();
} // release lock
- return transferTouchGesture(fromToken, destChannelToken);
+ return transferTouchGesture(fromToken, destChannelToken, /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false);
}
void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
- if (DEBUG_FOCUS) {
- ALOGD("Resetting and dropping all events (%s).", reason);
- }
+ LOG_IF(INFO, DEBUG_FOCUS) << "Resetting and dropping all events (" << reason << ").";
ScopedSyntheticEventTracer traceContext(mTracer);
CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason,
@@ -5932,7 +6061,7 @@
resetNoFocusedWindowTimeoutLocked();
mAnrTracker.clear();
- mTouchStatesByDisplay.clear();
+ mTouchStates.clear();
}
void InputDispatcher::logDispatchStateLocked() const {
@@ -5956,7 +6085,7 @@
std::string windowName = "None";
if (mWindowTokenWithPointerCapture) {
const sp<WindowInfoHandle> captureWindowHandle =
- getWindowHandleLocked(mWindowTokenWithPointerCapture);
+ mWindowInfos.findWindowHandle(mWindowTokenWithPointerCapture);
windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
: "token has capture without window";
}
@@ -5990,59 +6119,19 @@
dump += mFocusResolver.dump();
dump += dumpPointerCaptureStateLocked();
- if (!mTouchStatesByDisplay.empty()) {
- dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
- for (const auto& [displayId, state] : mTouchStatesByDisplay) {
- std::string touchStateDump = addLinePrefix(state.dump(), INDENT2);
- dump += INDENT2 + displayId.toString() + " : " + touchStateDump;
- }
- } else {
- dump += INDENT "TouchStates: <no displays touched>\n";
- }
+ dump += addLinePrefix(mTouchStates.dump(), INDENT);
if (mDragState) {
dump += StringPrintf(INDENT "DragState:\n");
mDragState->dump(dump, INDENT2);
}
- if (!mWindowHandlesByDisplay.empty()) {
- for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
- dump += StringPrintf(INDENT "Display: %s\n", displayId.toString().c_str());
- if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
- const auto& displayInfo = it->second;
- dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth,
- displayInfo.logicalHeight);
- displayInfo.transform.dump(dump, "transform", INDENT4);
- } else {
- dump += INDENT2 "No DisplayInfo found!\n";
- }
-
- if (!windowHandles.empty()) {
- dump += INDENT2 "Windows:\n";
- for (size_t i = 0; i < windowHandles.size(); i++) {
- dump += StringPrintf(INDENT3 "%zu: %s", i,
- streamableToString(*windowHandles[i]).c_str());
- }
- } else {
- dump += INDENT2 "Windows: <none>\n";
- }
- }
- } else {
- dump += INDENT "Displays: <none>\n";
- }
-
- 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";
- }
+ dump += addLinePrefix(mWindowInfos.dumpDisplayAndWindowInfo(), INDENT);
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());
@@ -6084,37 +6173,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) {
@@ -6135,16 +6193,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) {}
@@ -6155,9 +6203,7 @@
};
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
- if (DEBUG_CHANNEL_CREATION) {
- ALOGD("channel '%s' ~ createInputChannel", name.c_str());
- }
+ LOG_IF(INFO, DEBUG_CHANNEL_CREATION) << "channel '" << name << "' ~ createInputChannel";
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
@@ -6170,21 +6216,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.
@@ -6210,23 +6245,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.
@@ -6237,8 +6260,14 @@
status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) {
{ // acquire lock
std::scoped_lock _l(mLock);
+ 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'
+ return BAD_VALUE;
+ }
- status_t status = removeInputChannelLocked(connectionToken, /*notify=*/false);
+ status_t status = removeInputChannelLocked(connection, /*notify=*/false);
if (status) {
return status;
}
@@ -6250,30 +6279,18 @@
return OK;
}
-status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken,
+status_t InputDispatcher::removeInputChannelLocked(const std::shared_ptr<Connection>& connection,
bool notify) {
- std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);
- if (connection == nullptr) {
- // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel'
- return BAD_VALUE;
- }
+ LOG_ALWAYS_FATAL_IF(connection == nullptr);
+ abortBrokenDispatchCycleLocked(connection, notify);
- removeConnectionLocked(connection);
+ mAnrTracker.eraseToken(connection->getToken());
+ mConnectionManager.removeConnection(connection);
- if (connection->monitor) {
- removeMonitorChannelLocked(connectionToken);
- }
-
- mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
-
- nsecs_t currentTime = now();
- abortBrokenDispatchCycleLocked(currentTime, connection, notify);
-
- 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) {
@@ -6294,49 +6311,70 @@
}
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";
return BAD_VALUE;
}
- auto [statePtr, windowPtr, displayId] = findTouchStateWindowAndDisplayLocked(token);
- if (statePtr == nullptr || windowPtr == nullptr) {
- LOG(WARNING)
- << "Attempted to pilfer points from a channel without any on-going pointer streams."
- " Ignoring.";
- return BAD_VALUE;
- }
- std::set<int32_t> deviceIds = windowPtr->getTouchingDeviceIds();
- if (deviceIds.empty()) {
- LOG(WARNING) << "Can't pilfer: no touching devices in window: " << windowPtr->dump();
- return BAD_VALUE;
+ const auto result = mTouchStates.pilferPointers(token, *requestingConnection);
+ if (!result.ok()) {
+ return result.error().code();
}
ScopedSyntheticEventTracer traceContext(mTracer);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "input channel stole pointer stream", traceContext.getTracker());
+ const auto cancellations = *result;
+ for (const auto& cancellationArgs : cancellations) {
+ LOG_ALWAYS_FATAL_IF(cancellationArgs.mode !=
+ CancelationOptions::Mode::CANCEL_POINTER_EVENTS);
+ options.displayId = cancellationArgs.displayId;
+ options.deviceId = cancellationArgs.deviceId;
+ options.pointerIds = cancellationArgs.pointerIds;
+ synthesizeCancelationEventsForWindowLocked(cancellationArgs.windowHandle, options);
+ }
+ return OK;
+}
+
+base::Result<std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>, status_t>
+InputDispatcher::DispatcherTouchState::pilferPointers(const sp<IBinder>& token,
+ const Connection& requestingConnection) {
+ auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(token);
+ if (!touchStateWindowAndDisplay.has_value()) {
+ LOG(WARNING)
+ << "Attempted to pilfer points from a channel without any on-going pointer streams."
+ " Ignoring.";
+ return Error(BAD_VALUE);
+ }
+
+ auto [state, window, displayId] = touchStateWindowAndDisplay.value();
+
+ std::set<int32_t> deviceIds = window.getTouchingDeviceIds();
+ if (deviceIds.empty()) {
+ LOG(WARNING) << "Can't pilfer: no touching devices in window: " << window.dump();
+ return Error(BAD_VALUE);
+ }
+
+ std::list<CancellationArgs> cancellations;
for (const DeviceId deviceId : deviceIds) {
- TouchState& state = *statePtr;
- TouchedWindow& window = *windowPtr;
// Send cancel events to all the input channels we're stealing from.
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "input channel stole pointer stream", traceContext.getTracker());
- options.deviceId = deviceId;
- options.displayId = displayId;
std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId);
std::bitset<MAX_POINTER_ID + 1> pointerIds = getPointerIds(pointers);
- options.pointerIds = pointerIds;
-
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
if (w.windowHandle->getToken() != token) {
- synthesizeCancelationEventsForWindowLocked(w.windowHandle, options);
+ cancellations.emplace_back(w.windowHandle,
+ CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ deviceId, displayId, pointerIds);
canceledWindows += canceledWindows.empty() ? "[" : ", ";
canceledWindows += w.windowHandle->getName();
}
}
canceledWindows += canceledWindows.empty() ? "[]" : "]";
- LOG(INFO) << "Channel " << requestingConnection->getInputChannelName()
+ LOG(INFO) << "Channel " << requestingConnection.getInputChannelName()
<< " is stealing input gesture for device " << deviceId << " from "
<< canceledWindows;
@@ -6346,14 +6384,14 @@
state.cancelPointersForWindowsExcept(deviceId, pointerIds, token);
}
- return OK;
+ return cancellations;
}
void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) {
{ // acquire lock
std::scoped_lock _l(mLock);
if (DEBUG_FOCUS) {
- const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(windowToken);
+ const sp<WindowInfoHandle> windowHandle = mWindowInfos.findWindowHandle(windowToken);
ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable",
windowHandle != nullptr ? windowHandle->getName().c_str()
: "token without window");
@@ -6400,7 +6438,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) {
@@ -6411,7 +6450,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;
@@ -6426,19 +6465,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,
@@ -6493,17 +6519,17 @@
}
traceWaitQueueLength(*connection);
if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
- const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ const auto windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
// Only dispatch fallbacks if there is a window for the connection.
if (windowHandle != nullptr) {
- const auto inputTarget =
- createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
- dispatchEntry->targetFlags,
- fallbackKeyEntry->downTime);
- if (inputTarget.has_value()) {
- enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
- *inputTarget);
- }
+ nsecs_t downTime = fallbackKeyEntry->downTime;
+ enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
+ createInputTarget(connection, windowHandle,
+ InputTarget::DispatchMode::AS_IS,
+ dispatchEntry->targetFlags,
+ mWindowInfos.getRawTransform(
+ *windowHandle->getInfo()),
+ downTime));
}
}
releaseDispatchEntry(std::move(dispatchEntry));
@@ -6557,7 +6583,7 @@
ns2ms(currentWait),
oldestEntry.eventEntry->getDescription().c_str());
sp<IBinder> connectionToken = connection->getToken();
- updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
+ updateLastAnrStateLocked(mWindowInfos.findWindowHandle(connectionToken), reason);
processConnectionUnresponsiveLocked(*connection, std::move(reason));
@@ -6608,24 +6634,27 @@
void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
const KeyEntry& entry) {
const KeyEvent event = createKeyEvent(entry);
+ std::variant<nsecs_t, KeyEntry::InterceptKeyResult> interceptResult;
nsecs_t delay = 0;
{ // release lock
scoped_unlock unlock(mLock);
android::base::Timer t;
- delay = mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);
+ interceptResult =
+ mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
}
} // acquire lock
- if (delay < 0) {
- entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP;
- } else if (delay == 0) {
- entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
- } else {
+ if (std::holds_alternative<KeyEntry::InterceptKeyResult>(interceptResult)) {
+ entry.interceptKeyResult = std::get<KeyEntry::InterceptKeyResult>(interceptResult);
+ return;
+ }
+
+ if (std::holds_alternative<nsecs_t>(interceptResult)) {
entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER;
- entry.interceptKeyWakeupTime = now() + delay;
+ entry.interceptKeyWakeupTime = now() + std::get<nsecs_t>(interceptResult);
}
}
@@ -6660,12 +6689,12 @@
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(),
reason.c_str());
- const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);
+ const sp<WindowInfoHandle> handle = mWindowInfos.findWindowHandle(connectionToken);
if (handle != nullptr) {
pid = handle->getInfo()->ownerPid;
}
@@ -6680,10 +6709,10 @@
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 = getWindowHandleLocked(connectionToken);
+ const sp<WindowInfoHandle> handle = mWindowInfos.findWindowHandle(connectionToken);
if (handle != nullptr) {
pid = handle->getInfo()->ownerPid;
}
@@ -6725,12 +6754,11 @@
// then cancel the associated fallback key, if any.
if (fallbackKeyCode) {
// Dispatch the unhandled key to the policy with the cancel flag.
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount,
- keyEntry.policyFlags);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Asking policy to cancel fallback action. keyCode="
+ << keyEntry.keyCode << ", action=" << keyEntry.action
+ << ", repeatCount=" << keyEntry.repeatCount << ", policyFlags=0x" << std::hex
+ << keyEntry.policyFlags;
KeyEvent event = createKeyEvent(keyEntry);
event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
@@ -6748,7 +6776,7 @@
// Cancel the fallback key, but only if we still have a window for the channel.
// It could have been removed during the policy call.
if (*fallbackKeyCode != AKEYCODE_UNKNOWN) {
- const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ const auto windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
if (windowHandle != nullptr) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"application handled the original non-fallback key "
@@ -6767,21 +6795,22 @@
// Then ask the policy what to do with it.
bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
if (!fallbackKeyCode && !initialDown) {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: Skipping unhandled key event processing "
- "since this is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Skipping unhandled key event processing since this is "
+ "not an initial down. keyCode="
+ << originalKeyCode << ", action=" << keyEntry.action
+ << ", repeatCount=" << keyEntry.repeatCount << ", policyFlags=0x" << std::hex
+ << keyEntry.policyFlags;
return {};
}
// Dispatch the unhandled key to the policy.
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: Asking policy to perform fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Asking policy to perform fallback action. keyCode="
+ << keyEntry.keyCode << ", action=" << keyEntry.action
+ << ", repeatCount=" << keyEntry.repeatCount << ", policyFlags=0x" << std::hex
+ << keyEntry.policyFlags;
+ ;
KeyEvent event = createKeyEvent(keyEntry);
mLock.unlock();
@@ -6834,7 +6863,7 @@
}
}
- const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ const auto windowHandle = mWindowInfos.findWindowHandle(connection->getToken());
if (windowHandle != nullptr) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"canceling fallback, policy no longer desires it",
@@ -6878,16 +6907,13 @@
newEntry->traceTracker =
mTracer->traceDerivedEvent(*newEntry, *keyEntry.traceTracker);
}
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: Dispatching fallback key. "
- "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, *fallbackKeyCode, keyEntry.metaState);
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS)
+ << "Unhandled key event: Dispatching fallback key. originalKeyCode="
+ << originalKeyCode << ", fallbackKeyCode=" << *fallbackKeyCode
+ << ", fallbackMetaState=0x" << std::hex << keyEntry.metaState;
return newEntry;
} else {
- if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("Unhandled key event: No fallback key.");
- }
+ LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS) << "Unhandled key event: No fallback key.";
// Report the key as unhandled, since there is no fallback key.
mReporter->reportUnhandledKey(keyEntry.id);
@@ -6976,7 +7002,7 @@
std::scoped_lock _l(mLock);
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setFocusedWindow(request,
- getWindowHandlesLocked(
+ mWindowInfos.getWindowHandlesForDisplay(
ui::LogicalDisplayId{request.displayId}));
ScopedSyntheticEventTracer traceContext(mTracer);
if (changes) {
@@ -6994,7 +7020,7 @@
if (changes.oldFocus) {
const auto resolvedWindow = removedFocusedWindowHandle != nullptr
? removedFocusedWindowHandle
- : getWindowHandleLocked(changes.oldFocus, changes.displayId);
+ : mWindowInfos.findWindowHandle(changes.oldFocus, changes.displayId);
if (resolvedWindow == nullptr) {
LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
@@ -7028,7 +7054,7 @@
return;
}
- ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
+ LOG_IF(INFO, DEBUG_FOCUS) << "Disabling Pointer Capture because the window lost focus.";
if (mCurrentPointerCaptureRequest.isEnable()) {
setPointerCaptureLocked(nullptr);
@@ -7098,13 +7124,6 @@
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
- if (input_flags::split_all_touches()) {
- handlesPerDisplay[info.displayId]
- .back()
- ->editInfo()
- ->setInputConfig(android::gui::WindowInfo::InputConfig::PREVENT_SPLITTING,
- false);
- }
}
{ // acquire lock
@@ -7112,36 +7131,28 @@
// Ensure that we have an entry created for all existing displays so that if a displayId has
// no windows, we can tell that the windows were removed from the display.
- for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
- handlesPerDisplay[displayId];
- }
+ mWindowInfos.forEachDisplayId(
+ [&](ui::LogicalDisplayId displayId) { handlesPerDisplay[displayId]; });
- mDisplayInfos.clear();
- for (const auto& displayInfo : update.displayInfos) {
- mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
- }
+ mWindowInfos.setDisplayInfos(update.displayInfos);
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
}
- if (update.vsyncId < mWindowInfosVsyncId) {
- ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
- ", current update vsync id: %" PRId64,
- mWindowInfosVsyncId, update.vsyncId);
- }
mWindowInfosVsyncId = update.vsyncId;
}
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
-bool InputDispatcher::shouldDropInput(
- const EventEntry& entry, const sp<android::gui::WindowInfoHandle>& windowHandle) const {
+bool InputDispatcher::shouldDropInput(const EventEntry& entry,
+ const sp<WindowInfoHandle>& windowHandle,
+ const DispatcherWindowInfo& windowInfos) {
if (windowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::DROP_INPUT) ||
(windowHandle->getInfo()->inputConfig.test(
WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) &&
- isWindowObscuredLocked(windowHandle))) {
+ windowInfos.isWindowObscured(windowHandle))) {
ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on "
"display %s.",
ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
@@ -7166,7 +7177,7 @@
"cancel current touch", traceContext.getTracker());
synthesizeCancelationEventsForAllConnectionsLocked(options);
- mTouchStatesByDisplay.clear();
+ mTouchStates.clear();
}
// Wake up poll loop since there might be work to do.
mLooper->wake();
@@ -7177,11 +7188,10 @@
mMonitorDispatchingTimeout = timeout;
}
-void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
- const sp<WindowInfoHandle>& oldWindowHandle,
- const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, const MotionEntry& entry,
- std::vector<InputTarget>& targets) const {
+void InputDispatcher::DispatcherTouchState::slipWallpaperTouch(
+ ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle,
+ const sp<WindowInfoHandle>& newWindowHandle, TouchState& state, const MotionEntry& entry,
+ std::vector<InputTarget>& targets, std::function<void()> dump) {
LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
const DeviceId deviceId = entry.deviceId;
const PointerProperties& pointerProperties = entry.pointerProperties[0];
@@ -7194,16 +7204,17 @@
const sp<WindowInfoHandle> oldWallpaper =
oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
- newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr;
+ newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
return;
}
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
- addPointerWindowTargetLocked(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
- oldTouchedWindow.targetFlags, getPointerIds(pointers),
- oldTouchedWindow.getDownTimeInTarget(deviceId), targets);
+ addPointerWindowTarget(oldWallpaper, InputTarget::DispatchMode::SLIPPERY_EXIT,
+ oldTouchedWindow.targetFlags, getPointerIds(pointers),
+ oldTouchedWindow.getDownTimeInTarget(deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
}
@@ -7211,16 +7222,19 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointers, entry.eventTime);
+ deviceId, pointers, entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
}
}
-void InputDispatcher::transferWallpaperTouch(
+std::pair<std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>,
+ std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>
+InputDispatcher::DispatcherTouchState::transferWallpaperTouch(
+ const sp<gui::WindowInfoHandle> fromWindowHandle,
+ const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
+ android::DeviceId deviceId, const std::vector<PointerProperties>& pointers,
ftl::Flags<InputTarget::Flags> oldTargetFlags,
- ftl::Flags<InputTarget::Flags> newTargetFlags, const sp<WindowInfoHandle> fromWindowHandle,
- const sp<WindowInfoHandle> toWindowHandle, TouchState& state, DeviceId deviceId,
- const std::vector<PointerProperties>& pointers,
- const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) {
+ ftl::Flags<InputTarget::Flags> newTargetFlags) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7231,16 +7245,16 @@
const sp<WindowInfoHandle> oldWallpaper =
oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
- newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr;
+ newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
- return;
+ return {};
}
+ std::list<CancellationArgs> cancellations;
+ std::list<PointerDownArgs> pointerDowns;
if (oldWallpaper != nullptr) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "transferring touch focus to another window", traceTracker);
state.removeWindowByToken(oldWallpaper->getToken());
- synthesizeCancelationEventsForWindowLocked(oldWallpaper, options);
+ cancellations.emplace_back(oldWallpaper, CancelationOptions::Mode::CANCEL_POINTER_EVENTS);
}
if (newWallpaper != nullptr) {
@@ -7250,23 +7264,25 @@
wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
- deviceId, pointers, downTimeInTarget);
+ deviceId, pointers, downTimeInTarget,
+ /*forwardingWindowToken=*/nullptr);
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);
+ pointerDowns.emplace_back(downTimeInTarget, wallpaperConnection, wallpaperFlags);
}
+ pointerDowns.emplace_back(downTimeInTarget, wallpaperConnection, wallpaperFlags);
}
+ return {cancellations, pointerDowns};
}
-sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow(
+sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWallpaperWindowBelow(
const sp<WindowInfoHandle>& windowHandle) const {
const std::vector<sp<WindowInfoHandle>>& windowHandles =
- getWindowHandlesLocked(windowHandle->getInfo()->displayId);
+ getWindowHandlesForDisplay(windowHandle->getInfo()->displayId);
bool foundWindow = false;
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (!foundWindow && otherHandle != windowHandle) {
@@ -7298,18 +7314,7 @@
ui::LogicalDisplayId displayId, DeviceId deviceId,
int32_t pointerId) {
std::scoped_lock _l(mLock);
- auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- return false;
- }
- for (const TouchedWindow& window : touchStateIt->second.windows) {
- if (window.windowHandle->getToken() == token &&
- (window.hasTouchingPointer(deviceId, pointerId) ||
- window.hasHoveringPointer(deviceId, pointerId))) {
- return true;
- }
- }
- return false;
+ return mTouchStates.isPointerInWindow(token, displayId, deviceId, pointerId);
}
void InputDispatcher::setInputMethodConnectionIsActive(bool isActive) {
@@ -7319,4 +7324,392 @@
}
}
+void InputDispatcher::setDisplayTopology(
+ const android::DisplayTopologyGraph& displayTopologyGraph) {
+ std::scoped_lock _l(mLock);
+ mWindowInfos.setDisplayTopology(displayTopologyGraph);
+}
+
+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;
+}
+
+void InputDispatcher::DispatcherWindowInfo::setMaximumObscuringOpacityForTouch(float opacity) {
+ if (opacity < 0 || opacity > 1) {
+ LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+ }
+ mMaximumObscuringOpacityForTouch = opacity;
+}
+
+void InputDispatcher::DispatcherWindowInfo::setDisplayTopology(
+ const DisplayTopologyGraph& displayTopologyGraph) {
+ mTopology = displayTopologyGraph;
+}
+
+InputDispatcher::DispatcherTouchState::DispatcherTouchState(const DispatcherWindowInfo& windowInfos,
+ const ConnectionManager& connections)
+ : mWindowInfos(windowInfos), mConnectionManager(connections) {}
+
+ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetFlags(
+ const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit) {
+ ftl::Flags<InputTarget::Flags> targetFlags;
+ if (canReceiveForegroundTouches(*targetWindow->getInfo())) {
+ // There should only be one touched window that can be "foreground" for the pointer.
+ targetFlags |= InputTarget::Flags::FOREGROUND;
+ }
+ if (isSplit) {
+ targetFlags |= InputTarget::Flags::SPLIT;
+ }
+ if (mWindowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
+ targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
+ } else if (mWindowInfos.isWindowObscured(targetWindow)) {
+ targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
+ }
+ return targetFlags;
+}
+
+bool InputDispatcher::DispatcherTouchState::hasTouchingOrHoveringPointers(
+ ui::LogicalDisplayId displayId, int32_t deviceId) const {
+ bool hasTouchingOrHoveringPointers = false;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ hasTouchingOrHoveringPointers =
+ state.hasTouchingPointers(deviceId) || state.hasHoveringPointers(deviceId);
+ return hasTouchingOrHoveringPointers;
+ });
+ return hasTouchingOrHoveringPointers;
+}
+
+bool InputDispatcher::DispatcherTouchState::isPointerInWindow(const sp<android::IBinder>& token,
+ ui::LogicalDisplayId displayId,
+ android::DeviceId deviceId,
+ int32_t pointerId) const {
+ bool isPointerInWindow = false;
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ for (const TouchedWindow& window : state.windows) {
+ if (window.windowHandle->getToken() == token &&
+ (window.hasTouchingPointer(deviceId, pointerId) ||
+ window.hasHoveringPointer(deviceId, pointerId))) {
+ isPointerInWindow = true;
+ return true;
+ }
+ }
+ return false;
+ });
+ return isPointerInWindow;
+}
+
+std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>
+InputDispatcher::DispatcherTouchState::findExistingTouchedWindowHandleAndDisplay(
+ const sp<android::IBinder>& token) const {
+ std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
+ touchedWindowHandleAndDisplay;
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) {
+ for (const TouchedWindow& w : state.windows) {
+ if (w.windowHandle->getToken() == token) {
+ touchedWindowHandleAndDisplay.emplace(std::ref(w.windowHandle), displayId);
+ return true;
+ }
+ }
+ return false;
+ });
+ LOG_ALWAYS_FATAL_IF(!touchedWindowHandleAndDisplay.has_value(),
+ "%s : Touch state is out of sync: No touched window for token", __func__);
+ return touchedWindowHandleAndDisplay.value();
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchedWindows(
+ std::function<void(const sp<gui::WindowInfoHandle>&)> f) const {
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, const TouchState& state) {
+ for (const TouchedWindow& window : state.windows) {
+ f(window.windowHandle);
+ }
+ return false;
+ });
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchedWindowsOnDisplay(
+ ui::LogicalDisplayId displayId,
+ std::function<void(const sp<gui::WindowInfoHandle>&)> f) const {
+ forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ for (const TouchedWindow& window : state.windows) {
+ f(window.windowHandle);
+ }
+ return false;
+ });
+}
+
+std::string InputDispatcher::DispatcherTouchState::dump() const {
+ std::string dump;
+ if (mTouchStatesByDisplay.empty()) {
+ dump += "TouchStatesByDisplay: <no displays touched>\n";
+ } else {
+ dump += "TouchStatesByDisplay:\n";
+ for (const auto& [displayId, state] : mTouchStatesByDisplay) {
+ std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
+ dump += INDENT + displayId.toString() + " : " + touchStateDump;
+ }
+ }
+ if (mCursorStateByDisplay.empty()) {
+ dump += "CursorStatesByDisplay: <no displays touched by cursor>\n";
+ } else {
+ dump += "CursorStatesByDisplay:\n";
+ for (const auto& [displayId, state] : mCursorStateByDisplay) {
+ std::string touchStateDump = addLinePrefix(state.dump(), INDENT);
+ dump += INDENT + displayId.toString() + " : " + touchStateDump;
+ }
+ }
+ return dump;
+}
+
+void InputDispatcher::DispatcherTouchState::removeAllPointersForDevice(android::DeviceId deviceId) {
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) {
+ state.removeAllPointersForDevice(deviceId);
+ return false;
+ });
+}
+
+void InputDispatcher::DispatcherTouchState::clear() {
+ mTouchStatesByDisplay.clear();
+ mCursorStateByDisplay.clear();
+}
+
+void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry,
+ android::inputdispatcher::TouchState&& touchState) {
+ if (touchState.windows.empty()) {
+ eraseTouchStateForMotionEntry(entry);
+ return;
+ }
+
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ mCursorStateByDisplay[mWindowInfos.getPrimaryDisplayId(entry.displayId)] =
+ std::move(touchState);
+ } else {
+ mTouchStatesByDisplay[entry.displayId] = std::move(touchState);
+ }
+}
+
+void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry) {
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ mCursorStateByDisplay.erase(mWindowInfos.getPrimaryDisplayId(entry.displayId));
+ } else {
+ mTouchStatesByDisplay.erase(entry.displayId);
+ }
+}
+
+const TouchState* InputDispatcher::DispatcherTouchState::getTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry) const {
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ auto touchStateIt =
+ mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(entry.displayId));
+ if (touchStateIt != mCursorStateByDisplay.end()) {
+ return &touchStateIt->second;
+ }
+ } else {
+ auto touchStateIt = mTouchStatesByDisplay.find(entry.displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end()) {
+ return &touchStateIt->second;
+ }
+ }
+ return nullptr;
+}
+
+void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay(
+ ui::LogicalDisplayId displayId, std::function<bool(const TouchState&)> f) const {
+ const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
+ if (touchStateIt != mTouchStatesByDisplay.end() && f(touchStateIt->second)) {
+ return;
+ }
+
+ // DisplayId for the Cursor state may not be same as supplied displayId if display is part of
+ // topology. Instead we should to check from the topology's primary display.
+ const auto cursorStateIt =
+ mCursorStateByDisplay.find(mWindowInfos.getPrimaryDisplayId(displayId));
+ if (cursorStateIt != mCursorStateByDisplay.end()) {
+ f(cursorStateIt->second);
+ }
+}
+
+void InputDispatcher::DispatcherTouchState::forTouchAndCursorStatesOnDisplay(
+ ui::LogicalDisplayId displayId, std::function<bool(TouchState&)> f) {
+ const_cast<const DispatcherTouchState&>(*this)
+ .forTouchAndCursorStatesOnDisplay(displayId, [&](const TouchState& state) {
+ return f(const_cast<TouchState&>(state));
+ });
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates(
+ std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const {
+ for (auto& [displayId, state] : mTouchStatesByDisplay) {
+ if (f(displayId, state)) {
+ return;
+ }
+ }
+ for (auto& [displayId, state] : mCursorStateByDisplay) {
+ if (f(displayId, state)) {
+ return;
+ }
+ }
+}
+
+void InputDispatcher::DispatcherTouchState::forAllTouchAndCursorStates(
+ std::function<bool(ui::LogicalDisplayId, TouchState&)> f) {
+ const_cast<const DispatcherTouchState&>(*this).forAllTouchAndCursorStates(
+ [&](ui::LogicalDisplayId displayId, const TouchState& constState) {
+ return f(displayId, const_cast<TouchState&>(constState));
+ });
+}
+
+std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
+InputDispatcher::DispatcherTouchState::findTouchStateWindowAndDisplay(
+ const sp<android::IBinder>& token) {
+ std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
+ touchStateWindowAndDisplay;
+ forAllTouchAndCursorStates([&](ui::LogicalDisplayId displayId, TouchState& state) {
+ for (TouchedWindow& w : state.windows) {
+ if (w.windowHandle->getToken() == token) {
+ touchStateWindowAndDisplay.emplace(std::ref(state), std::ref(w), displayId);
+ return true;
+ }
+ }
+ return false;
+ });
+ return touchStateWindowAndDisplay;
+}
+
+bool InputDispatcher::DispatcherTouchState::isStylusActiveInDisplay(
+ ui::LogicalDisplayId displayId) const {
+ const auto it = mTouchStatesByDisplay.find(displayId);
+ if (it == mTouchStatesByDisplay.end()) {
+ return false;
+ }
+ const TouchState& state = it->second;
+ return state.hasActiveStylus();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index fade853..2e8f2ce 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -127,7 +127,7 @@
void setMaximumObscuringOpacityForTouch(float opacity) override;
bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop = false) override;
+ bool isDragDrop, bool transferEntireGesture) override;
bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken,
ui::LogicalDisplayId displayId) override;
@@ -164,6 +164,8 @@
void setInputMethodConnectionIsActive(bool isActive) override;
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) override;
+
private:
enum class DropReason {
NOT_DROPPED,
@@ -224,6 +226,317 @@
/** Stores the latest user-activity poke event times per user activity types. */
std::array<nsecs_t, USER_ACTIVITY_EVENT_LAST + 1> mLastUserActivityTimes GUARDED_BY(mLock);
+ template <typename T>
+ struct StrongPointerHash {
+ std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
+ };
+
+ 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);
+
+ class DispatcherWindowInfo {
+ public:
+ struct TouchOcclusionInfo {
+ bool hasBlockingOcclusion;
+ float obscuringOpacity;
+ std::string obscuringPackage;
+ gui::Uid obscuringUid = gui::Uid::INVALID;
+ std::vector<std::string> debugInfo;
+ };
+
+ void setWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId,
+ std::vector<sp<android::gui::WindowInfoHandle>>&& windowHandles);
+
+ void setDisplayInfos(const std::vector<android::gui::DisplayInfo>& displayInfos);
+
+ void removeDisplay(ui::LogicalDisplayId displayId);
+
+ void setMaximumObscuringOpacityForTouch(float opacity);
+
+ void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
+
+ // Get a reference to window handles by display, return an empty vector if not found.
+ const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay(
+ ui::LogicalDisplayId displayId) const;
+
+ void forEachWindowHandle(
+ std::function<void(const sp<android::gui::WindowInfoHandle>&)> f) const;
+
+ void forEachDisplayId(std::function<void(ui::LogicalDisplayId)> f) const;
+
+ // Get the transform for display, returns Identity-transform if display is missing.
+ ui::Transform getDisplayTransform(ui::LogicalDisplayId displayId) const;
+
+ // Get the raw transform to use for motion events going to the given window. Optionally a
+ // pointer displayId may be supplied if pointer is on a different display from the window.
+ ui::Transform getRawTransform(
+ const android::gui::WindowInfo& windowInfo,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId = std::nullopt) const;
+
+ // Lookup for WindowInfoHandle from token and optionally a display-id. In cases where
+ // display-id is not provided lookup is done for all displays.
+ sp<android::gui::WindowInfoHandle> findWindowHandle(
+ const sp<IBinder>& windowHandleToken,
+ std::optional<ui::LogicalDisplayId> displayId = {}) const;
+
+ // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected
+ // displays in the topology of the queried display.
+ sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const;
+
+ bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+ // Returns the touched window at the given location, excluding the ignoreWindow if provided.
+ sp<android::gui::WindowInfoHandle> findTouchedWindowAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
+ const sp<android::gui::WindowInfoHandle> ignoreWindow = nullptr) const;
+
+ TouchOcclusionInfo computeTouchOcclusionInfo(
+ const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const;
+
+ bool isWindowObscured(const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+ bool isWindowObscuredAtPoint(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ float x, float y) const;
+
+ sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
+ const sp<android::gui::WindowInfoHandle>& windowHandle) const;
+
+ bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const;
+
+ // Returns topology's primary display if the display belongs to it, otherwise the
+ // same displayId.
+ ui::LogicalDisplayId getPrimaryDisplayId(ui::LogicalDisplayId displayId) const;
+
+ bool areDisplaysConnected(ui::LogicalDisplayId display1,
+ ui::LogicalDisplayId display2) const;
+
+ std::string dumpDisplayAndWindowInfo() const;
+
+ private:
+ std::vector<ui::LogicalDisplayId> getConnectedDisplays(
+ ui::LogicalDisplayId displayId) const;
+
+ sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay(
+ const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const;
+
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/,
+ std::vector<sp<android::gui::WindowInfoHandle>>>
+ mWindowHandlesByDisplay;
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo>
+ mDisplayInfos;
+ float mMaximumObscuringOpacityForTouch{1.0f};
+
+ // Topology is initialized with default-constructed value, which is an empty topology until
+ // we receive setDisplayTopology call. Meanwhile we will treat every display as an
+ // independent display.
+ DisplayTopologyGraph mTopology;
+ };
+
+ DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
+
+ class DispatcherTouchState {
+ public:
+ struct CancellationArgs {
+ const sp<gui::WindowInfoHandle> windowHandle;
+ CancelationOptions::Mode mode;
+ std::optional<DeviceId> deviceId{std::nullopt};
+ ui::LogicalDisplayId displayId{ui::LogicalDisplayId::INVALID};
+ std::bitset<MAX_POINTER_ID + 1> pointerIds{};
+ };
+
+ struct PointerDownArgs {
+ const nsecs_t downTimeInTarget;
+ const std::shared_ptr<Connection> connection;
+ const ftl::Flags<InputTarget::Flags> targetFlags;
+ };
+
+ DispatcherTouchState(const DispatcherWindowInfo& windowInfos,
+ const ConnectionManager& connections);
+
+ void addPointerWindowTarget(const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode,
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ std::optional<nsecs_t> firstDownTimeInTarget,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId,
+ std::function<void()> dump,
+ std::vector<InputTarget>& inputTargets);
+
+ base::Result<std::vector<InputTarget>, android::os::InputEventInjectionResult>
+ findTouchedWindowTargets(nsecs_t currentTime, const MotionEntry& entry,
+ const sp<android::gui::WindowInfoHandle> dragWindow,
+ std::function<void(const MotionEntry&)> addDragEvent,
+ std::function<void()> dump);
+
+ sp<android::gui::WindowInfoHandle> findTouchedForegroundWindow(
+ ui::LogicalDisplayId displayId) const;
+
+ bool hasTouchingOrHoveringPointers(ui::LogicalDisplayId displayId, int32_t deviceId) const;
+
+ bool isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId,
+ DeviceId deviceId, int32_t pointerId) const;
+
+ // Find an existing touched windowHandle and display by token.
+ std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>
+ findExistingTouchedWindowHandleAndDisplay(const sp<IBinder>& token) const;
+
+ void forAllTouchedWindows(std::function<void(const sp<gui::WindowInfoHandle>&)> f) const;
+
+ void forAllTouchedWindowsOnDisplay(
+ ui::LogicalDisplayId displayId,
+ std::function<void(const sp<gui::WindowInfoHandle>&)> f) const;
+
+ std::string dump() const;
+
+ // Updates the touchState for display from WindowInfo,
+ // returns list of CancellationArgs for every cancelled touch
+ std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId);
+
+ void removeAllPointersForDevice(DeviceId deviceId);
+
+ // transfer touch between provided tokens, returns destination WindowHandle, deviceId,
+ // pointers, list of cancelled windows and pointers on successful transfer.
+ std::optional<
+ std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>,
+ std::list<CancellationArgs>, std::list<PointerDownArgs>>>
+ transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool transferEntireGesture);
+
+ base::Result<std::list<CancellationArgs>, status_t> pilferPointers(
+ const sp<IBinder>& token, const Connection& requestingConnection);
+
+ void clear();
+
+ private:
+ std::unordered_map<ui::LogicalDisplayId, TouchState> mTouchStatesByDisplay;
+
+ // As there can be only one CursorState per topology group, we will treat all displays in
+ // the topology as one connected display-group. These will be identified by
+ // DisplayTopologyGraph::primaryDisplayId.
+ // Cursor on the any of the displays that are not part of the topology will be identified by
+ // the displayId similar to mTouchStatesByDisplay.
+ std::unordered_map<ui::LogicalDisplayId, TouchState> mCursorStateByDisplay;
+
+ // The supplied lambda is invoked for each touch and cursor state of the display.
+ // The function iterates until the lambda returns true, effectively performing a 'break'
+ // from the iteration.
+ void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId,
+ std::function<bool(const TouchState&)> f) const;
+
+ void forTouchAndCursorStatesOnDisplay(ui::LogicalDisplayId displayId,
+ std::function<bool(TouchState&)> f);
+
+ // The supplied lambda is invoked for each touchState. The function iterates until
+ // the lambda returns true, effectively performing a 'break' from the iteration.
+ void forAllTouchAndCursorStates(
+ std::function<bool(ui::LogicalDisplayId, const TouchState&)> f) const;
+
+ void forAllTouchAndCursorStates(std::function<bool(ui::LogicalDisplayId, TouchState&)> f);
+
+ std::optional<std::tuple<TouchState&, TouchedWindow&, ui::LogicalDisplayId>>
+ findTouchStateWindowAndDisplay(const sp<IBinder>& token);
+
+ std::pair<std::list<CancellationArgs>, std::list<PointerDownArgs>> transferWallpaperTouch(
+ const sp<gui::WindowInfoHandle> fromWindowHandle,
+ const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
+ DeviceId deviceId, const std::vector<PointerProperties>& pointers,
+ ftl::Flags<InputTarget::Flags> oldTargetFlags,
+ ftl::Flags<InputTarget::Flags> newTargetFlags);
+
+ void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState);
+
+ void eraseTouchStateForMotionEntry(const MotionEntry& entry);
+
+ const TouchState* getTouchStateForMotionEntry(
+ const android::inputdispatcher::MotionEntry& entry) const;
+
+ bool canWindowReceiveMotion(const sp<gui::WindowInfoHandle>& window,
+ const MotionEntry& motionEntry) const;
+
+ // Return true if stylus is currently down anywhere on the specified display,
+ // and false otherwise.
+ bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId) const;
+
+ std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId);
+
+ std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId);
+
+ std::vector<InputTarget> findOutsideTargets(ui::LogicalDisplayId displayId,
+ const sp<gui::WindowInfoHandle>& touchedWindow,
+ int32_t pointerId, std::function<void()> dump);
+
+ /**
+ * Slip the wallpaper touch if necessary.
+ *
+ * @param targetFlags the target flags
+ * @param oldWindowHandle the old window that the touch slipped out of
+ * @param newWindowHandle the new window that the touch is slipping into
+ * @param state the current touch state. This will be updated if necessary to reflect the
+ * new windows that are receiving touch.
+ * @param deviceId the device id of the current motion being processed
+ * @param pointerProperties the pointer properties of the current motion being processed
+ * @param targets the current targets to add the walpaper ones to
+ * @param eventTime the new downTime for the wallpaper target
+ */
+ void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
+ const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
+ const sp<android::gui::WindowInfoHandle>& newWindowHandle,
+ TouchState& state, const MotionEntry& entry,
+ std::vector<InputTarget>& targets, std::function<void()> dump);
+
+ ftl::Flags<InputTarget::Flags> getTargetFlags(
+ const sp<android::gui::WindowInfoHandle>& targetWindow, vec2 targetPosition,
+ bool isSplit);
+
+ const DispatcherWindowInfo& mWindowInfos;
+ const ConnectionManager& mConnectionManager;
+ };
+
+ DispatcherTouchState mTouchStates GUARDED_BY(mLock);
+
// With each iteration, InputDispatcher nominally processes one queued event,
// a timeout, or a response from an input consumer.
// This method should only be called on the input dispatcher's own thread.
@@ -252,45 +565,8 @@
// to transfer focus to a new application.
std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
- sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
- bool ignoreDragWindow = false) const REQUIRES(mLock);
- std::vector<InputTarget> findOutsideTargetsLocked(
- ui::LogicalDisplayId displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
- int32_t pointerId) const REQUIRES(mLock);
-
- std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus,
- DeviceId deviceId) const REQUIRES(mLock);
-
- sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(
- ui::LogicalDisplayId displayId) const REQUIRES(mLock);
-
- 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>
- struct StrongPointerHash {
- 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;
@@ -350,7 +626,6 @@
bool mDispatchEnabled GUARDED_BY(mLock);
bool mDispatchFrozen GUARDED_BY(mLock);
bool mInputFilterEnabled GUARDED_BY(mLock);
- float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
// This map is not really needed, but it helps a lot with debugging (dumpsys input).
// In the java layer, touch mode states are spread across multiple DisplayContent objects,
@@ -368,28 +643,12 @@
};
sp<gui::WindowInfosListener> mWindowInfoListener;
- std::unordered_map<ui::LogicalDisplayId /*displayId*/,
- std::vector<sp<android::gui::WindowInfoHandle>>>
- mWindowHandlesByDisplay GUARDED_BY(mLock);
- std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
- GUARDED_BY(mLock);
void setInputWindowsLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
ui::LogicalDisplayId displayId) REQUIRES(mLock);
- // Get a reference to window handles by display, return an empty vector if not found.
- const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked(
- ui::LogicalDisplayId displayId) const REQUIRES(mLock);
- ui::Transform getTransformLocked(ui::LogicalDisplayId displayId) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken,
- std::optional<ui::LogicalDisplayId> displayId = {}) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(
ui::LogicalDisplayId displayId) const REQUIRES(mLock);
- bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window,
- const MotionEntry& motionEntry) const REQUIRES(mLock);
// Returns all the input targets (with their respective input channels) from the window handles
// passed as argument.
@@ -404,8 +663,6 @@
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
ui::LogicalDisplayId displayId) REQUIRES(mLock);
- std::unordered_map<ui::LogicalDisplayId /*displayId*/, TouchState> mTouchStatesByDisplay
- GUARDED_BY(mLock);
std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
void setFocusedApplicationLocked(
@@ -545,26 +802,12 @@
base::Result<sp<android::gui::WindowInfoHandle>, android::os::InputEventInjectionResult>
findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry,
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);
- std::optional<InputTarget> createInputTargetLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
- std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
- void addPointerWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode,
- ftl::Flags<InputTarget::Flags> targetFlags,
- std::bitset<MAX_POINTER_ID + 1> pointerIds,
- std::optional<nsecs_t> firstDownTimeInTarget,
- std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
ui::LogicalDisplayId displayId) REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
@@ -573,30 +816,16 @@
void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
void finishDragAndDrop(ui::LogicalDisplayId displayId, float x, float y) REQUIRES(mLock);
- struct TouchOcclusionInfo {
- bool hasBlockingOcclusion;
- float obscuringOpacity;
- std::string obscuringPackage;
- gui::Uid obscuringUid = gui::Uid::INVALID;
- std::vector<std::string> debugInfo;
- };
-
- TouchOcclusionInfo computeTouchOcclusionInfoLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const
- REQUIRES(mLock);
- bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
- bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- float x, float y) const REQUIRES(mLock);
- bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const
- REQUIRES(mLock);
- std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info,
- bool isTouchWindow) const;
std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
const sp<android::gui::WindowInfoHandle>& windowHandle);
- bool shouldDropInput(const EventEntry& entry,
- const sp<android::gui::WindowInfoHandle>& windowHandle) const
- REQUIRES(mLock);
+ static std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAt(
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId,
+ const DispatcherWindowInfo& windowInfos);
+
+ static bool shouldDropInput(const EventEntry& entry,
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ const DispatcherWindowInfo& windowInfo);
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
@@ -618,8 +847,7 @@
void finishDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection, uint32_t seq,
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
- void abortBrokenDispatchCycleLocked(nsecs_t currentTime,
- const std::shared_ptr<Connection>& connection, bool notify)
+ void abortBrokenDispatchCycleLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
void drainDispatchQueue(std::deque<std::unique_ptr<DispatchEntry>>& queue);
void releaseDispatchEntry(std::unique_ptr<DispatchEntry> dispatchEntry);
@@ -659,13 +887,10 @@
// 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 sp<IBinder>& connectionToken, bool notify)
+ status_t removeInputChannelLocked(const std::shared_ptr<Connection>& connection, bool notify)
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
@@ -694,19 +919,14 @@
std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
bool handled) REQUIRES(mLock);
-
- // Find touched state and touched window by token.
- std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
- findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
- std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
- findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const REQUIRES(mLock);
- bool windowHasTouchingPointersLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- DeviceId deviceId) const REQUIRES(mLock);
+ void findAndDispatchFallbackEvent(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
+ std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
// Statistics gathering.
nsecs_t mLastStatisticPushTime = 0;
std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock);
+ // Must outlive `mLatencyTracker`.
+ std::vector<InputDeviceInfo> mInputDevices;
LatencyTracker mLatencyTracker GUARDED_BY(mLock);
void traceInboundQueueLengthLocked() REQUIRES(mLock);
void traceOutboundQueueLength(const Connection& connection);
@@ -718,36 +938,6 @@
sp<InputReporterInterface> mReporter;
- /**
- * Slip the wallpaper touch if necessary.
- *
- * @param targetFlags the target flags
- * @param oldWindowHandle the old window that the touch slipped out of
- * @param newWindowHandle the new window that the touch is slipping into
- * @param state the current touch state. This will be updated if necessary to reflect the new
- * windows that are receiving touch.
- * @param deviceId the device id of the current motion being processed
- * @param pointerProperties the pointer properties of the current motion being processed
- * @param targets the current targets to add the walpaper ones to
- * @param eventTime the new downTime for the wallpaper target
- */
- void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
- const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
- const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, const MotionEntry& entry,
- std::vector<InputTarget>& targets) const REQUIRES(mLock);
- void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
- ftl::Flags<InputTarget::Flags> newTargetFlags,
- const sp<android::gui::WindowInfoHandle> fromWindowHandle,
- const sp<android::gui::WindowInfoHandle> toWindowHandle,
- TouchState& state, DeviceId deviceId,
- const std::vector<PointerProperties>& pointers,
- const std::unique_ptr<trace::EventTrackerInterface>& traceTracker)
- REQUIRES(mLock);
-
- sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
- const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
-
/** Stores the value of the input flag for per device input latency metrics. */
const bool mPerDeviceInputLatencyMetricsFlag =
com::android::input::flags::enable_per_device_input_latency_metrics();
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 9b5a79b..782a54f 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -15,7 +15,9 @@
*/
#include "DebugConfig.h"
+#include "input/Input.h"
#include "input/InputDevice.h"
+#include "input/InputFlags.h"
#include "InputState.h"
@@ -221,10 +223,15 @@
}
ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
+ // If we have connected displays a mouse can move between displays and displayId may change
+ // while a gesture is in-progress.
+ const bool skipDisplayCheck =
+ InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source);
for (size_t i = 0; i < mMotionMementos.size(); i++) {
const MotionMemento& memento = mMotionMementos[i];
if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
- memento.displayId == entry.displayId && memento.hovering == hovering) {
+ memento.hovering == hovering &&
+ (skipDisplayCheck || memento.displayId == entry.displayId)) {
return i;
}
}
@@ -338,7 +345,10 @@
// would receive different events from each display. Since the TouchStates are per-display,
// it's unlikely that those two streams would be consistent with each other. Therefore,
// cancel the previous gesture if the display id changes.
- if (motionEntry.displayId != lastMemento.displayId) {
+ // Except when we have connected-displays where a mouse may move across display boundaries.
+ const bool skipDisplayCheck = (InputFlags::connectedDisplaysCursorEnabled() &&
+ isMouseOrTouchpad(motionEntry.source));
+ if (!skipDisplayCheck && motionEntry.displayId != lastMemento.displayId) {
LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId
<< " and new event is " << motionEntry;
return true;
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 90374f1..76f3fe0 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -77,8 +77,8 @@
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- // Current display transform. Used for compatibility for raw coordinates.
- ui::Transform displayTransform;
+ // The raw coordinate transform that's used for compatibility for MotionEvent's getRaw APIs.
+ ui::Transform rawTransform;
// Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
// FLAG_SPLIT is set.
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/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 0921e37..7c23694 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -67,8 +67,9 @@
} // namespace
-LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor)
- : mTimelineProcessor(&processor) {}
+LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor,
+ std::vector<InputDeviceInfo>& inputDevices)
+ : mTimelineProcessor(&processor), mInputDevices(inputDevices) {}
void LatencyTracker::trackListener(const NotifyArgs& args) {
if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) {
@@ -248,8 +249,4 @@
StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
}
-void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) {
- mInputDevices = inputDevices;
-}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 79ea14c..7e2cc79 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -20,9 +20,11 @@
#include <map>
#include <unordered_map>
+#include <vector>
#include <binder/IBinder.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include "InputEventTimeline.h"
#include "NotifyArgs.h"
@@ -41,8 +43,10 @@
/**
* Create a LatencyTracker.
* param reportingFunction: the function that will be called in order to report full latency.
+ * param inputDevices: input devices relevant for tracking.
*/
- LatencyTracker(InputEventTimelineProcessor& processor);
+ LatencyTracker(InputEventTimelineProcessor& processor,
+ std::vector<InputDeviceInfo>& inputDevices);
/**
* Start keeping track of an event identified by the args. This must be called first.
* If duplicate events are encountered (events that have the same eventId), none of them will be
@@ -60,7 +64,6 @@
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
std::string dump(const char* prefix) const;
- void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
private:
/**
@@ -81,7 +84,7 @@
std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes;
InputEventTimelineProcessor* mTimelineProcessor;
- std::vector<InputDeviceInfo> mInputDevices;
+ std::vector<InputDeviceInfo>& mInputDevices;
void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 2bf63be..f1fca0c 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -74,7 +74,7 @@
const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode,
ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget) {
+ std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken) {
if (touchingPointers.empty()) {
LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
return android::base::Error();
@@ -88,6 +88,7 @@
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags |= targetFlags;
+ touchedWindow.forwardingWindowToken = forwardingWindowToken;
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
@@ -103,6 +104,7 @@
touchedWindow.windowHandle = windowHandle;
touchedWindow.dispatchMode = dispatchMode;
touchedWindow.targetFlags = targetFlags;
+ touchedWindow.forwardingWindowToken = forwardingWindowToken;
touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 451d917..20155f4 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,7 @@
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget);
+ std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer, float x,
float y);
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index fa5be1a..053a2e2 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -331,9 +331,9 @@
std::string out;
std::string deviceStates =
dumpMap(mDeviceStates, constToString, TouchedWindow::deviceStateToString);
- out += StringPrintf("name='%s', targetFlags=%s, mDeviceStates=%s\n",
+ out += StringPrintf("name='%s', targetFlags=%s, forwardingWindowToken=%p, mDeviceStates=%s\n",
windowHandle->getName().c_str(), targetFlags.string().c_str(),
- deviceStates.c_str());
+ forwardingWindowToken.get(), deviceStates.c_str());
return out;
}
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index c38681e..d27c597 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -34,6 +34,11 @@
InputTarget::DispatchMode dispatchMode = InputTarget::DispatchMode::AS_IS;
ftl::Flags<InputTarget::Flags> targetFlags;
+ // If another window has transferred touches to this window, and wants to continue sending the
+ // rest of the gesture to this window, store the token of the originating (transferred-from)
+ // window here.
+ sp<IBinder> forwardingWindowToken;
+
// Hovering
bool hasHoveringPointers() const;
bool hasHoveringPointers(DeviceId deviceId) const;
@@ -76,7 +81,7 @@
};
std::vector<DeviceId> eraseHoveringPointersIf(
- std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition);
+ std::function<bool(const PointerProperties&, float x, float y)> condition);
private:
struct DeviceState {
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 463a952..b22ddca 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -24,6 +24,7 @@
#include <android/os/InputEventInjectionSync.h>
#include <gui/InputApplication.h>
#include <gui/WindowInfo.h>
+#include <input/DisplayTopologyGraph.h>
#include <input/InputDevice.h>
#include <input/InputTransport.h>
#include <unordered_map>
@@ -147,7 +148,7 @@
* Returns true on success. False if the window did not actually have an active touch gesture.
*/
virtual bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
- bool isDragDrop) = 0;
+ bool isDragDrop, bool transferEntireGesture) = 0;
/**
* Transfer a touch gesture to the provided channel, no matter where the current touch is.
@@ -243,6 +244,11 @@
* Notify the dispatcher that the state of the input method connection changed.
*/
virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
+
+ /*
+ * Notify the dispatcher of the latest DisplayTopology.
+ */
+ virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index b885ba1..5dcd984 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -20,12 +20,14 @@
#include <android-base/properties.h>
#include <binder/IBinder.h>
+#include <dispatcher/Entry.h>
#include <gui/InputApplication.h>
#include <gui/PidUid.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <utils/RefBase.h>
#include <set>
+#include <variant>
namespace android {
@@ -106,9 +108,9 @@
uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
- const KeyEvent& keyEvent,
- uint32_t policyFlags) = 0;
+ virtual std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
+ uint32_t policyFlags) = 0;
/* Allows the policy a chance to perform default processing for an unhandled key.
* Returns an alternate key event to redispatch as a fallback, if needed. */
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
deleted file mode 100644
index 0b17507..0000000
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * 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.
- */
-
-#include "AndroidInputEventProtoConverter.h"
-
-#include <android-base/logging.h>
-#include <perfetto/trace/android/android_input_event.pbzero.h>
-
-namespace android::inputdispatcher::trace {
-
-namespace {
-
-using namespace ftl::flag_operators;
-
-// The trace config to use for maximal tracing.
-const impl::TraceConfig CONFIG_TRACE_ALL{
- .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS |
- impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH,
- .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE,
- .matchAllPackages = {},
- .matchAnyPackages = {},
- .matchSecure{},
- .matchImeConnectionActive = {}}},
-};
-
-} // namespace
-
-void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event,
- proto::AndroidMotionEvent& outProto,
- bool isRedacted) {
- outProto.set_event_id(event.id);
- outProto.set_event_time_nanos(event.eventTime);
- outProto.set_down_time_nanos(event.downTime);
- outProto.set_source(event.source);
- outProto.set_action(event.action);
- outProto.set_device_id(event.deviceId);
- outProto.set_display_id(event.displayId.val());
- outProto.set_classification(static_cast<int32_t>(event.classification));
- outProto.set_flags(event.flags);
- outProto.set_policy_flags(event.policyFlags);
-
- if (!isRedacted) {
- outProto.set_cursor_position_x(event.xCursorPosition);
- outProto.set_cursor_position_y(event.yCursorPosition);
- outProto.set_meta_state(event.metaState);
- }
-
- for (uint32_t i = 0; i < event.pointerProperties.size(); i++) {
- auto* pointer = outProto.add_pointer();
-
- const auto& props = event.pointerProperties[i];
- pointer->set_pointer_id(props.id);
- pointer->set_tool_type(static_cast<int32_t>(props.toolType));
-
- const auto& coords = event.pointerCoords[i];
- auto bits = BitSet64(coords.bits);
- for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
- const auto axis = bits.clearFirstMarkedBit();
- auto axisEntry = pointer->add_axis_value();
- axisEntry->set_axis(axis);
-
- if (!isRedacted) {
- axisEntry->set_value(coords.values[axisIndex]);
- }
- }
- }
-}
-
-void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event,
- proto::AndroidKeyEvent& outProto,
- bool isRedacted) {
- outProto.set_event_id(event.id);
- outProto.set_event_time_nanos(event.eventTime);
- outProto.set_down_time_nanos(event.downTime);
- outProto.set_source(event.source);
- outProto.set_action(event.action);
- outProto.set_device_id(event.deviceId);
- outProto.set_display_id(event.displayId.val());
- outProto.set_repeat_count(event.repeatCount);
- outProto.set_flags(event.flags);
- outProto.set_policy_flags(event.policyFlags);
-
- if (!isRedacted) {
- outProto.set_key_code(event.keyCode);
- outProto.set_scan_code(event.scanCode);
- outProto.set_meta_state(event.metaState);
- }
-}
-
-void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(
- const WindowDispatchArgs& args, proto::AndroidWindowInputDispatchEvent& outProto,
- bool isRedacted) {
- std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry);
- outProto.set_vsync_id(args.vsyncId);
- outProto.set_window_id(args.windowId);
- outProto.set_resolved_flags(args.resolvedFlags);
-
- if (isRedacted) {
- return;
- }
- if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) {
- for (size_t i = 0; i < motion->pointerProperties.size(); i++) {
- auto* pointerProto = outProto.add_dispatched_pointer();
- pointerProto->set_pointer_id(motion->pointerProperties[i].id);
- const auto rawXY =
- MotionEvent::calculateTransformedXY(motion->source, args.rawTransform,
- motion->pointerCoords[i].getXYValue());
- pointerProto->set_x_in_display(rawXY.x);
- pointerProto->set_y_in_display(rawXY.y);
-
- const auto& coords = motion->pointerCoords[i];
- const auto coordsInWindow =
- MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
- args.transform, coords);
- auto bits = BitSet64(coords.bits);
- for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
- const uint32_t axis = bits.clearFirstMarkedBit();
- const float axisValueInWindow = coordsInWindow.values[axisIndex];
- if (coords.values[axisIndex] != axisValueInWindow) {
- auto* axisEntry = pointerProto->add_axis_value_in_window();
- axisEntry->set_axis(axis);
- axisEntry->set_value(axisValueInWindow);
- }
- }
- }
- }
-}
-
-impl::TraceConfig AndroidInputEventProtoConverter::parseConfig(
- proto::AndroidInputEventConfig::Decoder& protoConfig) {
- if (protoConfig.has_mode() &&
- protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) {
- // User has requested the preset for maximal tracing
- return CONFIG_TRACE_ALL;
- }
-
- impl::TraceConfig config;
-
- // Parse trace flags
- if (protoConfig.has_trace_dispatcher_input_events() &&
- protoConfig.trace_dispatcher_input_events()) {
- config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS;
- }
- if (protoConfig.has_trace_dispatcher_window_dispatch() &&
- protoConfig.trace_dispatcher_window_dispatch()) {
- config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH;
- }
-
- // Parse trace rules
- auto rulesIt = protoConfig.rules();
- while (rulesIt) {
- proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()};
- config.rules.emplace_back();
- auto& rule = config.rules.back();
-
- rule.level = protoRule.has_trace_level()
- ? static_cast<impl::TraceLevel>(protoRule.trace_level())
- : impl::TraceLevel::TRACE_LEVEL_NONE;
-
- if (protoRule.has_match_all_packages()) {
- auto pkgIt = protoRule.match_all_packages();
- while (pkgIt) {
- rule.matchAllPackages.emplace_back(pkgIt->as_std_string());
- pkgIt++;
- }
- }
-
- if (protoRule.has_match_any_packages()) {
- auto pkgIt = protoRule.match_any_packages();
- while (pkgIt) {
- rule.matchAnyPackages.emplace_back(pkgIt->as_std_string());
- pkgIt++;
- }
- }
-
- if (protoRule.has_match_secure()) {
- rule.matchSecure = protoRule.match_secure();
- }
-
- if (protoRule.has_match_ime_connection_active()) {
- rule.matchImeConnectionActive = protoRule.match_ime_connection_active();
- }
-
- rulesIt++;
- }
-
- return config;
-}
-
-} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
index 887913f..c19d278 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
@@ -26,20 +26,214 @@
namespace android::inputdispatcher::trace {
+namespace internal {
+
+using namespace ftl::flag_operators;
+
+// The trace config to use for maximal tracing.
+const impl::TraceConfig CONFIG_TRACE_ALL{
+ .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS |
+ impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH,
+ .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE,
+ .matchAllPackages = {},
+ .matchAnyPackages = {},
+ .matchSecure{},
+ .matchImeConnectionActive = {}}},
+};
+
+template <typename Pointer>
+void writeAxisValue(Pointer* pointer, int32_t axis, float value, bool isRedacted) {
+ auto* axisEntry = pointer->add_axis_value();
+ axisEntry->set_axis(axis);
+
+ if (!isRedacted) {
+ axisEntry->set_value(value);
+ }
+}
+
+} // namespace internal
+
/**
* Write traced events into Perfetto protos.
+ *
+ * This class is templated so that the logic can be tested while substituting the proto classes
+ * auto-generated by Perfetto's pbzero library with mock implementations.
*/
+template <typename ProtoMotion, typename ProtoKey, typename ProtoDispatch,
+ typename ProtoConfigDecoder>
class AndroidInputEventProtoConverter {
public:
- static void toProtoMotionEvent(const TracedMotionEvent& event,
- proto::AndroidMotionEvent& outProto, bool isRedacted);
- static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto,
- bool isRedacted);
- static void toProtoWindowDispatchEvent(const WindowDispatchArgs&,
- proto::AndroidWindowInputDispatchEvent& outProto,
- bool isRedacted);
+ static void toProtoMotionEvent(const TracedMotionEvent& event, ProtoMotion& outProto,
+ bool isRedacted) {
+ outProto.set_event_id(event.id);
+ outProto.set_event_time_nanos(event.eventTime);
+ outProto.set_down_time_nanos(event.downTime);
+ outProto.set_source(event.source);
+ outProto.set_action(event.action);
+ outProto.set_device_id(event.deviceId);
+ outProto.set_display_id(event.displayId.val());
+ outProto.set_classification(static_cast<int32_t>(event.classification));
+ outProto.set_flags(event.flags);
+ outProto.set_policy_flags(event.policyFlags);
+ outProto.set_button_state(event.buttonState);
+ outProto.set_action_button(event.actionButton);
- static impl::TraceConfig parseConfig(proto::AndroidInputEventConfig::Decoder& protoConfig);
+ if (!isRedacted) {
+ outProto.set_cursor_position_x(event.xCursorPosition);
+ outProto.set_cursor_position_y(event.yCursorPosition);
+ outProto.set_meta_state(event.metaState);
+ outProto.set_precision_x(event.xPrecision);
+ outProto.set_precision_y(event.yPrecision);
+ }
+
+ for (uint32_t i = 0; i < event.pointerProperties.size(); i++) {
+ auto* pointer = outProto.add_pointer();
+
+ const auto& props = event.pointerProperties[i];
+ pointer->set_pointer_id(props.id);
+ pointer->set_tool_type(static_cast<int32_t>(props.toolType));
+
+ const auto& coords = event.pointerCoords[i];
+ auto bits = BitSet64(coords.bits);
+
+ if (isFromSource(event.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ // Always include the X and Y axes for pointer events, since the
+ // bits will not be marked if the value is 0.
+ for (const auto axis : {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y}) {
+ if (!bits.hasBit(axis)) {
+ internal::writeAxisValue(pointer, axis, 0.0f, isRedacted);
+ }
+ }
+ }
+
+ for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
+ const auto axis = bits.clearFirstMarkedBit();
+ internal::writeAxisValue(pointer, axis, coords.values[axisIndex], isRedacted);
+ }
+ }
+ }
+
+ static void toProtoKeyEvent(const TracedKeyEvent& event, ProtoKey& outProto, bool isRedacted) {
+ outProto.set_event_id(event.id);
+ outProto.set_event_time_nanos(event.eventTime);
+ outProto.set_down_time_nanos(event.downTime);
+ outProto.set_source(event.source);
+ outProto.set_action(event.action);
+ outProto.set_device_id(event.deviceId);
+ outProto.set_display_id(event.displayId.val());
+ outProto.set_repeat_count(event.repeatCount);
+ outProto.set_flags(event.flags);
+ outProto.set_policy_flags(event.policyFlags);
+
+ if (!isRedacted) {
+ outProto.set_key_code(event.keyCode);
+ outProto.set_scan_code(event.scanCode);
+ outProto.set_meta_state(event.metaState);
+ }
+ }
+
+ static void toProtoWindowDispatchEvent(const WindowDispatchArgs& args, ProtoDispatch& outProto,
+ bool isRedacted) {
+ std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry);
+ outProto.set_vsync_id(args.vsyncId);
+ outProto.set_window_id(args.windowId);
+ outProto.set_resolved_flags(args.resolvedFlags);
+
+ if (isRedacted) {
+ return;
+ }
+ if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) {
+ for (size_t i = 0; i < motion->pointerProperties.size(); i++) {
+ auto* pointerProto = outProto.add_dispatched_pointer();
+ pointerProto->set_pointer_id(motion->pointerProperties[i].id);
+ const auto& coords = motion->pointerCoords[i];
+ const auto rawXY =
+ MotionEvent::calculateTransformedXY(motion->source, args.rawTransform,
+ coords.getXYValue());
+ if (coords.getXYValue() != rawXY) {
+ // These values are only traced if they were modified by the raw transform
+ // to save space. Trace consumers should be aware of this optimization.
+ pointerProto->set_x_in_display(rawXY.x);
+ pointerProto->set_y_in_display(rawXY.y);
+ }
+
+ const auto coordsInWindow =
+ MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
+ args.transform, coords);
+ auto bits = BitSet64(coords.bits);
+ for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
+ const uint32_t axis = bits.clearFirstMarkedBit();
+ const float axisValueInWindow = coordsInWindow.values[axisIndex];
+ // Only values that are modified by the window transform are traced.
+ if (coords.values[axisIndex] != axisValueInWindow) {
+ auto* axisEntry = pointerProto->add_axis_value_in_window();
+ axisEntry->set_axis(axis);
+ axisEntry->set_value(axisValueInWindow);
+ }
+ }
+ }
+ }
+ }
+
+ static impl::TraceConfig parseConfig(ProtoConfigDecoder& protoConfig) {
+ if (protoConfig.has_mode() &&
+ protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) {
+ // User has requested the preset for maximal tracing
+ return internal::CONFIG_TRACE_ALL;
+ }
+
+ impl::TraceConfig config;
+
+ // Parse trace flags
+ if (protoConfig.has_trace_dispatcher_input_events() &&
+ protoConfig.trace_dispatcher_input_events()) {
+ config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS;
+ }
+ if (protoConfig.has_trace_dispatcher_window_dispatch() &&
+ protoConfig.trace_dispatcher_window_dispatch()) {
+ config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH;
+ }
+
+ // Parse trace rules
+ auto rulesIt = protoConfig.rules();
+ while (rulesIt) {
+ proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()};
+ config.rules.emplace_back();
+ auto& rule = config.rules.back();
+
+ rule.level = protoRule.has_trace_level()
+ ? static_cast<impl::TraceLevel>(protoRule.trace_level())
+ : impl::TraceLevel::TRACE_LEVEL_NONE;
+
+ if (protoRule.has_match_all_packages()) {
+ auto pkgIt = protoRule.match_all_packages();
+ while (pkgIt) {
+ rule.matchAllPackages.emplace_back(pkgIt->as_std_string());
+ pkgIt++;
+ }
+ }
+
+ if (protoRule.has_match_any_packages()) {
+ auto pkgIt = protoRule.match_any_packages();
+ while (pkgIt) {
+ rule.matchAnyPackages.emplace_back(pkgIt->as_std_string());
+ pkgIt++;
+ }
+ }
+
+ if (protoRule.has_match_secure()) {
+ rule.matchSecure = protoRule.match_secure();
+ }
+
+ if (protoRule.has_match_ime_connection_active()) {
+ rule.matchImeConnectionActive = protoRule.match_ime_connection_active();
+ }
+
+ rulesIt++;
+ }
+
+ return config;
+ }
};
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index 761d619..2ff6e1c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -50,7 +50,7 @@
uint32_t policyFlags;
int32_t deviceId;
uint32_t source;
- ui::LogicalDisplayId displayId;
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
int32_t action;
int32_t keyCode;
int32_t scanCode;
@@ -70,7 +70,7 @@
uint32_t policyFlags;
int32_t deviceId;
uint32_t source;
- ui::LogicalDisplayId displayId;
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
int32_t action;
int32_t actionButton;
int32_t flags;
@@ -108,7 +108,7 @@
TracedEvent eventEntry;
nsecs_t deliveryTime;
int32_t resolvedFlags;
- gui::Uid targetUid;
+ gui::Uid targetUid = gui::Uid::INVALID;
int64_t vsyncId;
int32_t windowId;
ui::Transform transform;
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index 77b5c2e..ebcd9c9 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -34,6 +34,11 @@
constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent";
+using ProtoConverter =
+ AndroidInputEventProtoConverter<proto::AndroidMotionEvent, proto::AndroidKeyEvent,
+ proto::AndroidWindowInputDispatchEvent,
+ proto::AndroidInputEventConfig::Decoder>;
+
bool isPermanentlyAllowed(gui::Uid uid) {
switch (uid.val()) {
case AID_SYSTEM:
@@ -85,7 +90,7 @@
const auto rawConfig = args.config->android_input_event_config_raw();
auto protoConfig = perfetto::protos::pbzero::AndroidInputEventConfig::Decoder{rawConfig};
- mConfig = AndroidInputEventProtoConverter::parseConfig(protoConfig);
+ mConfig = ProtoConverter::parseConfig(protoConfig);
}
void PerfettoBackend::InputEventDataSource::OnStart(const InputEventDataSource::StartArgs&) {
@@ -238,7 +243,7 @@
auto* inputEvent = winscopeExtensions->set_android_input_event();
auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted()
: inputEvent->set_dispatcher_motion_event();
- AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted);
+ ProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted);
});
}
@@ -266,7 +271,7 @@
auto* inputEvent = winscopeExtensions->set_android_input_event();
auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted()
: inputEvent->set_dispatcher_key_event();
- AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted);
+ ProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted);
});
}
@@ -295,8 +300,7 @@
auto* dispatchEvent = isRedacted
? inputEvent->set_dispatcher_window_dispatch_event_redacted()
: inputEvent->set_dispatcher_window_dispatch_event();
- AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent,
- isRedacted);
+ ProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent, isRedacted);
});
}
diff --git a/services/inputflinger/docs/device_configuration.md b/services/inputflinger/docs/device_configuration.md
new file mode 100644
index 0000000..0b75eb2
--- /dev/null
+++ b/services/inputflinger/docs/device_configuration.md
@@ -0,0 +1,10 @@
+# Input Device Configuration
+
+There are a number of properties that can be specified for an input device.
+
+|Property|Value|
+|---|----|
+|`audio.mic`|A boolean (`0` or `1`) that indicates whether the device has a microphone.|
+|`device.additionalSysfsLedsNode`|A string representing the path to search for device lights to be used in addition to searching the device node itself for lights.|
+|`device.internal`|A boolean (`0` or `1`) that indicates if this input device is part of the device as opposed to be externally attached.|
+|`device.type`|A string representing if the device is of a certain type. Valid values include `rotaryEncoder` and `externalStylus`.
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 4d6b6c7..c843200 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -139,8 +139,21 @@
// The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest).
int32_t mousePointerSpeed;
- // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
- std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled;
+ // Displays on which all pointer scaling, including linear scaling based on the
+ // user's pointer speed setting, should be disabled for mice. This differs from
+ // disabling acceleration via the 'mousePointerAccelerationEnabled' setting, where
+ // the pointer speed setting still influences the scaling factor.
+ std::set<ui::LogicalDisplayId> displaysWithMouseScalingDisabled;
+
+ // True if the connected mouse should exhibit pointer acceleration. If false,
+ // a flat acceleration curve (linear scaling) is used, but the user's pointer
+ // speed setting still affects the scaling factor.
+ bool mousePointerAccelerationEnabled;
+
+ // True if the touchpad should exhibit pointer acceleration. If false,
+ // a flat acceleration curve (linear scaling) is used, but the user's pointer
+ // speed setting still affects the scaling factor.
+ bool touchpadAccelerationEnabled;
// Velocity control parameters for touchpad pointer movements on the old touchpad stack (based
// on TouchInputMapper).
@@ -274,12 +287,17 @@
: virtualKeyQuietTime(0),
defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
mousePointerSpeed(0),
- displaysWithMousePointerAccelerationDisabled(),
+ displaysWithMouseScalingDisabled(),
+ mousePointerAccelerationEnabled(true),
+ touchpadAccelerationEnabled(true),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
static_cast<float>(
android::os::IInputConstants::
DEFAULT_POINTER_ACCELERATION)),
- wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
+ wheelVelocityControlParameters(1.0f, 15.0f, 50.0f,
+ static_cast<float>(
+ android::os::IInputConstants::
+ DEFAULT_MOUSE_WHEEL_ACCELERATION)),
pointerGesturesEnabled(true),
pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
@@ -425,6 +443,9 @@
/* Get the Bluetooth address of an input device, if known. */
virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0;
+ /* Gets the sysfs root path for this device. Returns an empty path if there is none. */
+ virtual std::filesystem::path getSysfsRootPath(int32_t deviceId) const = 0;
+
/* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 14487fe..c513bfc 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -225,4 +225,6 @@
const char* toString(const NotifyArgs& args);
+std::ostream& operator<<(std::ostream& out, const NotifyArgs& args);
+
} // namespace android
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 36614b2..c805b74 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -61,6 +61,16 @@
/* Notifies that mouse cursor faded due to typing. */
virtual void notifyMouseCursorFadedOnTyping() = 0;
+
+ /**
+ * Give accessibility a chance to filter motion event by pointer devices.
+ * The return values denotes the delta x and y after filtering it.
+ *
+ * This call happens on the input hot path and it is extremely performance sensitive.
+ * This also must not call back into native code.
+ */
+ virtual std::optional<vec2> filterPointerMotionForAccessibility(
+ const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b3cd35c..dc7f7c1 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -66,9 +66,10 @@
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
"mapper/accumulator/TouchButtonAccumulator.cpp",
"mapper/gestures/GestureConverter.cpp",
- "mapper/gestures/GesturesLogging.cpp",
+ "mapper/gestures/GesturesLogcatAdapter.cpp",
"mapper/gestures/HardwareProperties.cpp",
"mapper/gestures/HardwareStateConverter.cpp",
+ "mapper/gestures/Logging.cpp",
"mapper/gestures/PropertyProvider.cpp",
"mapper/gestures/TimerProvider.cpp",
],
@@ -79,25 +80,25 @@
srcs: [":libinputreader_sources"],
shared_libs: [
"android.companion.virtualdevice.flags-aconfig-cc",
+ "libPlatformProperties",
"libbase",
"libcap",
"libcrypto",
"libcutils",
- "libjsoncpp",
"libinput",
+ "libjsoncpp",
"liblog",
- "libPlatformProperties",
"libstatslog",
"libstatspull",
- "libutils",
"libstatssocket",
+ "libutils",
],
static_libs: [
"libchrome-gestures",
- "libui-types",
"libexpresslog",
- "libtextclassifier_hash_static",
"libstatslog_express",
+ "libtextclassifier_hash_static",
+ "libui-types",
],
header_libs: [
"libbatteryservice_headers",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 013ef86..559bc0a 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -246,7 +246,7 @@
/**
* Returns the sysfs root path of the input device.
*/
-static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) {
+static std::optional<std::filesystem::path> getSysfsRootForEvdevDevicePath(const char* devicePath) {
std::error_code errorCode;
// Stat the device path to get the major and minor number of the character file
@@ -351,6 +351,22 @@
return colors;
}
+static base::Result<std::shared_ptr<PropertyMap>> loadConfiguration(
+ const InputDeviceIdentifier& ident) {
+ std::string configurationFile =
+ getInputDeviceConfigurationFilePathByDeviceIdentifier(ident,
+ InputDeviceConfigurationFileType::
+ CONFIGURATION);
+ if (configurationFile.empty()) {
+ ALOGD("No input device configuration file found for device '%s'.", ident.name.c_str());
+ return base::Result<std::shared_ptr<PropertyMap>>(nullptr);
+ }
+ base::Result<std::shared_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(configurationFile.c_str());
+
+ return propertyMap;
+}
+
/**
* Read country code information exposed through the sysfs path and convert it to Layout info.
*/
@@ -409,11 +425,22 @@
* Read information about lights exposed through the sysfs path.
*/
static std::unordered_map<int32_t /*lightId*/, RawLightInfo> readLightsConfiguration(
- const std::filesystem::path& sysfsRootPath) {
+ const std::filesystem::path& sysfsRootPath, const std::shared_ptr<PropertyMap>& config) {
std::unordered_map<int32_t, RawLightInfo> lightInfos;
int32_t nextLightId = 0;
- // Check if device has any lights.
- const auto& paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+ // Check if device has any lights. If the Input Device Configuration file specifies any lights,
+ // use those in addition to searching the device node itself for lights.
+ std::vector<std::filesystem::path> paths = findSysfsNodes(sysfsRootPath, SysfsClass::LEDS);
+
+ if (config) {
+ auto additionalLights = config->getString("device.additionalSysfsLedsNode");
+ if (additionalLights) {
+ ALOGI("IDC specifies additional path for lights at '%s'",
+ additionalLights.value().c_str());
+ paths.push_back(std::filesystem::path(additionalLights.value()));
+ }
+ }
+
for (const auto& nodePath : paths) {
RawLightInfo info;
info.id = ++nextLightId;
@@ -532,17 +559,16 @@
// --- EventHub::Device ---
EventHub::Device::Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
- std::shared_ptr<const AssociatedDevice> assocDev)
+ std::shared_ptr<PropertyMap> config)
: fd(fd),
id(id),
path(std::move(path)),
identifier(std::move(identifier)),
classes(0),
- configuration(nullptr),
+ configuration(std::move(config)),
virtualKeyMap(nullptr),
ffEffectPlaying(false),
ffEffectId(-1),
- associatedDevice(std::move(assocDev)),
controllerNumber(0),
enabled(true),
isVirtual(fd < 0),
@@ -696,26 +722,6 @@
return false;
}
-void EventHub::Device::loadConfigurationLocked() {
- configurationFile =
- getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
- InputDeviceConfigurationFileType::
- CONFIGURATION);
- if (configurationFile.empty()) {
- ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
- } else {
- android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
- PropertyMap::load(configurationFile.c_str());
- if (!propertyMap.ok()) {
- ALOGE("Error loading input device configuration file for device '%s'. "
- "Using default configuration.",
- identifier.name.c_str());
- } else {
- configuration = std::move(*propertyMap);
- }
- }
-}
-
bool EventHub::Device::loadVirtualKeyMapLocked() {
// The virtual key map is supplied by the kernel as a system board property file.
std::string propPath = "/sys/board_properties/virtualkeys.";
@@ -1611,50 +1617,59 @@
}
std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked(
- const std::filesystem::path& devicePath) const {
+ const std::filesystem::path& devicePath, const std::shared_ptr<PropertyMap>& config) const {
const std::optional<std::filesystem::path> sysfsRootPathOpt =
- getSysfsRootPath(devicePath.c_str());
+ getSysfsRootForEvdevDevicePath(devicePath.c_str());
if (!sysfsRootPathOpt) {
return nullptr;
}
const auto& path = *sysfsRootPathOpt;
- std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>(
- AssociatedDevice{.sysfsRootPath = path,
- .batteryInfos = readBatteryConfiguration(path),
- .lightInfos = readLightsConfiguration(path),
- .layoutInfo = readLayoutConfiguration(path)});
-
- bool associatedDeviceChanged = false;
+ std::shared_ptr<const AssociatedDevice> associatedDevice;
for (const auto& [id, dev] : mDevices) {
- if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) {
- if (*associatedDevice != *dev->associatedDevice) {
- associatedDeviceChanged = true;
- dev->associatedDevice = associatedDevice;
- }
- associatedDevice = dev->associatedDevice;
+ if (!dev->associatedDevice || dev->associatedDevice->sysfsRootPath != path) {
+ continue;
}
+ if (!associatedDevice) {
+ // Found matching associated device for the first time.
+ associatedDevice = dev->associatedDevice;
+ // Reload this associated device if needed. Use the base device
+ // config. Note that this will essentially arbitrarily pick one
+ // Device as the base for the AssociatedDevice configuration. If
+ // there are multiple Device's that have a configuration for the
+ // AssociatedDevice, only one configuration will be chosen and will
+ // be used for all other AssociatedDevices for the same sysfs path.
+ const auto reloadedDevice = AssociatedDevice(path, associatedDevice->baseDevConfig);
+ if (reloadedDevice != *dev->associatedDevice) {
+ ALOGI("The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
+ path.c_str(), associatedDevice->dump().c_str());
+ associatedDevice = std::make_shared<AssociatedDevice>(std::move(reloadedDevice));
+ }
+ }
+ // Update the associatedDevice.
+ dev->associatedDevice = associatedDevice;
}
- ALOGI_IF(associatedDeviceChanged,
- "The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
- path.c_str(), associatedDevice->dump().c_str());
+
+ if (!associatedDevice) {
+ // No existing associated device found for this path, so create a new one.
+ associatedDevice = std::make_shared<AssociatedDevice>(path, config);
+ }
return associatedDevice;
}
-bool EventHub::AssociatedDevice::isChanged() const {
- std::unordered_map<int32_t, RawBatteryInfo> newBatteryInfos =
- readBatteryConfiguration(sysfsRootPath);
- std::unordered_map<int32_t, RawLightInfo> newLightInfos =
- readLightsConfiguration(sysfsRootPath);
- std::optional<RawLayoutInfo> newLayoutInfo = readLayoutConfiguration(sysfsRootPath);
+EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& sysfsRootPath,
+ std::shared_ptr<PropertyMap> config)
+ : sysfsRootPath(sysfsRootPath),
+ baseDevConfig(std::move(config)),
+ batteryInfos(readBatteryConfiguration(sysfsRootPath)),
+ lightInfos(readLightsConfiguration(sysfsRootPath, baseDevConfig)),
+ layoutInfo(readLayoutConfiguration(sysfsRootPath)) {}
- if (newBatteryInfos == batteryInfos && newLightInfos == lightInfos &&
- newLayoutInfo == layoutInfo) {
- return false;
- }
- return true;
+std::string EventHub::AssociatedDevice::dump() const {
+ return StringPrintf("path=%s, numBatteries=%zu, numLight=%zu", sysfsRootPath.c_str(),
+ batteryInfos.size(), lightInfos.size());
}
void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
@@ -1882,58 +1897,89 @@
break; // return to the caller before we actually rescan
}
- // Report any devices that had last been added/removed.
- for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
- std::unique_ptr<Device> device = std::move(*it);
- ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
- const int32_t deviceId = (device->id == mBuiltInKeyboardId)
- ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
- : device->id;
- events.push_back({
- .when = now,
- .deviceId = deviceId,
- .type = DEVICE_REMOVED,
- });
- it = mClosingDevices.erase(it);
- if (events.size() == EVENT_BUFFER_SIZE) {
- break;
+ handleSysfsNodeChangeNotificationsLocked();
+
+ // Use a do-while loop to ensure that we drain the closing and opening devices loop
+ // at least once, even if there are no devices to re-open.
+ do {
+ if (!mDeviceIdsToReopen.empty()) {
+ // If there are devices that need to be re-opened, ensure that we re-open them
+ // one at a time to send the DEVICE_REMOVED and DEVICE_ADDED notifications for
+ // each before moving on to the next. This is to avoid notifying all device
+ // removals and additions in one batch, which could cause additional unnecessary
+ // device added/removed notifications for merged InputDevices from InputReader.
+ const int32_t deviceId = mDeviceIdsToReopen.back();
+ mDeviceIdsToReopen.erase(mDeviceIdsToReopen.end() - 1);
+ if (auto it = mDevices.find(deviceId); it != mDevices.end()) {
+ ALOGI("Reopening input device: id=%d, name=%s", it->second->id,
+ it->second->identifier.name.c_str());
+ const auto path = it->second->path;
+ closeDeviceLocked(*it->second);
+ openDeviceLocked(path);
+ }
}
- }
- if (mNeedToScanDevices) {
- mNeedToScanDevices = false;
- scanDevicesLocked();
- }
-
- while (!mOpeningDevices.empty()) {
- std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
- mOpeningDevices.pop_back();
- ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
- const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
- events.push_back({
- .when = now,
- .deviceId = deviceId,
- .type = DEVICE_ADDED,
- });
-
- // Try to find a matching video device by comparing device names
- for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
- it++) {
- std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
- if (tryAddVideoDeviceLocked(*device, videoDevice)) {
- // videoDevice was transferred to 'device'
- it = mUnattachedVideoDevices.erase(it);
+ // Report any devices that had last been added/removed.
+ for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
+ std::unique_ptr<Device> device = std::move(*it);
+ ALOGV("Reporting device closed: id=%d, name=%s\n", device->id,
+ device->path.c_str());
+ const int32_t deviceId = (device->id == mBuiltInKeyboardId)
+ ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
+ : device->id;
+ events.push_back({
+ .when = now,
+ .deviceId = deviceId,
+ .type = DEVICE_REMOVED,
+ });
+ it = mClosingDevices.erase(it);
+ if (events.size() == EVENT_BUFFER_SIZE) {
break;
}
}
- auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
- if (!inserted) {
- ALOGW("Device id %d exists, replaced.", device->id);
+ if (mNeedToScanDevices) {
+ mNeedToScanDevices = false;
+ scanDevicesLocked();
}
- if (events.size() == EVENT_BUFFER_SIZE) {
- break;
+
+ while (!mOpeningDevices.empty()) {
+ std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
+ mOpeningDevices.pop_back();
+ ALOGV("Reporting device opened: id=%d, name=%s\n", device->id,
+ device->path.c_str());
+ const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+ events.push_back({
+ .when = now,
+ .deviceId = deviceId,
+ .type = DEVICE_ADDED,
+ });
+
+ // Try to find a matching video device by comparing device names
+ for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
+ it++) {
+ std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
+ if (tryAddVideoDeviceLocked(*device, videoDevice)) {
+ // videoDevice was transferred to 'device'
+ it = mUnattachedVideoDevices.erase(it);
+ break;
+ }
+ }
+
+ auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
+ if (!inserted) {
+ ALOGW("Device id %d exists, replaced.", device->id);
+ }
+ if (events.size() == EVENT_BUFFER_SIZE) {
+ break;
+ }
}
+
+ // Perform this loop of re-opening devices so that we re-open one device at a time.
+ } while (!mDeviceIdsToReopen.empty());
+
+ if (events.size() == EVENT_BUFFER_SIZE) {
+ break;
}
// Grab the next input event.
@@ -2335,11 +2381,21 @@
// Fill in the descriptor.
assignDescriptorLocked(identifier);
+ // Load the configuration file for the device.
+ std::shared_ptr<PropertyMap> configuration = nullptr;
+ base::Result<std::shared_ptr<PropertyMap>> propertyMapResult = loadConfiguration(identifier);
+ if (!propertyMapResult.ok()) {
+ ALOGE("Error loading input device configuration file for device '%s'. "
+ "Using default configuration. Error: %s",
+ identifier.name.c_str(), propertyMapResult.error().message().c_str());
+ } else {
+ configuration = propertyMapResult.value();
+ }
+
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
std::unique_ptr<Device> device =
- std::make_unique<Device>(fd, deviceId, devicePath, identifier,
- obtainAssociatedDeviceLocked(devicePath));
+ std::make_unique<Device>(fd, deviceId, devicePath, identifier, configuration);
ALOGV("add device %d: %s\n", deviceId, devicePath.c_str());
ALOGV(" bus: %04x\n"
@@ -2354,8 +2410,8 @@
ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff,
driverVersion & 0xff);
- // Load the configuration file for the device.
- device->loadConfigurationLocked();
+ // Obtain the associated device, if any.
+ device->associatedDevice = obtainAssociatedDeviceLocked(devicePath, device->configuration);
// Figure out the kinds of events the device reports.
device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask);
@@ -2639,40 +2695,91 @@
return device->disable();
}
+std::filesystem::path EventHub::getSysfsRootPath(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__);
+ return {};
+ }
+
+ return device->associatedDevice ? device->associatedDevice->sysfsRootPath
+ : std::filesystem::path{};
+}
+
// TODO(b/274755573): Shift to uevent handling on native side and remove this method
// Currently using Java UEventObserver to trigger this which uses UEvent infrastructure that uses a
// NETLINK socket to observe UEvents. We can create similar infrastructure on Eventhub side to
// directly observe UEvents instead of triggering from Java side.
void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
- std::scoped_lock _l(mLock);
+ mChangedSysfsNodeNotifications.emplace(sysfsNodePath);
+}
- // Check in opening devices
- for (auto it = mOpeningDevices.begin(); it != mOpeningDevices.end(); it++) {
- std::unique_ptr<Device>& device = *it;
- if (device->associatedDevice &&
- sysfsNodePath.find(device->associatedDevice->sysfsRootPath.string()) !=
- std::string::npos &&
- device->associatedDevice->isChanged()) {
- it = mOpeningDevices.erase(it);
- openDeviceLocked(device->path);
+void EventHub::handleSysfsNodeChangeNotificationsLocked() {
+ // Use a set to de-dup any repeated notifications.
+ std::set<std::string> changedNodes;
+ while (true) {
+ auto node = mChangedSysfsNodeNotifications.popWithTimeout(std::chrono::nanoseconds(0));
+ if (!node.has_value()) break;
+ changedNodes.emplace(*node);
+ }
+ if (changedNodes.empty()) {
+ return;
+ }
+
+ // Testing whether a sysfs node changed involves several syscalls, so use a cache to avoid
+ // testing the same node multiple times.
+ // TODO(b/281822656): Notify InputReader separately when an AssociatedDevice changes,
+ // instead of needing to re-open all of Devices that are associated with it.
+ std::map<std::shared_ptr<const AssociatedDevice>, bool /*changed*/> testedDevices;
+ auto shouldReopenDevice = [&testedDevices, &changedNodes](const Device& dev) {
+ if (!dev.associatedDevice) {
+ return false;
+ }
+ if (auto testedIt = testedDevices.find(dev.associatedDevice);
+ testedIt != testedDevices.end()) {
+ return testedIt->second;
+ }
+ // Cache miss
+ const bool anyNodesChanged =
+ std::any_of(changedNodes.begin(), changedNodes.end(), [&](const std::string& node) {
+ return node.find(dev.associatedDevice->sysfsRootPath.string()) !=
+ std::string::npos;
+ });
+ if (!anyNodesChanged) {
+ testedDevices.emplace(dev.associatedDevice, false);
+ return false;
+ }
+ auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath,
+ dev.associatedDevice->baseDevConfig);
+ const bool changed = *dev.associatedDevice != reloadedDevice;
+ if (changed) {
+ ALOGI("sysfsNodeChanged: Identified change in sysfs nodes for device: %s",
+ dev.identifier.name.c_str());
+ }
+ testedDevices.emplace(dev.associatedDevice, changed);
+ return changed;
+ };
+
+ // Check in opening devices. These can be re-opened directly because we have not yet notified
+ // the Reader about these devices.
+ for (const auto& dev : mOpeningDevices) {
+ if (shouldReopenDevice(*dev)) {
+ ALOGI("Reopening input device from mOpeningDevices: id=%d, name=%s", dev->id,
+ dev->identifier.name.c_str());
+ const auto path = dev->path;
+ closeDeviceLocked(*dev); // The Device object is deleted by this function.
+ openDeviceLocked(path);
}
}
- // Check in already added device
- std::vector<Device*> devicesToReopen;
- for (const auto& [id, device] : mDevices) {
- if (device->associatedDevice &&
- sysfsNodePath.find(device->associatedDevice->sysfsRootPath.string()) !=
- std::string::npos &&
- device->associatedDevice->isChanged()) {
- devicesToReopen.push_back(device.get());
+ // Check in already added devices. Add them to the re-opening list so they can be
+ // re-opened serially.
+ for (const auto& [id, dev] : mDevices) {
+ if (shouldReopenDevice(*dev)) {
+ mDeviceIdsToReopen.emplace_back(dev->id);
}
}
- for (const auto& device : devicesToReopen) {
- closeDeviceLocked(*device);
- openDeviceLocked(device->path);
- }
- devicesToReopen.clear();
}
void EventHub::createVirtualKeyboardLocked() {
@@ -2768,9 +2875,23 @@
releaseControllerNumberLocked(device.controllerNumber);
device.controllerNumber = 0;
device.close();
- mClosingDevices.push_back(std::move(mDevices[device.id]));
- mDevices.erase(device.id);
+ // Try to remove this device from mDevices.
+ if (auto it = mDevices.find(device.id); it != mDevices.end()) {
+ mClosingDevices.push_back(std::move(mDevices[device.id]));
+ mDevices.erase(device.id);
+ return;
+ }
+
+ // Try to remove this device from mOpeningDevices.
+ if (auto it = std::find_if(mOpeningDevices.begin(), mOpeningDevices.end(),
+ [&device](auto& d) { return d->id == device.id; });
+ it != mOpeningDevices.end()) {
+ mOpeningDevices.erase(it);
+ return;
+ }
+
+ LOG_ALWAYS_FATAL("%s: Device with id %d was not found!", __func__, device.id);
}
base::Result<void> EventHub::readNotifyLocked() {
@@ -2972,9 +3093,4 @@
std::unique_lock<std::mutex> lock(mLock);
}
-std::string EventHub::AssociatedDevice::dump() const {
- return StringPrintf("path=%s, numBatteries=%zu, numLight=%zu", sysfsRootPath.c_str(),
- batteryInfos.size(), lightInfos.size());
-}
-
} // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 5e42d57..594dcba 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -136,6 +136,8 @@
} else {
dump += "<none>\n";
}
+ dump += StringPrintf(INDENT2 "SysfsRootPath: %s\n",
+ mSysfsRootPath.empty() ? "<none>" : mSysfsRootPath.c_str());
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: %s\n",
inputEventSourceToString(deviceInfo.getSources()).c_str());
@@ -195,6 +197,10 @@
DevicePair& devicePair = mDevices[eventHubId];
devicePair.second = createMappers(*devicePair.first, readerConfig);
+ if (mSysfsRootPath.empty()) {
+ mSysfsRootPath = devicePair.first->getSysfsRootPath();
+ }
+
// Must change generation to flag this device as changed
bumpGeneration();
return out;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 24919b6..74ef972 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <utils/Errors.h>
#include <utils/Thread.h>
+#include <string>
#include "InputDevice.h"
#include "include/gestures.h"
@@ -62,6 +63,28 @@
identifier1.location == identifier2.location);
}
+/**
+ * Determines if the device classes passed for two devices represent incompatible combinations
+ * that should not be merged into into a single InputDevice.
+ */
+
+bool isCompatibleSubDevice(ftl::Flags<InputDeviceClass> classes1,
+ ftl::Flags<InputDeviceClass> classes2) {
+ if (!com::android::input::flags::prevent_merging_input_pointer_devices()) {
+ return true;
+ }
+
+ const ftl::Flags<InputDeviceClass> pointerFlags =
+ ftl::Flags<InputDeviceClass>{InputDeviceClass::TOUCH, InputDeviceClass::TOUCH_MT,
+ InputDeviceClass::CURSOR, InputDeviceClass::TOUCHPAD};
+
+ // Do not merge devices that both have any type of pointer event.
+ if (classes1.any(pointerFlags) && classes2.any(pointerFlags)) return false;
+
+ // Safe to merge
+ return true;
+}
+
bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
@@ -174,9 +197,8 @@
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
- if (debugRawEvents()) {
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
- }
+ ALOGD_IF(debugRawEvents(), "Timeout expired, latency=%0.3fms",
+ (now - mNextTimeout) * 0.000001f);
mNextTimeout = LLONG_MAX;
mPendingArgs += timeoutExpiredLocked(now);
}
@@ -241,9 +263,7 @@
}
batchSize += 1;
}
- if (debugRawEvents()) {
- ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
- }
+ ALOGD_IF(debugRawEvents(), "BatchSize: %zu Count: %zu", batchSize, count);
out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
@@ -271,7 +291,8 @@
}
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
- std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier);
+ ftl::Flags<InputDeviceClass> classes = mEventHub->getDeviceClasses(eventHubId);
+ std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier, classes);
mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
mPendingArgs += device->reset(when);
@@ -354,12 +375,16 @@
}
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
- nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) {
- auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
- const InputDeviceIdentifier identifier2 =
- devicePair.second->getDeviceInfo().getIdentifier();
- return isSubDevice(identifier, identifier2);
- });
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes) {
+ auto deviceIt =
+ std::find_if(mDevices.begin(), mDevices.end(), [identifier, classes](auto& devicePair) {
+ const InputDeviceIdentifier identifier2 =
+ devicePair.second->getDeviceInfo().getIdentifier();
+ const ftl::Flags<InputDeviceClass> classes2 = devicePair.second->getClasses();
+ return isSubDevice(identifier, identifier2) &&
+ isCompatibleSubDevice(classes, classes2);
+ });
std::shared_ptr<InputDevice> device;
if (deviceIt != mDevices.end()) {
@@ -892,8 +917,19 @@
return *associatedDisplayId == displayId;
}
+std::filesystem::path InputReader::getSysfsRootPath(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+
+ const InputDevice* device = findInputDeviceLocked(deviceId);
+ if (!device) {
+ return {};
+ }
+ return device->getSysfsRootPath();
+}
+
void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) {
mEventHub->sysfsNodeChanged(sysfsNodePath);
+ mEventHub->wake();
}
DeviceId InputReader::getLastUsedInputDeviceId() {
@@ -921,7 +957,10 @@
void InputReader::dump(std::string& dump) {
std::scoped_lock _l(mLock);
+ dumpLocked(dump);
+}
+void InputReader::dumpLocked(std::string& dump) {
mEventHub->dump(dump);
dump += "\n";
@@ -1008,6 +1047,12 @@
InputReader::ContextImpl::ContextImpl(InputReader* reader)
: mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {}
+std::string InputReader::ContextImpl::dump() {
+ std::string dump;
+ mReader->dumpLocked(dump);
+ return dump;
+}
+
void InputReader::ContextImpl::updateGlobalMetaState() {
// lock is already held by the input loop
mReader->updateGlobalMetaStateLocked();
@@ -1085,7 +1130,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/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 7434ae4..df22890 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -78,10 +78,8 @@
if (rawMaxBrightness != MAX_BRIGHTNESS) {
brightness = brightness * ratio;
}
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
- brightness, ratio);
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
+ rawLightId, brightness, ratio);
return brightness;
}
@@ -97,10 +95,8 @@
if (rawMaxBrightness != MAX_BRIGHTNESS) {
brightness = ceil(brightness / ratio);
}
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
- brightness, ratio);
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
+ rawLightId, brightness, ratio);
context.setLightBrightness(rawLightId, brightness);
}
@@ -453,10 +449,9 @@
if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
rawGlobalId = rawId;
}
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, rawInfo->name.c_str(),
- rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), rawInfo->flags.string().c_str());
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Light rawId %d name %s max %d flags %s\n", rawInfo->id,
+ rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
+ rawInfo->flags.string().c_str());
}
// Construct a player ID light
@@ -473,10 +468,8 @@
}
// Construct a RGB light for composed RGB light
if (hasRedLed && hasGreenLed && hasBlueLed) {
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
- rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Rgb light ids [%d, %d, %d]\n", rawRgbIds.at(LightColor::RED),
+ rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
keyboardBacklightIds.end() &&
keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
@@ -518,9 +511,8 @@
// If the node is multi-color led, construct a MULTI_COLOR light
if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Multicolor light Id %d name %s\n", rawInfo.id,
+ rawInfo.name.c_str());
std::unique_ptr<Light> light =
std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
type, rawInfo.id);
@@ -528,9 +520,8 @@
continue;
}
// Construct a Mono LED light
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "Mono light Id %d name %s\n", rawInfo.id,
+ rawInfo.name.c_str());
std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
++mNextId, type, rawInfo.id);
@@ -552,10 +543,8 @@
return false;
}
auto& light = it->second;
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
- ftl::enum_string(light->type).c_str(), color);
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "setLightColor lightId %d type %s color 0x%x", lightId,
+ ftl::enum_string(light->type).c_str(), color);
return light->setLightColor(color);
}
@@ -566,10 +555,8 @@
}
auto& light = it->second;
std::optional<int32_t> color = light->getLightColor();
- if (DEBUG_LIGHT_DETAILS) {
- ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
- ftl::enum_string(light->type).c_str(), color.value_or(0));
- }
+ ALOGD_IF(DEBUG_LIGHT_DETAILS, "getLightColor lightId %d type %s color 0x%x", lightId,
+ ftl::enum_string(light->type).c_str(), color.value_or(0));
return color;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5839b4c..9f3a57c 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -21,6 +21,7 @@
#include <filesystem>
#include <functional>
#include <map>
+#include <memory>
#include <optional>
#include <ostream>
#include <string>
@@ -30,6 +31,7 @@
#include <batteryservice/BatteryService.h>
#include <ftl/flags.h>
+#include <input/BlockingQueue.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/KeyCharacterMap.h>
@@ -87,6 +89,7 @@
* If any new classes are added, we need to add them in rust input side too.
*/
enum class InputDeviceClass : uint32_t {
+ // LINT.IfChange
/* The input device is a keyboard or has buttons. */
KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD,
@@ -143,6 +146,7 @@
/* The input device is external (not built-in). */
EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL,
+ // LINT.ThenChange(frameworks/native/services/inputflinger/tests/fuzzers/MapperHelpers.h)
};
enum class SysfsClass : uint32_t {
@@ -395,6 +399,9 @@
/* Disable an input device. Closes file descriptor to that device. */
virtual status_t disableDevice(int32_t deviceId) = 0;
+ /* Gets the sysfs root path for this device. Returns an empty path if there is none. */
+ virtual std::filesystem::path getSysfsRootPath(int32_t deviceId) const = 0;
+
/* Sysfs node changed. Reopen the Eventhub device if any new Peripheral like Light, Battery,
* etc. is detected. */
virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
@@ -610,6 +617,8 @@
status_t disableDevice(int32_t deviceId) override final;
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override final;
+
void sysfsNodeChanged(const std::string& sysfsNodePath) override final;
bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override final;
@@ -619,13 +628,16 @@
private:
// Holds information about the sysfs device associated with the Device.
struct AssociatedDevice {
+ AssociatedDevice(const std::filesystem::path& sysfsRootPath,
+ std::shared_ptr<PropertyMap> baseDevConfig);
// The sysfs root path of the misc device.
std::filesystem::path sysfsRootPath;
+ // The configuration of the base device.
+ std::shared_ptr<PropertyMap> baseDevConfig;
std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
std::optional<RawLayoutInfo> layoutInfo;
- bool isChanged() const;
bool operator==(const AssociatedDevice&) const = default;
bool operator!=(const AssociatedDevice&) const = default;
std::string dump() const;
@@ -658,7 +670,7 @@
std::map<int /*axis*/, AxisState> absState;
std::string configurationFile;
- std::unique_ptr<PropertyMap> configuration;
+ std::shared_ptr<PropertyMap> configuration;
std::unique_ptr<VirtualKeyMap> virtualKeyMap;
KeyMap keyMap;
@@ -672,7 +684,7 @@
int32_t controllerNumber;
Device(int fd, int32_t id, std::string path, InputDeviceIdentifier identifier,
- std::shared_ptr<const AssociatedDevice> assocDev);
+ std::shared_ptr<PropertyMap> config);
~Device();
void close();
@@ -692,7 +704,6 @@
void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
bool hasKeycodeInternalLocked(int keycode) const;
- void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
status_t loadKeyMapLocked();
bool isExternalDeviceLocked();
@@ -724,7 +735,8 @@
void addDeviceLocked(std::unique_ptr<Device> device) REQUIRES(mLock);
void assignDescriptorLocked(InputDeviceIdentifier& identifier) REQUIRES(mLock);
std::shared_ptr<const AssociatedDevice> obtainAssociatedDeviceLocked(
- const std::filesystem::path& devicePath) const REQUIRES(mLock);
+ const std::filesystem::path& devicePath,
+ const std::shared_ptr<PropertyMap>& config) const REQUIRES(mLock);
void closeDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
void closeVideoDeviceByPathLocked(const std::string& devicePath) REQUIRES(mLock);
@@ -769,6 +781,8 @@
void addDeviceInputInotify();
void addDeviceInotify();
+ void handleSysfsNodeChangeNotificationsLocked() REQUIRES(mLock);
+
// Protect all internal state.
mutable std::mutex mLock;
@@ -801,6 +815,7 @@
bool mNeedToReopenDevices;
bool mNeedToScanDevices;
std::vector<std::string> mExcludedDevices;
+ std::vector<int32_t> mDeviceIdsToReopen;
int mEpollFd;
int mINotifyFd;
@@ -818,6 +833,10 @@
size_t mPendingEventCount;
size_t mPendingEventIndex;
bool mPendingINotify;
+
+ // The sysfs node change notifications that have been sent to EventHub.
+ // Enqueuing notifications does not require the lock to be held.
+ BlockingQueue<std::string> mChangedSysfsNodeNotifications;
};
} // namespace android
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 4744dd0..a1a8891 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -81,6 +81,8 @@
inline virtual KeyboardType getKeyboardType() const { return mKeyboardType; }
+ inline std::filesystem::path getSysfsRootPath() const { return mSysfsRootPath; }
+
bool isEnabled();
void dump(std::string& dump, const std::string& eventHubDevStr);
@@ -209,6 +211,7 @@
bool mHasMic;
bool mDropUntilNextSync;
std::optional<bool> mShouldSmoothScroll;
+ std::filesystem::path mSysfsRootPath;
typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
@@ -471,6 +474,9 @@
inline void setKeyboardType(KeyboardType keyboardType) {
return mDevice.setKeyboardType(keyboardType);
}
+ inline std::filesystem::path getSysfsRootPath() const {
+ return mEventHub->getSysfsRootPath(mId);
+ }
inline bool setKernelWakeEnabled(bool enabled) {
return mEventHub->setKernelWakeEnabled(mId, enabled);
}
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 1403ca2..9212d37 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,10 +21,12 @@
#include <utils/Mutex.h>
#include <memory>
+#include <string>
#include <unordered_map>
#include <vector>
#include "EventHub.h"
+#include "InputDevice.h"
#include "InputListener.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
@@ -116,6 +118,8 @@
std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override;
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override;
+
void sysfsNodeChanged(const std::string& sysfsNodePath) override;
DeviceId getLastUsedInputDeviceId() override;
@@ -127,7 +131,8 @@
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
- const InputDeviceIdentifier& identifier)
+ const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes)
REQUIRES(mLock);
// With each iteration of the loop, InputReader reads and processes one incoming message from
@@ -140,6 +145,7 @@
public:
explicit ContextImpl(InputReader* reader);
+ std::string dump() REQUIRES(mReader->mLock) override;
// lock is already held by the input loop
void updateGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
int32_t getGlobalMetaState() NO_THREAD_SAFETY_ANALYSIS override;
@@ -154,7 +160,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)
@@ -214,6 +220,8 @@
// The input device that produced a new gesture most recently.
DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID};
+ void dumpLocked(std::string& dump) REQUIRES(mLock);
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index e0e0ac2..f38fd7b 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -20,6 +20,7 @@
#include <input/KeyboardClassifier.h>
#include "NotifyArgs.h"
+#include <string>
#include <vector>
namespace android {
@@ -39,6 +40,8 @@
InputReaderContext() {}
virtual ~InputReaderContext() {}
+ virtual std::string dump() = 0;
+
virtual void updateGlobalMetaState() = 0;
virtual int32_t getGlobalMetaState() = 0;
@@ -55,7 +58,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/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index dd46bbc..d796af1 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -25,8 +25,6 @@
#include <linux/input-event-codes.h>
#include <log/log_main.h>
-namespace input_flags = com::android::input::flags;
-
namespace android {
namespace {
@@ -119,15 +117,10 @@
}
void CapturedTouchpadEventConverter::populateMotionRanges(InputDeviceInfo& info) const {
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
- AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
- tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
- AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
- } else {
- tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_X, ABS_MT_POSITION_X);
- tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_Y, ABS_MT_POSITION_Y);
- }
+ tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_X,
+ AMOTION_EVENT_AXIS_RELATIVE_X, ABS_MT_POSITION_X);
+ tryAddRawMotionRangeWithRelative(/*byref*/ info, AMOTION_EVENT_AXIS_Y,
+ AMOTION_EVENT_AXIS_RELATIVE_Y, ABS_MT_POSITION_Y);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOUCH_MINOR, ABS_MT_TOUCH_MINOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
@@ -213,13 +206,11 @@
}
out.push_back(
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, coords, properties));
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- // For any further events we send from this sync, the pointers won't have moved relative
- // to the positions we just reported in this MOVE event, so zero out the relative axes.
- for (PointerCoords& pointer : coords) {
- pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
- pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
- }
+ // For any further events we send from this sync, the pointers won't have moved relative to
+ // the positions we just reported in this MOVE event, so zero out the relative axes.
+ for (PointerCoords& pointer : coords) {
+ pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ pointer.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
}
}
@@ -275,9 +266,7 @@
/*flags=*/cancel ? AMOTION_EVENT_FLAG_CANCELED : 0));
freePointerIdForSlot(slotNumber);
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- mPreviousCoordsForSlotNumber.erase(slotNumber);
- }
+ mPreviousCoordsForSlotNumber.erase(slotNumber);
coords.erase(coords.begin() + indexToRemove);
properties.erase(properties.begin() + indexToRemove);
// Now that we've removed some coords and properties, we might have to update the slot
@@ -336,15 +325,13 @@
coords.clear();
coords.setAxisValue(AMOTION_EVENT_AXIS_X, slot.getX());
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, slot.getY());
- if (input_flags::include_relative_axis_values_for_captured_touchpads()) {
- if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
- it != mPreviousCoordsForSlotNumber.end()) {
- auto [oldX, oldY] = it->second;
- coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
- coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
- }
- mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
+ if (auto it = mPreviousCoordsForSlotNumber.find(slotNumber);
+ it != mPreviousCoordsForSlotNumber.end()) {
+ auto [oldX, oldY] = it->second;
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, slot.getX() - oldX);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, slot.getY() - oldY);
}
+ mPreviousCoordsForSlotNumber[slotNumber] = std::make_pair(slot.getX(), slot.getY());
coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, slot.getTouchMajor());
coords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, slot.getTouchMinor());
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index b33659c..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;
}
@@ -481,15 +481,21 @@
mPointerVelocityControl.setAccelerationEnabled(false);
mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
- } else {
- mPointerVelocityControl.setAccelerationEnabled(
- config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0);
- mPointerVelocityControl.setCurve(
- createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
- mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
- mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
+ return;
}
+
+ bool disableAllScaling = config.displaysWithMouseScalingDisabled.count(
+ mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) != 0;
+
+ mPointerVelocityControl.setAccelerationEnabled(!disableAllScaling);
+
+ mPointerVelocityControl.setCurve(
+ config.mousePointerAccelerationEnabled
+ ? createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)
+ : createFlatAccelerationCurve(config.mousePointerSpeed));
+
+ mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters);
+ mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters);
}
void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfiguration& config) {
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 8319922..f2b2b6f 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.
@@ -115,6 +115,7 @@
ui::Rotation mOrientation{ui::ROTATION_0};
FloatRect mBoundsInLogicalDisplay{};
+ // The button state as of the last sync.
int32_t mButtonState;
nsecs_t mDownTime;
nsecs_t mLastEventTime;
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/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index fd8224a..4d08f19 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -79,19 +79,17 @@
if (id) {
outState->rawPointerData.canceledIdBits.markBit(id.value());
}
- if (DEBUG_POINTERS) {
- ALOGI("Stop processing slot %zu for it received a palm event from device %s",
- inIndex, getDeviceName().c_str());
- }
+ ALOGI_IF(DEBUG_POINTERS,
+ "Stop processing slot %zu for it received a palm event from device %s",
+ inIndex, getDeviceName().c_str());
continue;
}
if (outCount >= MAX_POINTERS) {
- if (DEBUG_POINTERS) {
- ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
- "ignoring the rest.",
- getDeviceName().c_str(), MAX_POINTERS);
- }
+ ALOGD_IF(DEBUG_POINTERS,
+ "MultiTouch device %s emitted more than maximum of %zu pointers; ignoring the "
+ "rest.",
+ getDeviceName().c_str(), MAX_POINTERS);
break; // too many fingers!
}
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 1f6600d..0d1d884 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -235,9 +235,8 @@
// else calculate difference between previous and current MSC_TIMESTAMP
if (mPrevMscTime == 0) {
mHardwareTimestamp = evTime;
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp);
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Initialize hardware timestamp = %" PRId64,
+ mHardwareTimestamp);
} else {
// Calculate the difference between current msc_timestamp and
// previous msc_timestamp, including when msc_timestamp wraps around.
@@ -330,11 +329,10 @@
bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency) {
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
- ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
- maxBatchReportLatency.count());
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS,
+ "Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
+ ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
+ maxBatchReportLatency.count());
if (!setSensorEnabled(sensorType, /*enabled=*/true)) {
return false;
@@ -355,9 +353,7 @@
}
void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str());
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Disable Sensor %s", ftl::enum_string(sensorType).c_str());
if (!setSensorEnabled(sensorType, /*enabled=*/false)) {
return;
@@ -389,15 +385,12 @@
}
nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
- ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
+ ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
if (sensor.lastSampleTimeNs.has_value() &&
timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
- if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str());
- }
+ ALOGD_IF(DEBUG_SENSOR_EVENT_DETAILS, "Sensor %s Skip a sample.",
+ ftl::enum_string(sensorType).c_str());
} else {
// Convert to Android unit
convertFromLinuxToAndroid(values, sensorType);
diff --git a/services/inputflinger/reader/mapper/SlopController.cpp b/services/inputflinger/reader/mapper/SlopController.cpp
index 9ec02a6..d55df51 100644
--- a/services/inputflinger/reader/mapper/SlopController.cpp
+++ b/services/inputflinger/reader/mapper/SlopController.cpp
@@ -54,13 +54,13 @@
mCumulativeValue += value;
if (abs(mCumulativeValue) >= mSlopThreshold) {
- ALOGD("SlopController: did not drop event with value .%3f", value);
+ ALOGD("SlopController: did not drop event with value %.3f", value);
mHasSlopBeenMet = true;
// Return the amount of value that exceeds the slop.
return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
}
- ALOGD("SlopController: dropping event with value .%3f", value);
+ ALOGD("SlopController: dropping event with value %.3f", value);
return 0;
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 5c90cbb..4d36db8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -24,12 +24,15 @@
#include <cinttypes>
#include <cmath>
#include <cstddef>
+#include <sstream>
+#include <string>
#include <tuple>
#include <math.h>
#include <android-base/stringprintf.h>
#include <android/input.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
#include <input/PropertyMap.h>
@@ -47,6 +50,8 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+
// --- Constants ---
// Artificial latency on synthetic events created from stylus data without corresponding touch
@@ -135,6 +140,40 @@
*outY = y;
}
+std::ostream& operator<<(std::ostream& out, const RawPointerData::Pointer& p) {
+ out << "id=" << p.id << ", x=" << p.x << ", y=" << p.y << ", pressure=" << p.pressure
+ << ", touchMajor=" << p.touchMajor << ", touchMinor=" << p.touchMinor
+ << ", toolMajor=" << p.toolMajor << ", toolMinor=" << p.toolMinor
+ << ", orientation=" << p.orientation << ", tiltX=" << p.tiltX << ", tiltY=" << p.tiltY
+ << ", distance=" << p.distance << ", toolType=" << ftl::enum_string(p.toolType)
+ << ", isHovering=" << p.isHovering;
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const RawPointerData& data) {
+ out << data.pointerCount << " pointers:\n";
+ for (uint32_t i = 0; i < data.pointerCount; i++) {
+ out << INDENT << "[" << i << "]: " << data.pointers[i] << std::endl;
+ }
+ out << "ID bits: hovering = 0x" << std::hex << std::setfill('0') << std::setw(8)
+ << data.hoveringIdBits.value << ", touching = 0x" << std::setfill('0') << std::setw(8)
+ << data.touchingIdBits.value << ", canceled = 0x" << std::setfill('0') << std::setw(8)
+ << data.canceledIdBits.value << std::dec;
+ return out;
+}
+
+// --- TouchInputMapper::RawState ---
+
+std::ostream& operator<<(std::ostream& out, const TouchInputMapper::RawState& state) {
+ out << "When: " << state.when << std::endl;
+ out << "Read time: " << state.readTime << std::endl;
+ out << "Button state: 0x" << std::setfill('0') << std::setw(8) << std::hex << state.buttonState
+ << std::dec << std::endl;
+ out << "Raw pointer data:" << std::endl;
+ out << addLinePrefix(streamableToString(state.rawPointerData), INDENT);
+ return out;
+}
+
// --- TouchInputMapper ---
TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext,
@@ -229,20 +268,8 @@
dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale);
dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState);
- dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n",
- mLastRawState.rawPointerData.pointerCount);
- for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) {
- const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i];
- dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
- "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
- "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, "
- "toolType=%s, isHovering=%s\n",
- i, pointer.id, pointer.x, pointer.y, pointer.pressure,
- pointer.touchMajor, pointer.touchMinor, pointer.toolMajor,
- pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY,
- pointer.distance, ftl::enum_string(pointer.toolType).c_str(),
- toString(pointer.isHovering));
- }
+ dump += INDENT3 "Last Raw Touch:\n";
+ dump += addLinePrefix(streamableToString(mLastRawState), INDENT4) + "\n";
dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n",
mLastCookedState.buttonState);
@@ -299,6 +326,8 @@
ConfigurationChanges changes) {
std::list<NotifyArgs> out = InputMapper::reconfigure(when, config, changes);
+ std::optional<ui::LogicalDisplayId> previousDisplayId = getAssociatedDisplayId();
+
mConfig = config;
// Full configuration should happen the first time configure is called and
@@ -347,6 +376,8 @@
}
if (changes.any() && resetNeeded) {
+ // Touches should be aborted using the previous display id, so that the stream is consistent
+ out += abortTouches(when, when, /*policyFlags=*/0, previousDisplayId);
out += reset(when);
// Send reset, unless this is the first time the device has been configured,
@@ -1469,6 +1500,22 @@
last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
next.rawPointerData.canceledIdBits.value);
+ if (debugRawEvents() && last.rawPointerData.pointerCount == 0 &&
+ next.rawPointerData.pointerCount == 1) {
+ // Dump a bunch of info to try to debug b/396796958.
+ // TODO(b/396796958): remove this debug dump.
+ ALOGD("pointerCount went from 0 to 1. last:\n%s",
+ addLinePrefix(streamableToString(last), INDENT).c_str());
+ ALOGD("next:\n%s", addLinePrefix(streamableToString(next), INDENT).c_str());
+ ALOGD("InputReader dump:");
+ // The dump is too long to simply add as a format parameter in one log message, so we have
+ // to split it by line and log them individually.
+ std::istringstream stream(mDeviceContext.getContext()->dump());
+ std::string line;
+ while (std::getline(stream, line, '\n')) {
+ ALOGD(INDENT "%s", line.c_str());
+ }
+ }
if (!next.rawPointerData.touchingIdBits.isEmpty() &&
!next.rawPointerData.hoveringIdBits.isEmpty() &&
@@ -1575,7 +1622,8 @@
mLastCookedState.buttonState, mCurrentCookedState.buttonState);
// Dispatch the touches either directly or by translation through a pointer on screen.
- if (mDeviceMode == DeviceMode::POINTER) {
+ if (!input_flags::disable_touch_input_mapper_pointer_usage() &&
+ mDeviceMode == DeviceMode::POINTER) {
for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
const RawPointerData::Pointer& pointer =
@@ -1613,7 +1661,9 @@
}
out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
- } else {
+ }
+ if (input_flags::disable_touch_input_mapper_pointer_usage() ||
+ mDeviceMode != DeviceMode::POINTER) {
if (!mCurrentMotionAborted) {
out += dispatchButtonRelease(when, readTime, policyFlags);
out += dispatchHoverExit(when, readTime, policyFlags);
@@ -1651,6 +1701,10 @@
mParameters.hasAssociatedDisplay;
}
+ui::LogicalDisplayId TouchInputMapper::resolveDisplayId() const {
+ return getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID);
+};
+
void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) {
if (mDeviceMode == DeviceMode::DIRECT && hasExternalStylus()) {
// If any of the external buttons are already pressed by the touch device, ignore them.
@@ -1922,8 +1976,9 @@
keyEventFlags, keyCode, scanCode, metaState, downTime);
}
-std::list<NotifyArgs> TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime,
- uint32_t policyFlags) {
+std::list<NotifyArgs> TouchInputMapper::abortTouches(
+ nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ std::optional<ui::LogicalDisplayId> currentGestureDisplayId) {
std::list<NotifyArgs> out;
if (mCurrentMotionAborted) {
// Current motion event was already aborted.
@@ -1934,6 +1989,7 @@
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ currentGestureDisplayId.value_or(resolveDisplayId()),
AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedState.cookedPointerData.pointerProperties,
@@ -1988,14 +2044,15 @@
if (!currentIdBits.isEmpty()) {
// No pointer id changes so this is a move event.
// The listener takes care of batching moves so we don't have to deal with that here.
- out.push_back(
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE,
- 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mCurrentCookedState.cookedPointerData.pointerProperties,
- mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits,
- -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime,
- MotionClassification::NONE));
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
+ AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+ AMOTION_EVENT_EDGE_FLAG_NONE,
+ mCurrentCookedState.cookedPointerData.pointerProperties,
+ mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ currentIdBits, -1, mOrientedXPrecision,
+ mOrientedYPrecision, mDownTime,
+ MotionClassification::NONE));
}
} else {
// There may be pointers going up and pointers going down and pointers moving
@@ -2025,7 +2082,7 @@
if (isCanceled) {
ALOGI("Canceling pointer %d for the palm event was detected.", upId);
}
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_POINTER_UP, 0,
isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState,
buttonState, 0,
@@ -2044,7 +2101,7 @@
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded && !moveIdBits.isEmpty()) {
ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -2065,7 +2122,7 @@
}
out.push_back(
- dispatchMotion(when, readTime, policyFlags, mSource,
+ dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState,
0, mCurrentCookedState.cookedPointerData.pointerProperties,
mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -2084,7 +2141,7 @@
(mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
!mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
int32_t metaState = getContext()->getGlobalMetaState();
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
mLastCookedState.buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
@@ -2105,7 +2162,7 @@
!mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
if (!mSentHoverEnter) {
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
mCurrentRawState.buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
@@ -2117,7 +2174,7 @@
mSentHoverEnter = true;
}
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
mCurrentRawState.buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
@@ -2140,7 +2197,7 @@
while (!releasedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
buttonState &= ~actionButton;
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
metaState, buttonState, 0,
mLastCookedState.cookedPointerData.pointerProperties,
@@ -2162,7 +2219,7 @@
while (!pressedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
buttonState |= actionButton;
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState,
buttonState, 0,
mCurrentCookedState.cookedPointerData.pointerProperties,
@@ -2186,7 +2243,7 @@
while (!releasedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
buttonState &= ~actionButton;
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
metaState, buttonState, 0,
mPointerGesture.lastGestureProperties,
@@ -2210,7 +2267,7 @@
while (!pressedButtons.isEmpty()) {
int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
buttonState |= actionButton;
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState,
buttonState, 0, mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
@@ -2251,6 +2308,23 @@
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
+ bool isHovering = in.isHovering;
+
+ // A tool MOUSE pointer is only down/touching when a mouse button is pressed.
+ if (input_flags::disable_touch_input_mapper_pointer_usage() &&
+ in.toolType == ToolType::MOUSE &&
+ !mCurrentRawState.rawPointerData.canceledIdBits.hasBit(in.id)) {
+ if (isPointerDown(mCurrentRawState.buttonState)) {
+ isHovering = false;
+ mCurrentCookedState.cookedPointerData.touchingIdBits.markBit(in.id);
+ mCurrentCookedState.cookedPointerData.hoveringIdBits.clearBit(in.id);
+ } else {
+ isHovering = true;
+ mCurrentCookedState.cookedPointerData.touchingIdBits.clearBit(in.id);
+ mCurrentCookedState.cookedPointerData.hoveringIdBits.markBit(in.id);
+ }
+ }
+
// Size
float touchMajor, touchMinor, toolMajor, toolMinor, size;
switch (mCalibration.sizeCalibration) {
@@ -2340,7 +2414,7 @@
pressure = in.pressure * mPressureScale;
break;
default:
- pressure = in.isHovering ? 0 : 1;
+ pressure = isHovering ? 0 : 1;
break;
}
@@ -2541,7 +2615,7 @@
if (!dispatchedGestureIdBits.isEmpty()) {
if (cancelPreviousGesture) {
const uint32_t cancelFlags = flags | AMOTION_EVENT_FLAG_CANCELED;
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_CANCEL, 0, cancelFlags, metaState,
buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties,
@@ -2568,8 +2642,9 @@
}
const uint32_t id = upGestureIdBits.clearFirstMarkedBit();
out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
- AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState,
- buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ resolveDisplayId(), AMOTION_EVENT_ACTION_POINTER_UP, 0,
+ flags, metaState, buttonState,
+ AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties,
mPointerGesture.lastGestureCoords,
mPointerGesture.lastGestureIdToIndex,
@@ -2583,13 +2658,14 @@
// Send motion events for all pointers that moved.
if (moveNeeded) {
- out.push_back(
- dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0,
- flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
- mPointerGesture.currentGestureProperties,
- mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1,
- 0, 0, mPointerGesture.downTime, classification));
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
+ AMOTION_EVENT_ACTION_MOVE, 0, flags, metaState, buttonState,
+ AMOTION_EVENT_EDGE_FLAG_NONE,
+ mPointerGesture.currentGestureProperties,
+ mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ dispatchedGestureIdBits, -1, 0, 0, mPointerGesture.downTime,
+ classification));
}
// Send motion events for all pointers that went down.
@@ -2604,7 +2680,7 @@
mPointerGesture.downTime = when;
}
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_POINTER_DOWN, 0, flags, metaState,
buttonState, 0, mPointerGesture.currentGestureProperties,
mPointerGesture.currentGestureCoords,
@@ -2622,7 +2698,7 @@
// Send motion events for hover.
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.currentGestureProperties,
@@ -2681,7 +2757,7 @@
if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentRawState.buttonState;
- out.push_back(dispatchMotion(when, readTime, policyFlags, mSource,
+ out.push_back(dispatchMotion(when, readTime, policyFlags, mSource, resolveDisplayId(),
AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
mPointerGesture.lastGestureProperties,
@@ -3475,8 +3551,7 @@
hovering = false;
}
- return dispatchPointerSimple(when, readTime, policyFlags, down, hovering,
- ui::LogicalDisplayId::INVALID);
+ return dispatchPointerSimple(when, readTime, policyFlags, down, hovering, resolveDisplayId());
}
std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
@@ -3639,11 +3714,12 @@
}
NotifyMotionArgs TouchInputMapper::dispatchMotion(
- nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
+ nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
+ ui::LogicalDisplayId displayId, int32_t action, 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;
@@ -3691,13 +3767,13 @@
}
}
- const ui::LogicalDisplayId displayId =
- getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID);
-
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- xCursorPosition = yCursorPosition = 0.f;
+ ALOGW_IF(pointerCount != 1,
+ "Only single pointer events are fully supported in POINTER mode");
+ xCursorPosition = pointerCoords[0].getX();
+ yCursorPosition = pointerCoords[0].getY();
}
const DeviceId deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
@@ -3713,7 +3789,7 @@
std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
out += abortPointerUsage(when, readTime, /*policyFlags=*/0);
- out += abortTouches(when, readTime, /* policyFlags=*/0);
+ out += abortTouches(when, readTime, /* policyFlags=*/0, std::nullopt);
return out;
}
@@ -3966,15 +4042,9 @@
return true;
}
-std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() {
- if (mParameters.hasAssociatedDisplay) {
- if (mDeviceMode == DeviceMode::POINTER) {
- return ui::LogicalDisplayId::INVALID;
- } else {
- return std::make_optional(mViewport.displayId);
- }
- }
- return std::nullopt;
+std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() const {
+ return mParameters.hasAssociatedDisplay ? std::make_optional(mViewport.displayId)
+ : std::nullopt;
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index ef0e02f..45fc6bf 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;
@@ -215,7 +215,7 @@
DISABLED, // input is disabled
DIRECT, // direct mapping (touchscreen)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
- POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
+ POINTER, // pointer mapping (e.g. absolute mouse, drawing tablet)
ftl_last = POINTER
};
@@ -234,8 +234,11 @@
ftl_last = POINTER
};
+ // TouchInputMapper will configure devices with INPUT_PROP_DIRECT as
+ // DeviceType::TOUCH_SCREEN, and will otherwise use DeviceType::POINTER by default.
+ // This can be overridden by IDC files, using the `touch.deviceType` config.
DeviceType deviceType;
- bool hasAssociatedDisplay;
+ bool hasAssociatedDisplay = false;
bool associatedDisplayIsExternal;
bool orientationAware;
@@ -345,6 +348,8 @@
inline void clear() { *this = RawState(); }
};
+ friend std::ostream& operator<<(std::ostream& out, const RawState& state);
+
struct CookedState {
// Cooked pointer sample data.
CookedPointerData cookedPointerData{};
@@ -776,8 +781,9 @@
nsecs_t readTime);
const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
void cookPointerData();
- [[nodiscard]] std::list<NotifyArgs> abortTouches(nsecs_t when, nsecs_t readTime,
- uint32_t policyFlags);
+ [[nodiscard]] std::list<NotifyArgs> abortTouches(
+ nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+ std::optional<ui::LogicalDisplayId> gestureDisplayId);
[[nodiscard]] std::list<NotifyArgs> dispatchPointerUsage(nsecs_t when, nsecs_t readTime,
uint32_t policyFlags,
@@ -833,15 +839,18 @@
// method will take care of setting the index and transmuting the action to DOWN or UP
// it is the first / last pointer to go down / up.
[[nodiscard]] NotifyMotionArgs dispatchMotion(
- nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
- int32_t edgeFlags, const PropertiesArray& properties, const CoordsArray& coords,
+ nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
+ ui::LogicalDisplayId displayId, int32_t action, 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();
+ ui::LogicalDisplayId resolveDisplayId() const;
+
bool isPointInsidePhysicalFrame(int32_t x, int32_t y) const;
const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 0c094e6..63eb357 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -40,6 +40,7 @@
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
#include "gestures/HardwareProperties.h"
+#include "gestures/Logging.h"
#include "gestures/TimerProvider.h"
#include "ui/Rotation.h"
@@ -49,19 +50,12 @@
namespace {
-/**
- * Log details of each gesture output by the gestures library.
- * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
- * restarting the shell)
- */
-const bool DEBUG_TOUCHPAD_GESTURES =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
- ANDROID_LOG_INFO);
-
std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity,
+ bool accelerationEnabled,
size_t propertySize) {
- std::vector<AccelerationCurveSegment> segments =
- createAccelerationCurveForPointerSensitivity(sensitivity);
+ std::vector<AccelerationCurveSegment> segments = accelerationEnabled
+ ? createAccelerationCurveForPointerSensitivity(sensitivity)
+ : createFlatAccelerationCurve(sensitivity);
LOG_ALWAYS_FATAL_IF(propertySize < 4 * segments.size());
std::vector<double> output(propertySize, 0);
@@ -150,28 +144,39 @@
// records it if so.
void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) {
std::scoped_lock lock(mLock);
+ Counters& counters = mCounters[id];
switch (gesture.type) {
case kGestureTypeFling:
if (gesture.details.fling.fling_state == GESTURES_FLING_START) {
// Indicates the end of a two-finger scroll gesture.
- mCounters[id].twoFingerSwipeGestures++;
+ counters.twoFingerSwipeGestures++;
}
break;
case kGestureTypeSwipeLift:
- mCounters[id].threeFingerSwipeGestures++;
+ // The Gestures library occasionally outputs two lift gestures in a row, which can
+ // cause inaccurate metrics reporting. To work around this, deduplicate successive
+ // lift gestures.
+ // TODO(b/404529050): fix the Gestures library, and remove this check.
+ if (counters.lastGestureType != kGestureTypeSwipeLift) {
+ counters.threeFingerSwipeGestures++;
+ }
break;
case kGestureTypeFourFingerSwipeLift:
- mCounters[id].fourFingerSwipeGestures++;
+ // TODO(b/404529050): fix the Gestures library, and remove this check.
+ if (counters.lastGestureType != kGestureTypeFourFingerSwipeLift) {
+ counters.fourFingerSwipeGestures++;
+ }
break;
case kGestureTypePinch:
if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
- mCounters[id].pinchGestures++;
+ counters.pinchGestures++;
}
break;
default:
// We're not interested in any other gestures.
break;
}
+ counters.lastGestureType = gesture.type;
}
private:
@@ -220,6 +225,10 @@
int32_t threeFingerSwipeGestures = 0;
int32_t fourFingerSwipeGestures = 0;
int32_t pinchGestures = 0;
+
+ // Records the last type of gesture received for this device, for deduplication purposes.
+ // TODO(b/404529050): fix the Gestures library and remove this field.
+ GestureType lastGestureType = kGestureTypeContactInitiated;
};
// Metrics are aggregated by device model and version, so if two devices of the same model and
@@ -358,12 +367,14 @@
GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve");
accelCurveProp.setRealValues(
createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
+ config.touchpadAccelerationEnabled,
accelCurveProp.getCount()));
mPropertyProvider.getProperty("Use Custom Touchpad Scroll Accel Curve")
.setBoolValues({true});
GesturesProp scrollCurveProp = mPropertyProvider.getProperty("Scroll Accel Curve");
scrollCurveProp.setRealValues(
createAccelerationCurveForSensitivity(config.touchpadPointerSpeed,
+ config.touchpadAccelerationEnabled,
scrollCurveProp.getCount()));
mPropertyProvider.getProperty("Scroll X Out Scale").setRealValues({1.0});
mPropertyProvider.getProperty("Scroll Y Out Scale").setRealValues({1.0});
@@ -466,7 +477,7 @@
std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs) {
- ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str());
+ ALOGD_IF(debugTouchpadGestures(), "New hardware state: %s", schs.state.String().c_str());
mGestureInterpreter->PushHardwareState(&schs.state);
return processGestures(when, readTime);
}
@@ -477,7 +488,7 @@
}
void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
- ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str());
+ ALOGD_IF(debugTouchpadGestures(), "Gesture ready: %s", gesture->String().c_str());
if (mResettingInterpreter) {
// We already handle tidying up fake fingers etc. in GestureConverter::reset, so we should
// ignore any gestures produced from the interpreter while we're resetting it.
@@ -502,7 +513,7 @@
return out;
}
-std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() const {
return mDisplayId;
}
@@ -510,4 +521,12 @@
return mHardwareProperties;
}
+std::optional<GesturesProp> TouchpadInputMapper::getGesturePropertyForTesting(
+ const std::string& name) {
+ if (!mPropertyProvider.hasProperty(name)) {
+ return std::nullopt;
+ }
+ return mPropertyProvider.getProperty(name);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index a2c4be9..9f53a7b 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -66,10 +66,12 @@
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;
+ std::optional<GesturesProp> getGesturePropertyForTesting(const std::string& name);
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index a3a48ef..264ef6f 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -43,10 +43,8 @@
std::list<NotifyArgs> VibratorInputMapper::vibrate(const VibrationSequence& sequence,
ssize_t repeat, int32_t token) {
- if (DEBUG_VIBRATOR) {
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
- sequence.toString().c_str(), repeat, token);
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d",
+ getDeviceId(), sequence.toString().c_str(), repeat, token);
std::list<NotifyArgs> out;
mVibrating = true;
@@ -63,9 +61,7 @@
}
std::list<NotifyArgs> VibratorInputMapper::cancelVibrate(int32_t token) {
- if (DEBUG_VIBRATOR) {
- ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
std::list<NotifyArgs> out;
if (mVibrating && mToken == token) {
@@ -95,9 +91,7 @@
}
std::list<NotifyArgs> VibratorInputMapper::nextStep() {
- if (DEBUG_VIBRATOR) {
- ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
std::list<NotifyArgs> out;
mIndex += 1;
if (size_t(mIndex) >= mSequence.pattern.size()) {
@@ -111,16 +105,11 @@
const VibrationElement& element = mSequence.pattern[mIndex];
if (element.isOn()) {
- if (DEBUG_VIBRATOR) {
- std::string description = element.toString();
- ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
- description.c_str());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ element.toString().c_str());
getDeviceContext().vibrate(element);
} else {
- if (DEBUG_VIBRATOR) {
- ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -128,17 +117,13 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
- if (DEBUG_VIBRATOR) {
- ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "nextStep: scheduled timeout in %lldms", element.duration.count());
return out;
}
NotifyVibratorStateArgs VibratorInputMapper::stopVibrating() {
mVibrating = false;
- if (DEBUG_VIBRATOR) {
- ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
- }
+ ALOGD_IF(DEBUG_VIBRATOR, "stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
getDeviceContext().cancelVibrate();
// Request InputReader to notify InputManagerService for vibration complete.
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 6bd949a..3255877 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>
@@ -35,9 +39,6 @@
namespace {
-// This will disable the tap to click while the user is typing on a physical keyboard
-const bool ENABLE_TOUCHPAD_PALM_REJECTION = input_flags::enable_touchpad_typing_palm_rejection();
-
// In addition to v1, v2 will also cancel ongoing move gestures while typing and add delay in
// re-enabling the tap to click.
const bool ENABLE_TOUCHPAD_PALM_REJECTION_V2 =
@@ -81,7 +82,6 @@
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
mEnableNoFocusChange(input_flags::enable_touchpad_no_focus_change()),
// We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub
// won't classify a device as a touchpad if they're not present.
@@ -223,8 +223,7 @@
if (!mIsHoverCancelled) {
// handleFling calls hoverMove with zero delta on FLING_TAP_DOWN. Don't enable tap to click
// for this case as subsequent handleButtonsChange may choose to ignore this tap.
- if ((ENABLE_TOUCHPAD_PALM_REJECTION || ENABLE_TOUCHPAD_PALM_REJECTION_V2) &&
- (std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
+ if (std::abs(deltaX) > 0 || std::abs(deltaY) > 0) {
enableTapToClick(when);
}
}
@@ -251,6 +250,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);
@@ -263,7 +274,7 @@
// return early to prevent this tap
return out;
}
- } else if (ENABLE_TOUCHPAD_PALM_REJECTION && mReaderContext.isPreventingTouchpadTaps()) {
+ } else if (mReaderContext.isPreventingTouchpadTaps()) {
enableTapToClick(when);
if (gesture.details.buttons.is_tap) {
// return early to prevent this tap
@@ -313,6 +324,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,
@@ -363,7 +383,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();
@@ -406,7 +426,7 @@
break;
case GESTURES_FLING_TAP_DOWN:
if (mCurrentClassification == MotionClassification::NONE) {
- if (mEnableFlingStop && mFlingMayBeInProgress) {
+ if (mFlingMayBeInProgress) {
// The user has just touched the pad again after ending a two-finger scroll
// motion, which might have started a fling. We want to stop the fling, but
// unfortunately there's currently no API for doing so. Instead, send and
@@ -422,7 +442,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));
@@ -480,7 +500,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;
@@ -568,9 +588,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;
@@ -645,6 +663,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 8d92ead..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,
@@ -104,11 +106,10 @@
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
- const bool mEnableFlingStop;
const bool mEnableNoFocusChange;
bool mEnableSystemGestures{true};
- bool mThreeFingerTapShortcutEnabled;
+ bool mThreeFingerTapShortcutEnabled{false};
std::optional<ui::LogicalDisplayId> mDisplayId;
FloatRect mBoundsInLogicalDisplay{};
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
similarity index 72%
rename from services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
rename to services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
index 26028c5..51905f9 100644
--- a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogcatAdapter.cpp
@@ -22,29 +22,17 @@
#include <log/log.h>
+#include "Logging.h"
#include "include/gestures.h"
extern "C" {
-namespace {
-
-/**
- * Log details of each gesture output by the gestures library.
- * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
- * restarting the shell)
- */
-const bool DEBUG_TOUCHPAD_GESTURES =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
- ANDROID_LOG_INFO);
-
-} // namespace
-
void gestures_log(int verb, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
if (verb == GESTURES_LOG_ERROR) {
LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
- } else if (DEBUG_TOUCHPAD_GESTURES) {
+ } else if (android::debugTouchpadGestures()) {
if (verb == GESTURES_LOG_INFO) {
LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
} else {
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index 6885adb..3e62f36 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -25,12 +25,8 @@
#include <com_android_input_flags.h>
#include <linux/input-event-codes.h>
-namespace input_flags = com::android::input::flags;
-
namespace android {
-const bool REPORT_PALMS_TO_GESTURES_LIBRARY = input_flags::report_palms_to_gestures_library();
-
HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext,
MultiTouchMotionAccumulator& motionAccumulator)
: mDeviceContext(deviceContext),
@@ -81,18 +77,11 @@
}
schs.fingers.clear();
- size_t numPalms = 0;
for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
if (!slot.isInUse()) {
continue;
}
- // Some touchpads continue to report contacts even after they've identified them as palms.
- // We want to exclude these contacts from the HardwareStates.
- if (!REPORT_PALMS_TO_GESTURES_LIBRARY && slot.getToolType() == ToolType::PALM) {
- numPalms++;
- continue;
- }
FingerState& fingerState = schs.fingers.emplace_back();
fingerState = {};
@@ -105,15 +94,13 @@
fingerState.position_x = slot.getX();
fingerState.position_y = slot.getY();
fingerState.tracking_id = slot.getTrackingId();
- if (REPORT_PALMS_TO_GESTURES_LIBRARY) {
- fingerState.tool_type = slot.getToolType() == ToolType::PALM
- ? FingerState::ToolType::kPalm
- : FingerState::ToolType::kFinger;
- }
+ fingerState.tool_type = slot.getToolType() == ToolType::PALM
+ ? FingerState::ToolType::kPalm
+ : FingerState::ToolType::kFinger;
}
schs.state.fingers = schs.fingers.data();
schs.state.finger_cnt = schs.fingers.size();
- schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount() - numPalms;
+ schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
return schs;
}
diff --git a/services/inputflinger/reader/mapper/gestures/Logging.cpp b/services/inputflinger/reader/mapper/gestures/Logging.cpp
new file mode 100644
index 0000000..b9b97c3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/Logging.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "Logging.h"
+
+#include <android-base/properties.h>
+#include <log/log.h>
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+} // namespace
+
+namespace android {
+
+bool debugTouchpadGestures() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_RAW_EVENTS =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+ return DEBUG_RAW_EVENTS;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/gestures/Logging.h b/services/inputflinger/reader/mapper/gestures/Logging.h
new file mode 100644
index 0000000..db59fb3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/Logging.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace android {
+
+/**
+ * Log details of touchpad gesture library input, output, and processing.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
+ */
+bool debugTouchpadGestures();
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 5b7cc2d..78674e5 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -40,14 +40,14 @@
crate_name: "inputflinger",
srcs: ["lib.rs"],
rustlibs: [
- "libcxx",
- "com.android.server.inputflinger-rust",
"android.hardware.input.common-V1-rust",
+ "com.android.server.inputflinger-rust",
"libbinder_rs",
+ "libcxx",
+ "libinput_rust",
"liblog_rust",
"liblogger",
"libnix",
- "libinput_rust",
],
host_supported: true,
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 600ae52..677cf1e 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -40,17 +40,19 @@
// defaults rather than including them as shared or static libraries. By doing so, the tests
// will always run against the compiled version of the inputflinger code rather than the
// version on the device.
+ "libinputdispatcher_defaults",
"libinputflinger_base_defaults",
+ "libinputflinger_defaults",
"libinputreader_defaults",
"libinputreporter_defaults",
- "libinputdispatcher_defaults",
- "libinputflinger_defaults",
],
srcs: [
":inputdispatcher_common_test_sources",
+ "AndroidInputEventProtoConverter_test.cpp",
"AnrTracker_test.cpp",
"CapturedTouchpadEventConverter_test.cpp",
"CursorInputMapper_test.cpp",
+ "DisplayTopologyGraph_test.cpp",
"EventHub_test.cpp",
"FakeEventHub.cpp",
"FakeInputReaderPolicy.cpp",
@@ -62,16 +64,18 @@
"HardwareStateConverter_test.cpp",
"InputDeviceMetricsCollector_test.cpp",
"InputDeviceMetricsSource_test.cpp",
- "InputMapperTest.cpp",
- "InputProcessor_test.cpp",
- "InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
+ "InputMapperTest.cpp",
+ "InputProcessorConverter_test.cpp",
+ "InputProcessor_test.cpp",
"InputReader_test.cpp",
"InputTraceSession.cpp",
"InputTracingTest.cpp",
"InstrumentedInputReader.cpp",
"JoystickInputMapper_test.cpp",
+ "KeyboardInputMapper_test.cpp",
"LatencyTracker_test.cpp",
+ "MultiTouchInputMapper_test.cpp",
"MultiTouchMotionAccumulator_test.cpp",
"NotifyArgs_test.cpp",
"PointerChoreographer_test.cpp",
@@ -82,14 +86,12 @@
"SlopController_test.cpp",
"SwitchInputMapper_test.cpp",
"SyncQueue_test.cpp",
- "TimerProvider_test.cpp",
"TestInputListener.cpp",
+ "TimerProvider_test.cpp",
"TouchpadInputMapper_test.cpp",
- "VibratorInputMapper_test.cpp",
- "MultiTouchInputMapper_test.cpp",
- "KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
+ "VibratorInputMapper_test.cpp",
],
aidl: {
include_dirs: [
@@ -109,7 +111,14 @@
undefined: true,
all_undefined: true,
diag: {
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
undefined: true,
+ misc_undefined: [
+ "all",
+ "bounds",
+ ],
},
},
static_libs: [
@@ -121,8 +130,8 @@
unit_test: true,
},
test_suites: [
- "device-tests",
"device-platinum-tests",
+ "device-tests",
],
native_coverage: false,
}
diff --git a/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp b/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp
new file mode 100644
index 0000000..1fd6cee
--- /dev/null
+++ b/services/inputflinger/tests/AndroidInputEventProtoConverter_test.cpp
@@ -0,0 +1,586 @@
+/*
+ * 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 "../dispatcher/trace/AndroidInputEventProtoConverter.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android::inputdispatcher::trace {
+
+namespace {
+
+using testing::Return, testing::_;
+
+class MockProtoAxisValue {
+public:
+ MOCK_METHOD(void, set_axis, (int32_t));
+ MOCK_METHOD(void, set_value, (float));
+};
+
+class MockProtoPointer {
+public:
+ MOCK_METHOD(void, set_pointer_id, (uint32_t));
+ MOCK_METHOD(void, set_tool_type, (int32_t));
+ MOCK_METHOD(MockProtoAxisValue*, add_axis_value, ());
+};
+
+class MockProtoMotion {
+public:
+ MOCK_METHOD(void, set_event_id, (uint32_t));
+ MOCK_METHOD(void, set_event_time_nanos, (int64_t));
+ MOCK_METHOD(void, set_down_time_nanos, (int64_t));
+ MOCK_METHOD(void, set_source, (uint32_t));
+ MOCK_METHOD(void, set_action, (int32_t));
+ MOCK_METHOD(void, set_device_id, (uint32_t));
+ MOCK_METHOD(void, set_display_id, (uint32_t));
+ MOCK_METHOD(void, set_classification, (int32_t));
+ MOCK_METHOD(void, set_flags, (uint32_t));
+ MOCK_METHOD(void, set_policy_flags, (uint32_t));
+ MOCK_METHOD(void, set_button_state, (uint32_t));
+ MOCK_METHOD(void, set_action_button, (uint32_t));
+ MOCK_METHOD(void, set_cursor_position_x, (float));
+ MOCK_METHOD(void, set_cursor_position_y, (float));
+ MOCK_METHOD(void, set_meta_state, (uint32_t));
+ MOCK_METHOD(void, set_precision_x, (float));
+ MOCK_METHOD(void, set_precision_y, (float));
+ MOCK_METHOD(MockProtoPointer*, add_pointer, ());
+};
+
+class MockProtoKey {
+public:
+ MOCK_METHOD(void, set_event_id, (uint32_t));
+ MOCK_METHOD(void, set_event_time_nanos, (int64_t));
+ MOCK_METHOD(void, set_down_time_nanos, (int64_t));
+ MOCK_METHOD(void, set_source, (uint32_t));
+ MOCK_METHOD(void, set_action, (int32_t));
+ MOCK_METHOD(void, set_device_id, (uint32_t));
+ MOCK_METHOD(void, set_display_id, (uint32_t));
+ MOCK_METHOD(void, set_repeat_count, (uint32_t));
+ MOCK_METHOD(void, set_flags, (uint32_t));
+ MOCK_METHOD(void, set_policy_flags, (uint32_t));
+ MOCK_METHOD(void, set_key_code, (uint32_t));
+ MOCK_METHOD(void, set_scan_code, (uint32_t));
+ MOCK_METHOD(void, set_meta_state, (uint32_t));
+};
+
+class MockProtoDispatchPointer {
+public:
+ MOCK_METHOD(void, set_pointer_id, (uint32_t));
+ MOCK_METHOD(void, set_x_in_display, (float));
+ MOCK_METHOD(void, set_y_in_display, (float));
+ MOCK_METHOD(MockProtoAxisValue*, add_axis_value_in_window, ());
+};
+
+class MockProtoDispatch {
+public:
+ MOCK_METHOD(void, set_event_id, (uint32_t));
+ MOCK_METHOD(void, set_vsync_id, (uint32_t));
+ MOCK_METHOD(void, set_window_id, (uint32_t));
+ MOCK_METHOD(void, set_resolved_flags, (uint32_t));
+ MOCK_METHOD(MockProtoDispatchPointer*, add_dispatched_pointer, ());
+};
+
+using TestProtoConverter =
+ AndroidInputEventProtoConverter<MockProtoMotion, MockProtoKey, MockProtoDispatch,
+ proto::AndroidInputEventConfig::Decoder>;
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent) {
+ TracedMotionEvent event{};
+ event.id = 1;
+ event.eventTime = 2;
+ event.downTime = 3;
+ event.source = AINPUT_SOURCE_MOUSE;
+ event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS;
+ event.deviceId = 4;
+ event.displayId = ui::LogicalDisplayId(5);
+ event.classification = MotionClassification::PINCH;
+ event.flags = 6;
+ event.policyFlags = 7;
+ event.buttonState = 8;
+ event.actionButton = 9;
+ event.xCursorPosition = 10.0f;
+ event.yCursorPosition = 11.0f;
+ event.metaState = 12;
+ event.xPrecision = 13.0f;
+ event.yPrecision = 14.0f;
+ event.pointerProperties.emplace_back(PointerProperties{
+ .id = 15,
+ .toolType = ToolType::MOUSE,
+ });
+ event.pointerProperties.emplace_back(PointerProperties{
+ .id = 16,
+ .toolType = ToolType::FINGER,
+ });
+ event.pointerCoords.emplace_back();
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 17.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 18.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 19.0f);
+ event.pointerCoords.emplace_back();
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 20.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 21.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22.0f);
+
+ testing::StrictMock<MockProtoMotion> proto;
+ testing::StrictMock<MockProtoPointer> pointer1;
+ testing::StrictMock<MockProtoPointer> pointer2;
+ testing::StrictMock<MockProtoAxisValue> axisValue1;
+ testing::StrictMock<MockProtoAxisValue> axisValue2;
+ testing::StrictMock<MockProtoAxisValue> axisValue3;
+ testing::StrictMock<MockProtoAxisValue> axisValue4;
+ testing::StrictMock<MockProtoAxisValue> axisValue5;
+ testing::StrictMock<MockProtoAxisValue> axisValue6;
+
+ EXPECT_CALL(proto, set_event_id(1));
+ EXPECT_CALL(proto, set_event_time_nanos(2));
+ EXPECT_CALL(proto, set_down_time_nanos(3));
+ EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE));
+ EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ EXPECT_CALL(proto, set_device_id(4));
+ EXPECT_CALL(proto, set_display_id(5));
+ EXPECT_CALL(proto, set_classification(AMOTION_EVENT_CLASSIFICATION_PINCH));
+ EXPECT_CALL(proto, set_flags(6));
+ EXPECT_CALL(proto, set_policy_flags(7));
+ EXPECT_CALL(proto, set_button_state(8));
+ EXPECT_CALL(proto, set_action_button(9));
+ EXPECT_CALL(proto, set_cursor_position_x(10.0f));
+ EXPECT_CALL(proto, set_cursor_position_y(11.0f));
+ EXPECT_CALL(proto, set_meta_state(12));
+ EXPECT_CALL(proto, set_precision_x(13.0f));
+ EXPECT_CALL(proto, set_precision_y(14.0f));
+
+ EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2));
+
+ EXPECT_CALL(pointer1, set_pointer_id(15));
+ EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE));
+ EXPECT_CALL(pointer1, add_axis_value())
+ .WillOnce(Return(&axisValue1))
+ .WillOnce(Return(&axisValue2))
+ .WillOnce(Return(&axisValue3));
+ EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X));
+ EXPECT_CALL(axisValue1, set_value(17.0f));
+ EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue2, set_value(18.0f));
+ EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_PRESSURE));
+ EXPECT_CALL(axisValue3, set_value(19.0f));
+
+ EXPECT_CALL(pointer2, set_pointer_id(16));
+ EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER));
+ EXPECT_CALL(pointer2, add_axis_value())
+ .WillOnce(Return(&axisValue4))
+ .WillOnce(Return(&axisValue5))
+ .WillOnce(Return(&axisValue6));
+ EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_X));
+ EXPECT_CALL(axisValue4, set_value(20.0f));
+ EXPECT_CALL(axisValue5, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue5, set_value(21.0f));
+ EXPECT_CALL(axisValue6, set_axis(AMOTION_EVENT_AXIS_PRESSURE));
+ EXPECT_CALL(axisValue6, set_value(22.0f));
+
+ TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent_Redacted) {
+ TracedMotionEvent event{};
+ event.id = 1;
+ event.eventTime = 2;
+ event.downTime = 3;
+ event.source = AINPUT_SOURCE_MOUSE;
+ event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS;
+ event.deviceId = 4;
+ event.displayId = ui::LogicalDisplayId(5);
+ event.classification = MotionClassification::PINCH;
+ event.flags = 6;
+ event.policyFlags = 7;
+ event.buttonState = 8;
+ event.actionButton = 9;
+ event.xCursorPosition = 10.0f;
+ event.yCursorPosition = 11.0f;
+ event.metaState = 12;
+ event.xPrecision = 13.0f;
+ event.yPrecision = 14.0f;
+ event.pointerProperties.emplace_back(PointerProperties{
+ .id = 15,
+ .toolType = ToolType::MOUSE,
+ });
+ event.pointerProperties.emplace_back(PointerProperties{
+ .id = 16,
+ .toolType = ToolType::FINGER,
+ });
+ event.pointerCoords.emplace_back();
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 17.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 18.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 19.0f);
+ event.pointerCoords.emplace_back();
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 20.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 21.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22.0f);
+
+ testing::StrictMock<MockProtoMotion> proto;
+ testing::StrictMock<MockProtoPointer> pointer1;
+ testing::StrictMock<MockProtoPointer> pointer2;
+ testing::StrictMock<MockProtoAxisValue> axisValue1;
+ testing::StrictMock<MockProtoAxisValue> axisValue2;
+ testing::StrictMock<MockProtoAxisValue> axisValue3;
+ testing::StrictMock<MockProtoAxisValue> axisValue4;
+ testing::StrictMock<MockProtoAxisValue> axisValue5;
+ testing::StrictMock<MockProtoAxisValue> axisValue6;
+
+ EXPECT_CALL(proto, set_event_id(1));
+ EXPECT_CALL(proto, set_event_time_nanos(2));
+ EXPECT_CALL(proto, set_down_time_nanos(3));
+ EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE));
+ EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ EXPECT_CALL(proto, set_device_id(4));
+ EXPECT_CALL(proto, set_display_id(5));
+ EXPECT_CALL(proto, set_classification(AMOTION_EVENT_CLASSIFICATION_PINCH));
+ EXPECT_CALL(proto, set_flags(6));
+ EXPECT_CALL(proto, set_policy_flags(7));
+ EXPECT_CALL(proto, set_button_state(8));
+ EXPECT_CALL(proto, set_action_button(9));
+
+ EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2));
+
+ EXPECT_CALL(pointer1, set_pointer_id(15));
+ EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE));
+ EXPECT_CALL(pointer1, add_axis_value())
+ .WillOnce(Return(&axisValue1))
+ .WillOnce(Return(&axisValue2))
+ .WillOnce(Return(&axisValue3));
+ EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X));
+ EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_PRESSURE));
+
+ EXPECT_CALL(pointer2, set_pointer_id(16));
+ EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER));
+ EXPECT_CALL(pointer2, add_axis_value())
+ .WillOnce(Return(&axisValue4))
+ .WillOnce(Return(&axisValue5))
+ .WillOnce(Return(&axisValue6));
+ EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_X));
+ EXPECT_CALL(axisValue5, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue6, set_axis(AMOTION_EVENT_AXIS_PRESSURE));
+
+ // Redacted fields
+ EXPECT_CALL(proto, set_meta_state(_)).Times(0);
+ EXPECT_CALL(proto, set_cursor_position_x(_)).Times(0);
+ EXPECT_CALL(proto, set_cursor_position_y(_)).Times(0);
+ EXPECT_CALL(proto, set_precision_x(_)).Times(0);
+ EXPECT_CALL(proto, set_precision_y(_)).Times(0);
+ EXPECT_CALL(axisValue1, set_value(_)).Times(0);
+ EXPECT_CALL(axisValue2, set_value(_)).Times(0);
+ EXPECT_CALL(axisValue3, set_value(_)).Times(0);
+ EXPECT_CALL(axisValue4, set_value(_)).Times(0);
+ EXPECT_CALL(axisValue5, set_value(_)).Times(0);
+ EXPECT_CALL(axisValue6, set_value(_)).Times(0);
+
+ TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/true);
+}
+
+// Test any special handling for zero values for pointer events.
+TEST(AndroidInputEventProtoConverterTest, ToProtoMotionEvent_ZeroValues) {
+ TracedMotionEvent event{};
+ event.id = 0;
+ event.eventTime = 0;
+ event.downTime = 0;
+ event.source = AINPUT_SOURCE_MOUSE;
+ event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS;
+ event.deviceId = 0;
+ event.displayId = ui::LogicalDisplayId(0);
+ event.classification = {};
+ event.flags = 0;
+ event.policyFlags = 0;
+ event.buttonState = 0;
+ event.actionButton = 0;
+ event.xCursorPosition = 0.0f;
+ event.yCursorPosition = 0.0f;
+ event.metaState = 0;
+ event.xPrecision = 0.0f;
+ event.yPrecision = 0.0f;
+ event.pointerProperties.emplace_back(PointerProperties{
+ .id = 0,
+ .toolType = ToolType::MOUSE,
+ });
+ event.pointerProperties.emplace_back(PointerProperties{
+ .id = 1,
+ .toolType = ToolType::FINGER,
+ });
+ // Zero values for x and y axes are always traced for pointer events.
+ // However, zero values for other axes may not necessarily be traced.
+ event.pointerCoords.emplace_back();
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 0.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 1.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+ event.pointerCoords.emplace_back();
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 0.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 0.0f);
+ event.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+
+ testing::StrictMock<MockProtoMotion> proto;
+ testing::StrictMock<MockProtoPointer> pointer1;
+ testing::StrictMock<MockProtoPointer> pointer2;
+ testing::StrictMock<MockProtoAxisValue> axisValue1;
+ testing::StrictMock<MockProtoAxisValue> axisValue2;
+ testing::StrictMock<MockProtoAxisValue> axisValue3;
+ testing::StrictMock<MockProtoAxisValue> axisValue4;
+
+ EXPECT_CALL(proto, set_event_id(0));
+ EXPECT_CALL(proto, set_event_time_nanos(0));
+ EXPECT_CALL(proto, set_down_time_nanos(0));
+ EXPECT_CALL(proto, set_source(AINPUT_SOURCE_MOUSE));
+ EXPECT_CALL(proto, set_action(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ EXPECT_CALL(proto, set_device_id(0));
+ EXPECT_CALL(proto, set_display_id(0));
+ EXPECT_CALL(proto, set_classification(0));
+ EXPECT_CALL(proto, set_flags(0));
+ EXPECT_CALL(proto, set_policy_flags(0));
+ EXPECT_CALL(proto, set_button_state(0));
+ EXPECT_CALL(proto, set_action_button(0));
+ EXPECT_CALL(proto, set_cursor_position_x(0.0f));
+ EXPECT_CALL(proto, set_cursor_position_y(0.0f));
+ EXPECT_CALL(proto, set_meta_state(0));
+ EXPECT_CALL(proto, set_precision_x(0.0f));
+ EXPECT_CALL(proto, set_precision_y(0.0f));
+
+ EXPECT_CALL(proto, add_pointer()).WillOnce(Return(&pointer1)).WillOnce(Return(&pointer2));
+
+ EXPECT_CALL(pointer1, set_pointer_id(0));
+ EXPECT_CALL(pointer1, set_tool_type(AMOTION_EVENT_TOOL_TYPE_MOUSE));
+ EXPECT_CALL(pointer1, add_axis_value())
+ .WillOnce(Return(&axisValue1))
+ .WillOnce(Return(&axisValue2));
+ EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_X));
+ EXPECT_CALL(axisValue1, set_value(0.0f));
+ EXPECT_CALL(axisValue2, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue2, set_value(1.0f));
+
+ EXPECT_CALL(pointer2, set_pointer_id(1));
+ EXPECT_CALL(pointer2, set_tool_type(AMOTION_EVENT_TOOL_TYPE_FINGER));
+ EXPECT_CALL(pointer2, add_axis_value())
+ .WillOnce(Return(&axisValue3))
+ .WillOnce(Return(&axisValue4));
+ EXPECT_CALL(axisValue3, set_axis(AMOTION_EVENT_AXIS_X));
+ EXPECT_CALL(axisValue3, set_value(0.0f));
+ EXPECT_CALL(axisValue4, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue4, set_value(0.0f));
+
+ TestProtoConverter::toProtoMotionEvent(event, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoKeyEvent) {
+ TracedKeyEvent event{};
+ event.id = 1;
+ event.eventTime = 2;
+ event.downTime = 3;
+ event.source = AINPUT_SOURCE_KEYBOARD;
+ event.action = AKEY_EVENT_ACTION_DOWN;
+ event.deviceId = 4;
+ event.displayId = ui::LogicalDisplayId(5);
+ event.repeatCount = 6;
+ event.flags = 7;
+ event.policyFlags = 8;
+ event.keyCode = 9;
+ event.scanCode = 10;
+ event.metaState = 11;
+
+ testing::StrictMock<MockProtoKey> proto;
+
+ EXPECT_CALL(proto, set_event_id(1));
+ EXPECT_CALL(proto, set_event_time_nanos(2));
+ EXPECT_CALL(proto, set_down_time_nanos(3));
+ EXPECT_CALL(proto, set_source(AINPUT_SOURCE_KEYBOARD));
+ EXPECT_CALL(proto, set_action(AKEY_EVENT_ACTION_DOWN));
+ EXPECT_CALL(proto, set_device_id(4));
+ EXPECT_CALL(proto, set_display_id(5));
+ EXPECT_CALL(proto, set_repeat_count(6));
+ EXPECT_CALL(proto, set_flags(7));
+ EXPECT_CALL(proto, set_policy_flags(8));
+ EXPECT_CALL(proto, set_key_code(9));
+ EXPECT_CALL(proto, set_scan_code(10));
+ EXPECT_CALL(proto, set_meta_state(11));
+
+ TestProtoConverter::toProtoKeyEvent(event, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoKeyEvent_Redacted) {
+ TracedKeyEvent event{};
+ event.id = 1;
+ event.eventTime = 2;
+ event.downTime = 3;
+ event.source = AINPUT_SOURCE_KEYBOARD;
+ event.action = AKEY_EVENT_ACTION_DOWN;
+ event.deviceId = 4;
+ event.displayId = ui::LogicalDisplayId(5);
+ event.repeatCount = 6;
+ event.flags = 7;
+ event.policyFlags = 8;
+ event.keyCode = 9;
+ event.scanCode = 10;
+ event.metaState = 11;
+
+ testing::StrictMock<MockProtoKey> proto;
+
+ EXPECT_CALL(proto, set_event_id(1));
+ EXPECT_CALL(proto, set_event_time_nanos(2));
+ EXPECT_CALL(proto, set_down_time_nanos(3));
+ EXPECT_CALL(proto, set_source(AINPUT_SOURCE_KEYBOARD));
+ EXPECT_CALL(proto, set_action(AKEY_EVENT_ACTION_DOWN));
+ EXPECT_CALL(proto, set_device_id(4));
+ EXPECT_CALL(proto, set_display_id(5));
+ EXPECT_CALL(proto, set_repeat_count(6));
+ EXPECT_CALL(proto, set_flags(7));
+ EXPECT_CALL(proto, set_policy_flags(8));
+
+ // Redacted fields
+ EXPECT_CALL(proto, set_key_code(_)).Times(0);
+ EXPECT_CALL(proto, set_scan_code(_)).Times(0);
+ EXPECT_CALL(proto, set_meta_state(_)).Times(0);
+
+ TestProtoConverter::toProtoKeyEvent(event, proto, /*isRedacted=*/true);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_IdentityTransform) {
+ TracedMotionEvent motion{};
+ motion.pointerProperties.emplace_back(PointerProperties{
+ .id = 4,
+ .toolType = ToolType::MOUSE,
+ });
+ motion.pointerCoords.emplace_back();
+ motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 5.0f);
+ motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f);
+
+ WindowDispatchArgs args{};
+ args.eventEntry = motion;
+ args.vsyncId = 1;
+ args.windowId = 2;
+ args.resolvedFlags = 3;
+ args.rawTransform = ui::Transform{};
+ args.transform = ui::Transform{};
+
+ testing::StrictMock<MockProtoDispatch> proto;
+ testing::StrictMock<MockProtoDispatchPointer> pointer;
+
+ EXPECT_CALL(proto, set_event_id(0));
+ EXPECT_CALL(proto, set_vsync_id(1));
+ EXPECT_CALL(proto, set_window_id(2));
+ EXPECT_CALL(proto, set_resolved_flags(3));
+ EXPECT_CALL(proto, add_dispatched_pointer()).WillOnce(Return(&pointer));
+ EXPECT_CALL(pointer, set_pointer_id(4));
+
+ // Since we are using identity transforms, the axis values will be identical to those in the
+ // traced event, so they should not be traced here.
+ EXPECT_CALL(pointer, add_axis_value_in_window()).Times(0);
+ EXPECT_CALL(pointer, set_x_in_display(_)).Times(0);
+ EXPECT_CALL(pointer, set_y_in_display(_)).Times(0);
+
+ TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_CustomTransform) {
+ TracedMotionEvent motion{};
+ motion.pointerProperties.emplace_back(PointerProperties{
+ .id = 4,
+ .toolType = ToolType::MOUSE,
+ });
+ motion.pointerCoords.emplace_back();
+ motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 8.0f);
+ motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f);
+
+ WindowDispatchArgs args{};
+ args.eventEntry = motion;
+ args.vsyncId = 1;
+ args.windowId = 2;
+ args.resolvedFlags = 3;
+ args.rawTransform.set(2, 0, 0, 0.5);
+ args.transform.set(1.0, 0, 0, 0.5);
+
+ testing::StrictMock<MockProtoDispatch> proto;
+ testing::StrictMock<MockProtoDispatchPointer> pointer;
+ testing::StrictMock<MockProtoAxisValue> axisValue1;
+
+ EXPECT_CALL(proto, set_event_id(0));
+ EXPECT_CALL(proto, set_vsync_id(1));
+ EXPECT_CALL(proto, set_window_id(2));
+ EXPECT_CALL(proto, set_resolved_flags(3));
+ EXPECT_CALL(proto, add_dispatched_pointer()).WillOnce(Return(&pointer));
+ EXPECT_CALL(pointer, set_pointer_id(4));
+
+ // Only the transformed axis-values that differ from the traced event will be traced.
+ EXPECT_CALL(pointer, add_axis_value_in_window()).WillOnce(Return(&axisValue1));
+ EXPECT_CALL(pointer, set_x_in_display(16.0f)); // MotionEvent::getRawX
+ EXPECT_CALL(pointer, set_y_in_display(3.0f)); // MotionEvent::getRawY
+
+ EXPECT_CALL(axisValue1, set_axis(AMOTION_EVENT_AXIS_Y));
+ EXPECT_CALL(axisValue1, set_value(3.0f));
+
+ TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/false);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Motion_Redacted) {
+ TracedMotionEvent motion{};
+ motion.pointerProperties.emplace_back(PointerProperties{
+ .id = 4,
+ .toolType = ToolType::MOUSE,
+ });
+ motion.pointerCoords.emplace_back();
+ motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 5.0f);
+ motion.pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 6.0f);
+
+ WindowDispatchArgs args{};
+ args.eventEntry = motion;
+ args.vsyncId = 1;
+ args.windowId = 2;
+ args.resolvedFlags = 3;
+ args.rawTransform = ui::Transform{};
+ args.transform = ui::Transform{};
+
+ testing::StrictMock<MockProtoDispatch> proto;
+
+ EXPECT_CALL(proto, set_event_id(0));
+ EXPECT_CALL(proto, set_vsync_id(1));
+ EXPECT_CALL(proto, set_window_id(2));
+ EXPECT_CALL(proto, set_resolved_flags(3));
+
+ // Redacted fields
+ EXPECT_CALL(proto, add_dispatched_pointer()).Times(0);
+
+ TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/true);
+}
+
+TEST(AndroidInputEventProtoConverterTest, ToProtoWindowDispatchEvent_Key) {
+ TracedKeyEvent key{};
+
+ WindowDispatchArgs args{};
+ args.eventEntry = key;
+ args.vsyncId = 1;
+ args.windowId = 2;
+ args.resolvedFlags = 3;
+ args.rawTransform = ui::Transform{};
+ args.transform = ui::Transform{};
+
+ testing::StrictMock<MockProtoDispatch> proto;
+
+ EXPECT_CALL(proto, set_event_id(0));
+ EXPECT_CALL(proto, set_vsync_id(1));
+ EXPECT_CALL(proto, set_window_id(2));
+ EXPECT_CALL(proto, set_resolved_flags(3));
+
+ TestProtoConverter::toProtoWindowDispatchEvent(args, proto, /*isRedacted=*/true);
+}
+
+} // namespace
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 353011a..c6246d9 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -33,8 +33,6 @@
#include "TestEventMatchers.h"
#include "TestInputListener.h"
-namespace input_flags = com::android::input::flags;
-
namespace android {
using testing::AllOf;
@@ -50,8 +48,6 @@
mReader(mFakeEventHub, mFakePolicy, mFakeListener),
mDevice(newDevice()),
mDeviceContext(*mDevice, EVENTHUB_ID) {
- input_flags::include_relative_axis_values_for_captured_touchpads(true);
-
const size_t slotCount = 8;
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, slotCount - 1, 0, 0, 0);
mAccumulator.configure(mDeviceContext, slotCount, /*usingSlotsProtocol=*/true);
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index d4e8fdf..594ee3b 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -27,6 +27,7 @@
#include <com_android_input_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <input/AccelerationCurve.h>
#include <input/DisplayViewport.h>
#include <input/InputEventLabels.h>
#include <linux/input-event-codes.h>
@@ -140,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,
@@ -1028,6 +1029,34 @@
WithCoords(0.0f, 0.0f)))));
}
+TEST_F(CursorInputMapperUnitTest, PointerAccelerationDisabled) {
+ mReaderConfiguration.mousePointerAccelerationEnabled = false;
+ mReaderConfiguration.mousePointerSpeed = 3;
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ std::list<NotifyArgs> reconfigureArgs;
+
+ reconfigureArgs += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::POINTER_SPEED);
+
+ std::vector<AccelerationCurveSegment> curve =
+ createFlatAccelerationCurve(mReaderConfiguration.mousePointerSpeed);
+ double baseGain = curve[0].baseGain;
+
+ std::list<NotifyArgs> motionArgs;
+ motionArgs += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ motionArgs += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ motionArgs += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ const float expectedRelX = 10 * baseGain;
+ const float expectedRelY = 20 * baseGain;
+ ASSERT_THAT(motionArgs,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE),
+ WithRelativeMotion(expectedRelX, expectedRelY)))));
+}
+
TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) {
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
@@ -1049,7 +1078,7 @@
ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f);
// Disable acceleration for the display, and verify that acceleration is no longer applied.
- mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
+ mReaderConfiguration.displaysWithMouseScalingDisabled.emplace(DISPLAY_ID);
args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::POINTER_SPEED);
args.clear();
@@ -1068,7 +1097,7 @@
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
// Disable acceleration for the display.
- mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
+ mReaderConfiguration.displaysWithMouseScalingDisabled.emplace(DISPLAY_ID);
// Don't associate the device with the display yet.
EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt));
@@ -1113,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/DisplayTopologyGraph_test.cpp b/services/inputflinger/tests/DisplayTopologyGraph_test.cpp
new file mode 100644
index 0000000..fd2f21c
--- /dev/null
+++ b/services/inputflinger/tests/DisplayTopologyGraph_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <input/DisplayTopologyGraph.h>
+
+#include <string>
+#include <string_view>
+#include <tuple>
+
+namespace android {
+
+namespace {
+
+constexpr ui::LogicalDisplayId DISPLAY_ID_1{1};
+constexpr ui::LogicalDisplayId DISPLAY_ID_2{2};
+constexpr int DENSITY_MEDIUM = 160;
+
+} // namespace
+
+using DisplayTopologyGraphTestFixtureParam =
+ std::tuple<std::string_view /*name*/, DisplayTopologyGraph, bool /*isValid*/>;
+
+class DisplayTopologyGraphTestFixture
+ : public testing::Test,
+ public testing::WithParamInterface<DisplayTopologyGraphTestFixtureParam> {};
+
+TEST_P(DisplayTopologyGraphTestFixture, DisplayTopologyGraphTest) {
+ const auto& [_, displayTopology, isValid] = GetParam();
+ EXPECT_EQ(isValid, displayTopology.isValid());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ DisplayTopologyGraphTest, DisplayTopologyGraphTestFixture,
+ testing::Values(
+ std::make_tuple(
+ "InvalidPrimaryDisplay",
+ DisplayTopologyGraph{.primaryDisplayId = ui::LogicalDisplayId::INVALID,
+ .graph = {},
+ .displaysDensity = {}},
+ false),
+ std::make_tuple("PrimaryDisplayNotInGraph",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {},
+ .displaysDensity = {}},
+ false),
+ std::make_tuple("DisplayDensityMissing",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {{DISPLAY_ID_1, {}}},
+ .displaysDensity = {}},
+ false),
+ std::make_tuple("ValidSingleDisplayTopology",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {{DISPLAY_ID_1, {}}},
+ .displaysDensity = {{DISPLAY_ID_1,
+ DENSITY_MEDIUM}}},
+ true),
+ std::make_tuple(
+ "MissingReverseEdge",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {{DISPLAY_ID_1,
+ {{DISPLAY_ID_2,
+ DisplayTopologyPosition::TOP, 0}}}},
+ .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+ {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+ false),
+ std::make_tuple(
+ "IncorrectReverseEdgeDirection",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {{DISPLAY_ID_1,
+ {{DISPLAY_ID_2,
+ DisplayTopologyPosition::TOP, 0}}},
+ {DISPLAY_ID_2,
+ {{DISPLAY_ID_1,
+ DisplayTopologyPosition::TOP, 0}}}},
+ .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+ {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+ false),
+ std::make_tuple(
+ "IncorrectReverseEdgeOffset",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {{DISPLAY_ID_1,
+ {{DISPLAY_ID_2,
+ DisplayTopologyPosition::TOP, 10}}},
+ {DISPLAY_ID_2,
+ {{DISPLAY_ID_1,
+ DisplayTopologyPosition::BOTTOM, 20}}}},
+ .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+ {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+ false),
+ std::make_tuple(
+ "ValidMultiDisplayTopology",
+ DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1,
+ .graph = {{DISPLAY_ID_1,
+ {{DISPLAY_ID_2,
+ DisplayTopologyPosition::TOP, 10}}},
+ {DISPLAY_ID_2,
+ {{DISPLAY_ID_1,
+ DisplayTopologyPosition::BOTTOM, -10}}}},
+ .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM},
+ {DISPLAY_ID_2, DENSITY_MEDIUM}}},
+ true)),
+ [](const testing::TestParamInfo<DisplayTopologyGraphTestFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index e72c440..8987b99 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -627,6 +627,14 @@
device->sysfsRootPath = sysfsRootPath;
}
+std::filesystem::path FakeEventHub::getSysfsRootPath(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ if (device == nullptr) {
+ return {};
+ }
+ return device->sysfsRootPath;
+}
+
void FakeEventHub::sysfsNodeChanged(const std::string& sysfsNodePath) {
int32_t foundDeviceId = -1;
Device* foundDevice = nullptr;
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 143b93b..1cd33c1 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -222,6 +222,7 @@
std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override;
std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities(
int32_t deviceId, int32_t lightId) const override;
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override;
void sysfsNodeChanged(const std::string& sysfsNodePath) override;
void dump(std::string&) const override {}
void monitor() const override {}
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
index db68d8a..dcb148f 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -16,6 +16,8 @@
#include "FakeInputDispatcherPolicy.h"
+#include <variant>
+
#include <gtest/gtest.h>
namespace android {
@@ -196,10 +198,6 @@
ASSERT_EQ(token, *receivedToken);
}
-void FakeInputDispatcherPolicy::setInterceptKeyTimeout(std::chrono::milliseconds timeout) {
- mInterceptKeyTimeout = timeout;
-}
-
std::chrono::nanoseconds FakeInputDispatcherPolicy::getKeyWaitingForEventsTimeout() {
return 500ms;
}
@@ -208,8 +206,9 @@
mStaleEventTimeout = timeout;
}
-void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) {
- mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching;
+void FakeInputDispatcherPolicy::setInterceptKeyBeforeDispatchingResult(
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result) {
+ mInterceptKeyBeforeDispatchingResult = result;
}
void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) {
@@ -402,21 +401,32 @@
void FakeInputDispatcherPolicy::interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) {
if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
// Clear intercept state when we handled the event.
- mInterceptKeyTimeout = 0ms;
+ if (std::holds_alternative<nsecs_t>(mInterceptKeyBeforeDispatchingResult)) {
+ mInterceptKeyBeforeDispatchingResult = nsecs_t(0);
+ }
}
}
void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(ui::LogicalDisplayId, uint32_t,
int32_t, nsecs_t, uint32_t&) {}
-nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&,
- const KeyEvent&, uint32_t) {
- if (mConsumeKeyBeforeDispatching) {
- return -1;
+std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&,
+ uint32_t) {
+ if (std::holds_alternative<inputdispatcher::KeyEntry::InterceptKeyResult>(
+ mInterceptKeyBeforeDispatchingResult)) {
+ return mInterceptKeyBeforeDispatchingResult;
}
- nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
+
+ nsecs_t delay =
+ std::chrono::nanoseconds(std::get<nsecs_t>(mInterceptKeyBeforeDispatchingResult))
+ .count();
+ if (delay == 0) {
+ return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE;
+ }
+
// Clear intercept state so we could dispatch the event in next wake.
- mInterceptKeyTimeout = 0ms;
+ mInterceptKeyBeforeDispatchingResult = nsecs_t(0);
return delay;
}
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index a9e39d1..b151686 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -28,11 +28,13 @@
#include <optional>
#include <queue>
#include <string>
+#include <variant>
#include <vector>
#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
#include <binder/IBinder.h>
+#include <dispatcher/Entry.h>
#include <gui/PidUid.h>
#include <gui/WindowInfo.h>
#include <input/BlockingQueue.h>
@@ -94,10 +96,6 @@
void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
const sp<IBinder>& targetToken);
void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token);
- /**
- * Set policy timeout. A value of zero means next key will not be intercepted.
- */
- void setInterceptKeyTimeout(std::chrono::milliseconds timeout);
std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override;
void setStaleEventTimeout(std::chrono::nanoseconds timeout);
void assertUserActivityNotPoked();
@@ -114,7 +112,12 @@
void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler);
void assertUnhandledKeyReported(int32_t keycode);
void assertUnhandledKeyNotReported();
- void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching);
+ /**
+ * Set policy timeout or the interception result.
+ * A timeout value of zero means next key will not be intercepted.
+ */
+ void setInterceptKeyBeforeDispatchingResult(
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult> result);
void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay);
private:
@@ -143,11 +146,10 @@
std::condition_variable mNotifyUserActivity;
std::queue<UserActivityPokeEvent> mUserActivityPokeEvents;
- std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
-
std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
- bool mConsumeKeyBeforeDispatching = false;
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ mInterceptKeyBeforeDispatchingResult;
BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
@@ -189,7 +191,8 @@
void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override;
void interceptMotionBeforeQueueing(ui::LogicalDisplayId, uint32_t, int32_t, nsecs_t,
uint32_t&) override;
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override;
+ std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+ interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override;
std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
uint32_t) override;
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 67b1e8c..5a14f4b 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -31,6 +31,30 @@
} // namespace
+DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType type) {
+ const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+ return v;
+};
+
void FakeInputReaderPolicy::assertInputDevicesChanged() {
waitForInputDevices(
[](bool devicesChanged) {
@@ -115,33 +139,6 @@
mConfig.setDisplayViewports(mViewports);
}
-void FakeInputReaderPolicy::addDisplayViewport(ui::LogicalDisplayId displayId, int32_t width,
- int32_t height, ui::Rotation orientation,
- bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort,
- ViewportType type) {
- const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- v.isActive = isActive;
- v.uniqueId = uniqueId;
- v.physicalPort = physicalPort;
- v.type = type;
-
- addDisplayViewport(v);
-}
-
bool FakeInputReaderPolicy::updateViewport(const DisplayViewport& viewport) {
size_t count = mViewports.size();
for (size_t i = 0; i < count; i++) {
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 42c9567..9dce31a 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -31,6 +31,10 @@
namespace android {
+DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, bool isActive, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType type);
+
class FakeInputReaderPolicy : public InputReaderPolicyInterface {
protected:
virtual ~FakeInputReaderPolicy() {}
@@ -50,9 +54,6 @@
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const;
void addDisplayViewport(DisplayViewport viewport);
- void addDisplayViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
- ui::Rotation orientation, bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType type);
bool updateViewport(const DisplayViewport& viewport);
void addExcludedDeviceName(const std::string& deviceName);
void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort);
diff --git a/services/inputflinger/tests/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h
index 3a3238a..54dc25a 100644
--- a/services/inputflinger/tests/FakeWindows.h
+++ b/services/inputflinger/tests/FakeWindows.h
@@ -144,10 +144,6 @@
mInfo.setInputConfig(InputConfig::PAUSE_DISPATCHING, paused);
}
- inline void setPreventSplitting(bool preventSplitting) {
- mInfo.setInputConfig(InputConfig::PREVENT_SPLITTING, preventSplitting);
- }
-
inline void setSlippery(bool slippery) {
mInfo.setInputConfig(InputConfig::SLIPPERY, slippery);
}
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index fe40a5e..35310a5 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"
@@ -38,13 +42,15 @@
namespace {
-const auto TOUCHPAD_PALM_REJECTION =
- ACONFIG_FLAG(input_flags, enable_touchpad_typing_palm_rejection);
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 +61,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>();
@@ -1297,7 +1302,6 @@
TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- input_flags::enable_touchpad_fling_stop(true);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -1455,7 +1459,6 @@
}
TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
@@ -1568,8 +1571,7 @@
ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f))));
}
-TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, ClickWithTapToClickDisabled) {
// Click should still produce button press/release events
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1638,8 +1640,7 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
+TEST_F(GestureConverterTest, MoveEnablesTapToClick) {
// initially disable tap-to-click
mReader->getContext()->setPreventingTouchpadTaps(true);
@@ -1699,4 +1700,136 @@
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) {
+ mConverter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+ input_flags::enable_button_state_verification(true);
+ mVerifier = std::make_unique<InputVerifier>("Test verifier");
+ }
+
+ base::Result<void> processMotionArgs(NotifyMotionArgs arg) {
+ return mVerifier->processMovement(arg.deviceId, arg.source, arg.action, arg.actionButton,
+ arg.getPointerCount(), arg.pointerProperties.data(),
+ arg.pointerCoords.data(), arg.flags, arg.buttonState);
+ }
+
+ 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;
+ std::unique_ptr<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/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index 34c81fc..ee125bb 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -33,13 +33,6 @@
namespace android {
-namespace {
-
-const auto REPORT_PALMS =
- ACONFIG_FLAG(com::android::input::flags, report_palms_to_gestures_library);
-
-} // namespace
-
class HardwareStateConverterTest : public testing::Test {
public:
HardwareStateConverterTest()
@@ -201,24 +194,7 @@
EXPECT_EQ(0u, finger2.flags);
}
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmDisableReportPalms,
- REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
-
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
- std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(0, schs->state.touch_cnt);
- EXPECT_EQ(0, schs->state.finger_cnt);
-}
-
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms,
- REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+TEST_F(HardwareStateConverterTest, OnePalm) {
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
@@ -234,54 +210,7 @@
EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type);
}
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmDisableReportPalms,
- REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) {
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100);
-
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
- processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1);
-
- std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(1, schs->state.touch_cnt);
- EXPECT_EQ(1, schs->state.finger_cnt);
-
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99);
-
- schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(0, schs->state.touch_cnt);
- ASSERT_EQ(0, schs->state.finger_cnt);
-
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97);
-
- schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(0, schs->state.touch_cnt);
- EXPECT_EQ(0, schs->state.finger_cnt);
-
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55);
- processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95);
- schs = processSync(ARBITRARY_TIME);
- ASSERT_TRUE(schs.has_value());
- EXPECT_EQ(1, schs->state.touch_cnt);
- ASSERT_EQ(1, schs->state.finger_cnt);
- const FingerState& newFinger = schs->state.fingers[0];
- EXPECT_EQ(123, newFinger.tracking_id);
- EXPECT_NEAR(55, newFinger.position_x, EPSILON);
- EXPECT_NEAR(95, newFinger.position_y, EPSILON);
-}
-
-TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms,
- REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) {
+TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms) {
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 3413caa..298ba42 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,6 +19,7 @@
#include "FakeInputDispatcherPolicy.h"
#include "FakeInputTracingBackend.h"
#include "FakeWindows.h"
+#include "ScopedFlagOverride.h"
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
@@ -36,6 +37,7 @@
#include <input/BlockingQueue.h>
#include <input/Input.h>
#include <input/InputConsumer.h>
+#include <input/KeyCharacterMap.h>
#include <input/PrintTools.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -117,8 +119,12 @@
// An arbitrary pid of the gesture monitor window
static constexpr gui::Pid MONITOR_PID{2001};
+static constexpr int32_t FLAG_WINDOW_IS_OBSCURED = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+static constexpr int32_t FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
static constexpr int EXPECTED_WALLPAPER_FLAGS =
- AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ FLAG_WINDOW_IS_OBSCURED | FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
@@ -134,39 +140,29 @@
return event;
}
-/**
- * Provide a local override for a flag value. The value is restored when the object of this class
- * goes out of scope.
- * This class is not intended to be used directly, because its usage is cumbersome.
- * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided.
- */
-class ScopedFlagOverride {
-public:
- ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value)
- : mInitialValue(read()), mWriteValue(write) {
- mWriteValue(value);
+InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId, DeviceId deviceId) {
+ InputDeviceIdentifier identifier;
+ identifier.vendor = vendorId;
+ identifier.product = productId;
+ auto info = InputDeviceInfo();
+ info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device",
+ /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID);
+ return info;
+}
+
+std::unique_ptr<KeyCharacterMap> loadKeyCharacterMap(const char* name) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ std::string path = getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(),
+ InputDeviceConfigurationFileType::
+ KEY_CHARACTER_MAP);
+
+ if (path.empty()) {
+ return nullptr;
}
- ~ScopedFlagOverride() { mWriteValue(mInitialValue); }
-private:
- const bool mInitialValue;
- std::function<void(bool)> mWriteValue;
-};
-
-typedef bool (*readFlagValueFunction)();
-typedef void (*writeFlagValueFunction)(bool);
-
-/**
- * Use this macro to locally override a flag value.
- * Example usage:
- * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
- * Note: this works by creating a local variable in your current scope. Don't call this twice for
- * the same flag, because the variable names will clash!
- */
-#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \
- readFlagValueFunction read##NAME = com::android::input::flags::NAME; \
- writeFlagValueFunction write##NAME = com::android::input::flags::NAME; \
- ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE))
+ return *KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
+}
} // namespace
@@ -1180,7 +1176,9 @@
// Transfer touch from the middle window to the right window.
ASSERT_TRUE(mDispatcher->transferTouchGesture(middleForegroundWindow->getToken(),
- rightForegroundWindow->getToken()));
+ rightForegroundWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false));
middleForegroundWindow->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
@@ -1211,22 +1209,159 @@
WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
}
-class ShouldSplitTouchFixture : public InputDispatcherTest,
- public ::testing::WithParamInterface<bool> {};
-INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture,
- ::testing::Values(true, false));
+/**
+ * If a window has requested touch to be transferred, only the current pointers should be
+ * transferred.
+ *
+ * Subsequent pointers should still go through the normal hit testing.
+ *
+ * In this test, we are invoking 'transferTouchGesture' with the parameter 'transferEntireGesture'
+ * set to true, but that value doesn't make any difference, since the flag is disabled. This test
+ * will be removed once that flag is fully rolled out.
+ */
+TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture_legacy) {
+ SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, false);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> topWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ sp<FakeWindowHandle> bottomWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Transfer touch from the top window to the bottom window.
+ // The actual value of parameter 'transferEntireGesture' doesn't matter, since the flag is off.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/true));
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // When the second pointer goes down, it will hit the top window, and should be delivered there
+ // as a new pointer.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}};
+ bottomWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers)));
+ bottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has requested touch to be transferred, the current pointers should be transferred.
+ *
+ * If the window did not request the "entire gesture" to be transferred, subsequent pointers should
+ * still go through the normal hit testing.
+ */
+TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture) {
+ SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> topWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ sp<FakeWindowHandle> bottomWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Transfer touch from the top window to the bottom window.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false));
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // When the second pointer goes down, it will hit the top window, and should be delivered there
+ // because "entire gesture" was not requested to be transferred when the original call to
+ // 'transferTouchGesture' was made.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}};
+ bottomWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers)));
+ bottomWindow->assertNoEvents();
+}
+
+/**
+ * If a window has requested touch to be transferred, all subsequent pointers from the same gesture
+ * should be transferred if 'transferEntireGesture' was set to 'true'.
+ *
+ * In this test, there are 2 windows - one above and one below.
+ * First pointer goes to the top window. Then top window calls 'transferTouch' upon receiving
+ * ACTION_DOWN and transfers touch to the bottom window.
+ * Subsequent pointers from the same gesture should still be forwarded to the bottom window,
+ * as long as they first land into the top window.
+ */
+TEST_F(InputDispatcherTest, TouchTransferSendsEntireGesture) {
+ SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> topWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ sp<FakeWindowHandle> bottomWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Transfer touch from the top window to the bottom window.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/true));
+ topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // When the second pointer goes down, it will hit the top window, but since the top window has
+ // requested the whole gesture to be transferred, it should be redirected to the bottom window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60))
+ .build());
+
+ bottomWindow->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+}
+
/**
* A single window that receives touch (on top), and a wallpaper window underneath it.
* The top window gets a multitouch gesture.
* Ensure that wallpaper gets the same gesture.
*/
-TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
+TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> foregroundWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
ui::LogicalDisplayId::DEFAULT);
foregroundWindow->setDupTouchToWallpaper(true);
- foregroundWindow->setPreventSplitting(GetParam());
sp<FakeWindowHandle> wallpaperWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
@@ -1571,6 +1706,60 @@
window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
}
+// Still send inject motion events to window which already be touched.
+TEST_F(InputDispatcherTest, AlwaysDispatchInjectMotionEventWhenAlreadyDownForWindow) {
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window1 =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "window1",
+ ui::LogicalDisplayId::DEFAULT);
+ window1->setFrame(Rect(0, 0, 100, 100));
+ window1->setWatchOutsideTouch(false);
+
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window2 =
+ sp<FakeWindowHandle>::make(application2, mDispatcher, "window2",
+ ui::LogicalDisplayId::DEFAULT);
+ window2->setFrame(Rect(50, 50, 100, 100));
+ window2->setWatchOutsideTouch(true);
+ mDispatcher->onWindowInfosChanged({{*window2->getInfo(), *window1->getInfo()}, {}, 0, 0});
+
+ std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT;
+ InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT;
+ std::optional<gui::Uid> targetUid = {};
+ uint32_t policyFlags = DEFAULT_POLICY_FLAGS;
+
+ const MotionEvent eventDown1 = MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1)
+ .build();
+ injectMotionEvent(*mDispatcher, eventDown1, injectionTimeout, injectionMode, targetUid,
+ policyFlags);
+ window2->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ const MotionEvent eventUp1 = MotionEventBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1)
+ .downTime(eventDown1.getDownTime()).build();
+ // Inject UP event, without the POLICY_FLAG_PASS_TO_USER (to simulate policy behaviour
+ // when screen is off).
+ injectMotionEvent(*mDispatcher, eventUp1, injectionTimeout, injectionMode, targetUid,
+ /*policyFlags=*/0);
+ window2->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ const MotionEvent eventDown2 = MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)).deviceId(-1)
+ .build();
+ injectMotionEvent(*mDispatcher, eventDown2, injectionTimeout, injectionMode, targetUid,
+ policyFlags);
+ window1->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window2->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+
+ const MotionEvent eventUp2 = MotionEventBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60)).deviceId(-1)
+ .downTime(eventDown2.getDownTime()).build();
+ injectMotionEvent(*mDispatcher, eventUp2, injectionTimeout, injectionMode, targetUid,
+ /*policyFlags=*/0);
+ window1->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ window2->assertNoEvents();
+}
+
/**
* Two windows: a window on the left and a window on the right.
* Mouse is hovered from the right window into the left window.
@@ -4256,17 +4445,15 @@
}
/**
- * When events are not split, the downTime should be adjusted such that the downTime corresponds
+ * When events are split, the downTime should be adjusted such that the downTime corresponds
* to the event time of the first ACTION_DOWN. If a new window appears, it should not affect
- * the event routing because the first window prevents splitting.
+ * the event routing unless pointers are delivered to the new window.
*/
TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window1 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
window1->setTouchableRegion(Region{{0, 0, 100, 100}});
- window1->setPreventSplitting(true);
sp<FakeWindowHandle> window2 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window2", DISPLAY_ID);
@@ -4286,13 +4473,18 @@
// Second window is added
mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
- // Now touch down on the window with another pointer
- mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
- .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
- .downTime(downArgs.downTime)
- .build());
- window1->consumeMotionPointerDown(1, AllOf(WithDownTime(downArgs.downTime)));
+ // Now touch down on the new window with another pointer
+ NotifyMotionArgs pointerDownArgs =
+ MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
+ .downTime(downArgs.downTime)
+ .build();
+ mDispatcher->notifyMotion(pointerDownArgs);
+ window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1),
+ WithDownTime(downArgs.downTime)));
+ window2->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(pointerDownArgs.eventTime)));
// Finish the gesture
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -4300,11 +4492,16 @@
.pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
.downTime(downArgs.downTime)
.build());
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime)));
+ window2->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithDownTime(pointerDownArgs.eventTime)));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.downTime(downArgs.downTime)
.build());
- window1->consumeMotionPointerUp(1, AllOf(WithDownTime(downArgs.downTime)));
+
window1->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_UP), WithDownTime(downArgs.downTime)));
window2->assertNoEvents();
@@ -4313,13 +4510,12 @@
/**
* When splitting touch events, the downTime should be adjusted such that the downTime corresponds
* to the event time of the first ACTION_DOWN sent to the new window.
- * If a new window that does not support split appears on the screen and gets touched with the
- * second finger, it should not get any events because it doesn't want split touches. At the same
- * time, the first window should not get the pointer_down event because it supports split touches
- * (and the touch occurred outside of the bounds of window1).
+ * If a new window appears on the screen and gets touched with the
+ * second finger, it should get the new event. At the same
+ * time, the first window should not get the pointer_down event because
+ * the touch occurred outside of its bounds.
*/
-TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, SplitTouchesWhenWindowIsAdded) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window1 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
@@ -4341,16 +4537,16 @@
AllOf(WithMotionAction(ACTION_DOWN), WithDownTime(downArgs.downTime)));
// Second window is added
- window2->setPreventSplitting(true);
mDispatcher->onWindowInfosChanged({{*window1->getInfo(), *window2->getInfo()}, {}, 0, 0});
- // Now touch down on the window with another pointer
+ // Now touch down on the new window with another pointer
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(1, ToolType::FINGER).x(150).y(50))
.downTime(downArgs.downTime)
.build());
- // Event is dropped because window2 doesn't support split touch, and window1 does.
+ window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1)));
+ window2->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Complete the gesture
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -4361,6 +4557,8 @@
// A redundant MOVE event is generated that doesn't carry any new information
window1->consumeMotionEvent(
AllOf(WithMotionAction(ACTION_MOVE), WithDownTime(downArgs.downTime)));
+ window2->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.downTime(downArgs.downTime)
@@ -4580,22 +4778,20 @@
* A spy window sits above a window with NO_INPUT_CHANNEL. Ensure that the spy receives events even
* though the window underneath should not get any events.
*/
-TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowSinglePointer) {
+TEST_F(InputDispatcherTest, SpyAboveNoInputChannelWindowSinglePointer) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(0, 0, 100, 100));
spyWindow->setTrustedOverlay(true);
- spyWindow->setPreventSplitting(true);
spyWindow->setSpy(true);
- // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ // Another window below spy that has NO_INPUT_CHANNEL
sp<FakeWindowHandle> inputSinkWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
ui::LogicalDisplayId::DEFAULT);
inputSinkWindow->setFrame(Rect(0, 0, 100, 100));
inputSinkWindow->setTrustedOverlay(true);
- inputSinkWindow->setPreventSplitting(true);
inputSinkWindow->setNoInputChannel(true);
mDispatcher->onWindowInfosChanged(
@@ -4620,22 +4816,20 @@
* though the window underneath should not get any events.
* Same test as above, but with two pointers touching instead of one.
*/
-TEST_F(InputDispatcherTest, NonSplittableSpyAboveNoInputChannelWindowTwoPointers) {
+TEST_F(InputDispatcherTest, SpyAboveNoInputChannelWindowTwoPointers) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(0, 0, 100, 100));
spyWindow->setTrustedOverlay(true);
- spyWindow->setPreventSplitting(true);
spyWindow->setSpy(true);
- // Another window below spy that would have both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ // Another window below spy that would have NO_INPUT_CHANNEL
sp<FakeWindowHandle> inputSinkWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
ui::LogicalDisplayId::DEFAULT);
inputSinkWindow->setFrame(Rect(0, 0, 100, 100));
inputSinkWindow->setTrustedOverlay(true);
- inputSinkWindow->setPreventSplitting(true);
inputSinkWindow->setNoInputChannel(true);
mDispatcher->onWindowInfosChanged(
@@ -4667,15 +4861,10 @@
inputSinkWindow->assertNoEvents();
}
-/** Check the behaviour for cases where input sink prevents or doesn't prevent splitting. */
-class SpyThatPreventsSplittingWithApplicationFixture : public InputDispatcherTest,
- public ::testing::WithParamInterface<bool> {
-};
-
/**
* Three windows:
* - An application window (app window)
- * - A spy window that does not overlap the app window. Has PREVENT_SPLITTING flag
+ * - A spy window that does not overlap the app window.
* - A window below the spy that has NO_INPUT_CHANNEL (call it 'inputSink')
*
* The spy window is side-by-side with the app window. The inputSink is below the spy.
@@ -4683,34 +4872,31 @@
* Only the SPY window should get the DOWN event.
* The spy pilfers after receiving the first DOWN event.
* Next, we touch the app window.
- * The spy should receive POINTER_DOWN(1) (since spy is preventing splits).
- * Also, since the spy is already pilfering the first pointer, it will be sent the remaining new
- * pointers automatically, as well.
+ * The spy should not receive POINTER_DOWN(1) (since the pointer is outside of the spy).
* Next, the first pointer (from the spy) is lifted.
- * Spy should get POINTER_UP(0).
+ * Spy should get ACTION_UP.
* This event should not go to the app because the app never received this pointer to begin with.
- * Now, lift the remaining pointer and check that the spy receives UP event.
+ * However, due to the current implementation, this would still cause an ACTION_MOVE event in the
+ * app.
+ * Now, lift the remaining pointer and check that the app receives UP event.
*
- * Finally, send a new ACTION_DOWN event to the spy and check that it's received.
+ * Finally, send a new ACTION_DOWN event to the spy and check that it gets received.
* This test attempts to reproduce a crash in the dispatcher.
*/
-TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingWithApplication) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, SpyThatPilfersAfterFirstPointerWithTwoOtherWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(100, 100, 200, 200));
spyWindow->setTrustedOverlay(true);
- spyWindow->setPreventSplitting(true);
spyWindow->setSpy(true);
- // Another window below spy that has both NO_INPUT_CHANNEL and PREVENT_SPLITTING
+ // Another window below spy that has NO_INPUT_CHANNEL
sp<FakeWindowHandle> inputSinkWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Input sink below spy",
ui::LogicalDisplayId::DEFAULT);
inputSinkWindow->setFrame(Rect(100, 100, 200, 200)); // directly below the spy
inputSinkWindow->setTrustedOverlay(true);
- inputSinkWindow->setPreventSplitting(GetParam());
inputSinkWindow->setNoInputChannel(true);
sp<FakeWindowHandle> appWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "App",
@@ -4732,16 +4918,15 @@
mDispatcher->pilferPointers(spyWindow->getToken());
- // Second finger lands in the app, and goes to the spy window. It doesn't go to the app because
- // the spy is already pilfering the first pointer, and this automatically grants the remaining
- // new pointers to the spy, as well.
+ // Second finger lands in the app. It goes to the app as ACTION_DOWN.
mDispatcher->notifyMotion(
MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build());
- spyWindow->consumeMotionPointerDown(1, WithPointerCount(2));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1)));
+ appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Now lift up the first pointer
mDispatcher->notifyMotion(
@@ -4749,14 +4934,15 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build());
- spyWindow->consumeMotionPointerUp(0, WithPointerCount(2));
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ appWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerCount(1)));
// And lift the remaining pointer!
mDispatcher->notifyMotion(
MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build());
- spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1)));
+ appWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithPointerCount(1)));
// Now send a new DOWN, which should again go to spy.
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -4768,10 +4954,6 @@
appWindow->assertNoEvents();
}
-// Behaviour should be the same regardless of whether inputSink supports splitting.
-INSTANTIATE_TEST_SUITE_P(SpyThatPreventsSplittingWithApplication,
- SpyThatPreventsSplittingWithApplicationFixture, testing::Bool());
-
TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -5478,7 +5660,8 @@
generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT);
const std::chrono::milliseconds interceptKeyTimeout = 50ms;
const nsecs_t injectTime = keyArgs.eventTime;
- mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ std::chrono::nanoseconds(interceptKeyTimeout).count());
mDispatcher->notifyKey(keyArgs);
// The dispatching time should be always greater than or equal to intercept key timeout.
window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
@@ -5506,7 +5689,7 @@
// Set a value that's significantly larger than the default consumption timeout. If the
// implementation is correct, the actual value doesn't matter; it won't slow down the test.
- mFakePolicy->setInterceptKeyTimeout(600ms);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(std::chrono::nanoseconds(600ms).count());
mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
// Window should receive key event immediately when same key up.
window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
@@ -5725,14 +5908,12 @@
window->assertNoEvents();
}
-TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, WindowDoesNotReceiveSecondPointerOutsideOfItsBounds) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
ui::LogicalDisplayId::DEFAULT);
- // Ensure window is non-split and have some transform.
- window->setPreventSplitting(true);
+ // Ensure window has a non-trivial transform.
window->setWindowOffset(20, 40);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -5740,7 +5921,10 @@
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithCoords(70, // 50 + 20
+ 90 // 50 + 40
+ )));
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
@@ -5749,45 +5933,32 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50))
.build();
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
- << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
-
- std::unique_ptr<MotionEvent> event = window->consumeMotionEvent();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(POINTER_1_DOWN, event->getAction());
- EXPECT_EQ(70, event->getX(0)); // 50 + 20
- EXPECT_EQ(90, event->getY(0)); // 50 + 40
- EXPECT_EQ(-10, event->getX(1)); // -30 + 20
- EXPECT_EQ(-10, event->getY(1)); // -50 + 40
+ << "Injection should fail because the second finger is outside of any window on the "
+ "screen.";
}
/**
- * Two windows: a splittable and a non-splittable.
- * The non-splittable window shouldn't receive any "incomplete" gestures.
- * Send the first pointer to the splittable window, and then touch the non-splittable window.
- * The second pointer should be dropped because the initial window is splittable, so it won't get
- * any pointers outside of it, and the second window is non-splittable, so it shouldn't get any
- * "incomplete" gestures.
+ * Two windows.
+ * Send the first pointer to the left window, and then touch the right window.
+ * The second pointer should generate an ACTION_DOWN in the right window.
*/
-TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, TwoWindowsTwoPointers) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
ui::LogicalDisplayId::DEFAULT);
- leftWindow->setPreventSplitting(false);
leftWindow->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right non-splittable Window",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
ui::LogicalDisplayId::DEFAULT);
- rightWindow->setPreventSplitting(true);
rightWindow->setFrame(Rect(100, 100, 200, 200));
mDispatcher->onWindowInfosChanged(
{{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
- // Touch down on left, splittable window
+ // Touch down on left window
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.build());
@@ -5798,8 +5969,8 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
.build());
- leftWindow->assertNoEvents();
- rightWindow->assertNoEvents();
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
}
/**
@@ -5822,7 +5993,6 @@
sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
ui::LogicalDisplayId::DEFAULT);
wallpaper->setIsWallpaper(true);
- wallpaper->setPreventSplitting(true);
wallpaper->setTouchable(false);
sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
@@ -5956,7 +6126,6 @@
sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
ui::LogicalDisplayId::DEFAULT);
wallpaper->setIsWallpaper(true);
- wallpaper->setPreventSplitting(true);
wallpaper->setTouchable(false);
sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
@@ -6070,20 +6239,18 @@
}
/**
- * Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
- * down event to the right window. Device B sends a down event to the left window, and then a
- * POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
- * POINTER_DOWN event should only go to the left window, and not to the right window.
+ * Two windows: left and right. Device A sends a DOWN event to the right window. Device B sends a
+ * DOWN event to the left window, and then a POINTER_DOWN event outside of all windows.
+ * The POINTER_DOWN event should be dropped.
* This test attempts to reproduce a crash.
*/
-TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
+TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsTwoPointers) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
- leftWindow->setPreventSplitting(true);
sp<FakeWindowHandle> rightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
@@ -6107,14 +6274,14 @@
.deviceId(deviceB)
.build());
leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
- // Send a second pointer from device B to the right window. It shouldn't go to the right window
- // because the left window prevents splitting.
+
+ // Send a second pointer from device B to an area outside of all windows.
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(deviceB)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
.build());
- leftWindow->consumeMotionPointerDown(1, WithDeviceId(deviceB));
+ // This is dropped because there's no touchable window at the location (120, 120)
// Finish the gesture for both devices
mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -6122,7 +6289,8 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
.build());
- leftWindow->consumeMotionPointerUp(1, WithDeviceId(deviceB));
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithPointerId(0, 0)));
+
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.deviceId(deviceB)
@@ -6501,19 +6669,47 @@
ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}}));
firstWindow->assertNoEvents();
- std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
- ASSERT_NE(nullptr, event);
- EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure that the events from the "getRaw" API are in logical display
+ // coordinates, which has an x-scale of 2 and y-scale of 4.
+ WithRawCoords(300, 880),
+ // Ensure that the x and y values are in the window's coordinate space.
+ // The left-top of the second window is at (100, 200) in display space, which is
+ // (200, 800) in the logical display space. This will be the origin of the window
+ // space.
+ WithCoords(100, 80)));
+}
- // Ensure that the events from the "getRaw" API are in logical display coordinates.
- EXPECT_EQ(300, event->getRawX(0));
- EXPECT_EQ(880, event->getRawY(0));
+TEST_F(InputDispatcherDisplayProjectionTest, UseCloneLayerStackTransformForRawCoordinates) {
+ SCOPED_FLAG_OVERRIDE(use_cloned_screen_coordinates_as_raw, true);
- // Ensure that the x and y values are in the window's coordinate space.
- // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
- // the logical display space. This will be the origin of the window space.
- EXPECT_EQ(100, event->getX(0));
- EXPECT_EQ(80, event->getY(0));
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ const std::array<float, 9> matrix = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0.0, 0.0, 1.0};
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set(matrix);
+ addDisplayInfo(SECOND_DISPLAY_ID, secondDisplayTransform);
+
+ // When a clone layer stack transform is provided for a window, we should use that as the
+ // "display transform" for input going to that window.
+ sp<FakeWindowHandle> secondWindowClone = secondWindow->clone(SECOND_DISPLAY_ID);
+ secondWindowClone->editInfo()->cloneLayerStackTransform = ui::Transform();
+ secondWindowClone->editInfo()->cloneLayerStackTransform->set(0.5, 0, 0, 0.25);
+ addWindow(secondWindowClone);
+
+ // Touch down on the clone window, and ensure its raw coordinates use
+ // the clone layer stack transform.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {PointF{150, 220}}));
+ secondWindowClone->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN),
+ // Ensure the x and y coordinates are in the window's coordinate space.
+ // See previous test case for calculation.
+ WithCoords(100, 80),
+ // Ensure the "getRaw" API uses the clone layer stack transform when it is
+ // provided for the window. It has an x-scale of 0.5 and y-scale of 0.25.
+ WithRawCoords(75, 55)));
}
TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
@@ -6556,7 +6752,8 @@
// The pointer is transferred to the second window, and the second window receives it in the
// correct coordinate space.
- mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken());
+ mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(),
+ /*isDragDrop=*/false, /*transferEntireGesture=*/false);
firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400)));
}
@@ -6949,7 +7146,7 @@
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
-TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
+TEST_P(TransferTouchFixture, TransferTouch_TwoPointers) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
PointF touchPoint = {10, 10};
@@ -6958,11 +7155,9 @@
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
ui::LogicalDisplayId::DEFAULT);
- firstWindow->setPreventSplitting(true);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
ui::LogicalDisplayId::DEFAULT);
- secondWindow->setPreventSplitting(true);
// Add the windows to the dispatcher
mDispatcher->onWindowInfosChanged(
@@ -7094,7 +7289,8 @@
[&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> from,
sp<IBinder> to) {
return dispatcher->transferTouchGesture(from, to,
- /*isDragAndDrop=*/false);
+ /*isDragAndDrop=*/false,
+ /*transferEntireGesture=*/false);
}));
TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
@@ -7134,7 +7330,8 @@
secondWindow->consumeMotionDown();
// Transfer touch to the second window
- mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken());
+ mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(),
+ /*isDragDrop=*/false, /*transferEntireGesture=*/false);
// The first window gets cancel and the new gets pointer down (it already saw down)
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionPointerDown(1, ui::LogicalDisplayId::DEFAULT,
@@ -7268,7 +7465,9 @@
// Transfer touch
ASSERT_TRUE(mDispatcher->transferTouchGesture(firstWindowInPrimary->getToken(),
- secondWindowInPrimary->getToken()));
+ secondWindowInPrimary->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false));
// The first window gets cancel.
firstWindowInPrimary->consumeMotionCancel();
secondWindowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
@@ -7414,7 +7613,8 @@
window->consumeFocusEvent(true);
- mFakePolicy->setConsumeKeyBeforeDispatching(true);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ inputdispatcher::KeyEntry::InterceptKeyResult::SKIP);
mDispatcher->notifyKey(
KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7440,7 +7640,8 @@
window->consumeFocusEvent(true);
- mFakePolicy->setConsumeKeyBeforeDispatching(true);
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ inputdispatcher::KeyEntry::InterceptKeyResult::SKIP);
mDispatcher->notifyKey(
KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7453,6 +7654,50 @@
mFakePolicy->assertUserActivityPoked();
}
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyFallbackKey) {
+#if !defined(__ANDROID__)
+ GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device.";
+#endif
+ InputDeviceInfo testDevice = generateTestDeviceInfo(/*vendorId=*/0,
+ /*productId=*/0, /*deviceId=*/1);
+ std::unique_ptr<KeyCharacterMap> kcm = loadKeyCharacterMap("Generic");
+ ASSERT_NE(nullptr, kcm);
+
+ testDevice.setKeyCharacterMap(std::move(kcm));
+ mDispatcher->notifyInputDevicesChanged(NotifyInputDevicesChangedArgs(/*id=*/1, {testDevice}));
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ window->setFocusable(true);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mFakePolicy->setInterceptKeyBeforeDispatchingResult(
+ inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK);
+
+ // In the Generic KCM fallbacks, Meta + Space => SEARCH.
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .keyCode(AKEYCODE_SPACE)
+ .metaState(AMETA_META_ON)
+ .build());
+ mDispatcher->waitForIdle();
+
+ // Should have poked user activity
+ mFakePolicy->assertUserActivityPoked();
+
+ // Fallback is generated and sent instead.
+ std::unique_ptr<KeyEvent> consumedEvent = window->consumeKey(/*handled=*/false);
+ ASSERT_NE(nullptr, consumedEvent);
+ ASSERT_THAT(*consumedEvent,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_SEARCH),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+}
+
class DisableUserActivityInputDispatcherTest : public InputDispatcherTest,
public ::testing::WithParamInterface<bool> {};
@@ -8077,6 +8322,17 @@
ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
ASSERT_EQ(0, verifiedKey.repeatCount);
+
+ // InputEvent and subclasses don't have a virtual destructor and only
+ // InputEvent's destructor gets called when `verified` goes out of scope,
+ // even if `verifyInputEvent` returns an object of a subclass. To fix this,
+ // we should either consider using std::variant in some way, or introduce an
+ // intermediate POD data structure that we will put the data into just prior
+ // to signing. Adding virtual functions to these classes is undesirable as
+ // the bytes in these objects are getting signed. As a temporary fix, cast
+ // the pointer to the correct class (which is statically known) before
+ // destruction.
+ delete (VerifiedKeyEvent*)verified.release();
}
TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
@@ -8124,6 +8380,10 @@
EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
+
+ // Cast to the correct type before destruction. See explanation at the end
+ // of the VerifyInputEvent_KeyEvent test.
+ delete (VerifiedMotionEvent*)verified.release();
}
/**
@@ -8819,12 +9079,10 @@
}
/**
- * When a device reports a DOWN event, which lands in a window that supports splits, and then the
- * device then reports a POINTER_DOWN, which lands in the location of a non-existing window, then
- * the previous window should receive this event and not be dropped.
+ * First finger lands into a window, and then the second finger lands in the location of a
+ * non-existent window. The second finger should be dropped.
*/
TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) {
- SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
ui::LogicalDisplayId::DEFAULT);
@@ -8842,13 +9100,13 @@
.pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
.build());
- window->consumeMotionEvent(AllOf(WithMotionAction(POINTER_1_DOWN)));
+ window->assertNoEvents();
}
/**
* When deviceA reports a DOWN event, which lands in a window that supports splits, and then deviceB
* also reports a DOWN event, which lands in the location of a non-existing window, then the
- * previous window should receive deviceB's event and it should be dropped.
+ * previous window should not receive deviceB's event and it should be dropped.
*/
TEST_F(InputDispatcherMultiDeviceTest, SecondDeviceDownEventDroppedWithoutWindowTarget) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -9270,6 +9528,24 @@
mWindow->assertNoEvents();
}
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterFocusedWindowChanged) {
+ sp<FakeWindowHandle> anotherWindow =
+ sp<FakeWindowHandle>::make(mApp, mDispatcher, "AnotherWindow",
+ ui::LogicalDisplayId::DEFAULT);
+ anotherWindow->setFocusable(true);
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *anotherWindow->getInfo()}, {}, 0, 0});
+
+ sendAndConsumeKeyDown(/*deviceId=*/1);
+ expectKeyRepeatOnce(/*repeatCount=*/1);
+ expectKeyRepeatOnce(/*repeatCount=*/2);
+ setFocusedWindow(anotherWindow);
+ anotherWindow->consumeFocusEvent(true);
+
+ // Window should receive key up event with cancel.
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT, AKEY_EVENT_FLAG_CANCELED);
+ anotherWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) {
sendAndConsumeKeyDown(/*deviceId=*/1);
expectKeyRepeatOnce(/*repeatCount=*/1);
@@ -9868,6 +10144,15 @@
AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
}
+TEST_F(InputFilterInjectionPolicyTest,
+ MotionEventsInjectedFromAccessibilityTool_HaveAccessibilityFlags) {
+ testInjectedMotion(POLICY_FLAG_FILTERED | POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY |
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL,
+ /*injectedDeviceId=*/3, /*resolvedDeviceId=*/3,
+ AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT |
+ AMOTION_EVENT_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL);
+}
+
TEST_F(InputFilterInjectionPolicyTest, RegularInjectedEvents_ReceiveVirtualDeviceId) {
testInjectedKey(/*policyFlags=*/0, /*injectedDeviceId=*/3,
/*resolvedDeviceId=*/VIRTUAL_KEYBOARD_ID, /*flags=*/0);
@@ -9878,6 +10163,9 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
+ // Use current time as start time otherwise events may be dropped due to being stale.
+ mGestureStartTime = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+
std::shared_ptr<FakeApplicationHandle> application =
std::make_shared<FakeApplicationHandle>();
application->setDispatchingTimeout(100ms);
@@ -9895,18 +10183,23 @@
mWindow->consumeFocusEvent(true);
}
- void notifyAndConsumeMotion(int32_t action, uint32_t source, ui::LogicalDisplayId displayId,
- nsecs_t eventTime) {
- mDispatcher->notifyMotion(MotionArgsBuilder(action, source)
- .displayId(displayId)
- .eventTime(eventTime)
- .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
- .build());
+ NotifyMotionArgs notifyAndConsumeMotion(int32_t action, uint32_t source,
+ ui::LogicalDisplayId displayId,
+ std::chrono::nanoseconds timeDelay) {
+ const NotifyMotionArgs motionArgs =
+ MotionArgsBuilder(action, source)
+ .displayId(displayId)
+ .eventTime((mGestureStartTime + timeDelay).count())
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build();
+ mDispatcher->notifyMotion(motionArgs);
mWindow->consumeMotionEvent(WithMotionAction(action));
+ return motionArgs;
}
private:
sp<FakeWindowHandle> mWindow;
+ std::chrono::nanoseconds mGestureStartTime;
};
TEST_F_WITH_FLAGS(
@@ -9916,55 +10209,55 @@
mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
// First event of type TOUCH. Should poke.
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(50));
+ NotifyMotionArgs motionArgs =
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(50));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
// 80ns > 50ns has passed since previous TOUCH event. Should poke.
- notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(130));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(130));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
// First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event).
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
- ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(135));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(135));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}});
// Within 50ns of previous TOUCH event. Should NOT poke.
notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(140));
+ std::chrono::milliseconds(140));
mFakePolicy->assertUserActivityNotPoked();
// Within 50ns of previous OTHER event. Should NOT poke.
notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
- ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(150));
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(150));
mFakePolicy->assertUserActivityNotPoked();
// Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke.
// Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source.
notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(160));
+ std::chrono::milliseconds(160));
mFakePolicy->assertUserActivityNotPoked();
// 65ns > 50ns has passed since previous OTHER event. Should poke.
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
- ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(200));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_OTHER, ui::LogicalDisplayId::DEFAULT}});
// 170ns > 50ns has passed since previous TOUCH event. Should poke.
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(300));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
+ std::chrono::milliseconds(300));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
// Assert that there's no more user activity poke event.
mFakePolicy->assertUserActivityNotPoked();
@@ -9974,21 +10267,21 @@
InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(200));
+ NotifyMotionArgs motionArgs =
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(280));
+ std::chrono::milliseconds(280));
mFakePolicy->assertUserActivityNotPoked();
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- milliseconds_to_nanoseconds(340));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(340));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
}
TEST_F_WITH_FLAGS(
@@ -9998,11 +10291,11 @@
mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- 20);
+ std::chrono::milliseconds(20));
mFakePolicy->assertUserActivityPoked();
notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- 30);
+ std::chrono::milliseconds(30));
mFakePolicy->assertUserActivityPoked();
}
@@ -12320,6 +12613,11 @@
sp<FakeWindowHandle> mSecondWindow;
sp<FakeWindowHandle> mDragWindow;
sp<FakeWindowHandle> mSpyWindow;
+
+ std::vector<gui::DisplayInfo> mDisplayInfos;
+
+ std::shared_ptr<FakeApplicationHandle> mSecondApplication;
+ sp<FakeWindowHandle> mWindowOnSecondDisplay;
// Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
static constexpr int32_t MOUSE_POINTER_ID = 1;
@@ -12340,85 +12638,142 @@
mSpyWindow->setTrustedOverlay(true);
mSpyWindow->setFrame(Rect(0, 0, 200, 100));
+ mSecondApplication = std::make_shared<FakeApplicationHandle>();
+ mWindowOnSecondDisplay =
+ sp<FakeWindowHandle>::make(mSecondApplication, mDispatcher,
+ "TestWindowOnSecondDisplay", SECOND_DISPLAY_ID);
+ mWindowOnSecondDisplay->setFrame({0, 0, 100, 100});
+
mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
mDispatcher->onWindowInfosChanged(
- {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()},
- {},
+ {{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo(),
+ *mWindowOnSecondDisplay->getInfo()},
+ mDisplayInfos,
0,
0});
}
- void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+ void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) {
+ bool consumeButtonPress = false;
+ const PointF location =
+ displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450);
switch (fromSource) {
- case AINPUT_SOURCE_TOUCHSCREEN:
+ case AINPUT_SOURCE_TOUCHSCREEN: {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
- ui::LogicalDisplayId::DEFAULT, {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId,
+ location))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
break;
- case AINPUT_SOURCE_STYLUS:
+ }
+ case AINPUT_SOURCE_STYLUS: {
+ PointerBuilder pointer =
+ PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_STYLUS)
.buttonState(
AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
- .pointer(PointerBuilder(0, ToolType::STYLUS)
- .x(50)
- .y(50))
+ .pointer(pointer)
.build()));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_STYLUS)
+ .actionButton(
+ AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .buttonState(
+ AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .pointer(pointer)
+ .build()));
+ consumeButtonPress = true;
break;
- case AINPUT_SOURCE_MOUSE:
+ }
+ case AINPUT_SOURCE_MOUSE: {
+ PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
+ .x(location.x)
+ .y(location.y);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
AINPUT_SOURCE_MOUSE)
+ .displayId(displayId)
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
- .pointer(PointerBuilder(MOUSE_POINTER_ID,
- ToolType::MOUSE)
- .x(50)
- .y(50))
+ .pointer(pointer)
.build()));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ AINPUT_SOURCE_MOUSE)
+ .displayId(displayId)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(pointer)
+ .build()));
+ consumeButtonPress = true;
break;
- default:
+ }
+ default: {
FAIL() << "Source " << fromSource << " doesn't support drag and drop";
+ }
}
// Window should receive motion event.
- mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
- // Spy window should also receive motion event
- mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ sp<FakeWindowHandle>& targetWindow =
+ displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay;
+ targetWindow->consumeMotionDown(displayId);
+ if (consumeButtonPress) {
+ targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ }
+
+ // Spy window should also receive motion event if event is on the same display.
+ if (displayId == ui::LogicalDisplayId::DEFAULT) {
+ mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ }
}
// Start performing drag, we will create a drag window and transfer touch to it.
// @param sendDown : if true, send a motion down on first window before perform drag and drop.
// Returns true on success.
- bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
+ bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) {
if (sendDown) {
- injectDown(fromSource);
+ injectDown(fromSource, dragStartDisplay);
}
// The drag window covers the entire display
- mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow",
- ui::LogicalDisplayId::DEFAULT);
+ mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay);
mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
- mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(),
- *mWindow->getInfo(), *mSecondWindow->getInfo()},
- {},
- 0,
- 0});
+ mDispatcher->onWindowInfosChanged(
+ {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(),
+ *mSecondWindow->getInfo(), *mWindowOnSecondDisplay->getInfo()},
+ mDisplayInfos,
+ 0,
+ 0});
+
+ sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT
+ ? mWindow
+ : mWindowOnSecondDisplay;
// Transfer touch focus to the drag window
bool transferred =
- mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(),
- /*isDragDrop=*/true);
+ mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(),
+ /*isDragDrop=*/true,
+ /*transferEntireGesture=*/false);
if (transferred) {
- mWindow->consumeMotionCancel();
- mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ targetWindow->consumeMotionCancel(dragStartDisplay);
+ mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
return transferred;
}
+
+ void addDisplay(ui::LogicalDisplayId displayId, ui::Transform transform) {
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = displayId;
+ displayInfo.transform = transform;
+ mDisplayInfos.push_back(displayInfo);
+ }
};
TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
@@ -12585,6 +12940,16 @@
// Move to another window and release button, expect to drop item.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_STYLUS)
+ .actionButton(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
@@ -12649,9 +13014,6 @@
}
TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
- // Ensure window could track pointerIds if it didn't support split touch.
- mWindow->setPreventSplitting(true);
-
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
ui::LogicalDisplayId::DEFAULT, {50, 50}))
@@ -12829,6 +13191,18 @@
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ AINPUT_SOURCE_MOUSE)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
+ .x(150)
+ .y(50))
+ .build()))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
.buttonState(0)
.pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE)
@@ -13551,14 +13925,10 @@
}
/**
- * The spy window should not be able to affect whether or not touches are split. Only the foreground
- * windows should be allowed to control split touch.
+ * The spy window should not be able to affect whether or not touches are split.
*/
TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) {
- // This spy window prevents touch splitting. However, we still expect to split touches
- // because a foreground window has not disabled splitting.
auto spy = createSpy();
- spy->setPreventSplitting(true);
auto window = createForeground();
window->setFrame(Rect(0, 0, 100, 100));
@@ -14684,4 +15054,640 @@
mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
}
+class InputDispatcherObscuredFlagTest : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApplication;
+ std::shared_ptr<FakeApplicationHandle> mOcclusionApplication;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mOcclusionWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mDispatcher->setMaximumObscuringOpacityForTouch(0.8f);
+ mApplication = std::make_shared<FakeApplicationHandle>();
+ mOcclusionApplication = std::make_shared<FakeApplicationHandle>();
+ mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Window", DISPLAY_ID);
+ mWindow->setOwnerInfo(WINDOW_PID, WINDOW_UID);
+
+ mOcclusionWindow = sp<FakeWindowHandle>::make(mOcclusionApplication, mDispatcher,
+ "Occlusion Window", DISPLAY_ID);
+
+ mOcclusionWindow->setTouchable(false);
+ mOcclusionWindow->setTouchOcclusionMode(TouchOcclusionMode::USE_OPACITY);
+ mOcclusionWindow->setAlpha(0.7f);
+ mOcclusionWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
+ }
+};
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use a finger to touch the bottom window.
+ * When the finger touches down in the obscured area, the motion event should always have the
+ * FLAG_WINDOW_IS_OBSCURED flag, regardless of where it is moved to. If it starts from a
+ * non-obscured area, the motion event should always with a FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag,
+ * regardless of where it is moved to.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, TouchObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 50});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // If the finger touch goes down in the region that is obscured.
+ // Expect the entire stream to use FLAG_WINDOW_IS_OBSCURED.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(10))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithFlags(FLAG_WINDOW_IS_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(60))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE),
+ WithFlags(FLAG_WINDOW_IS_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(110))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP),
+ WithFlags(FLAG_WINDOW_IS_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+}
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use a finger to touch the bottom window.
+ * When the finger starts from a non-obscured area, the motion event should always have the
+ * FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag, regardless of where it is moved to.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, TouchPartiallyObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 50});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // If the finger touch goes down in the region that is not directly obscured by the overlay.
+ // Expect the entire stream to use FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(60))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithFlags(FLAG_WINDOW_IS_PARTIALLY_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(80))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE),
+ WithFlags(FLAG_WINDOW_IS_PARTIALLY_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(110))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP),
+ WithFlags(FLAG_WINDOW_IS_PARTIALLY_OBSCURED),
+ WithDisplayId(DISPLAY_ID)));
+}
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use the mouse to hover over the bottom window.
+ * When the hover happens over the occluded area, the window below should receive a motion
+ * event with the FLAG_WINDOW_IS_OBSCURED flag. When the hover event moves to the non-occluded area,
+ * the window below should receive a motion event with the FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, MouseHoverObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 40});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // TODO(b/328160937): The window should receive a motion event with the FLAG_WINDOW_IS_OBSCURED
+ // flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(20))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(30))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ // TODO(b/328160937): The window should receive a motion event with the
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(110))
+ .build());
+
+ // TODO(b/328160937): The window should receive a HOVER_EXIT with the
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag. The cause of the current issue is that we moved the
+ // mouse to a location where there are no windows, so the HOVER_EXIT event cannot be generated.
+ mWindow->assertNoEvents();
+}
+
+/**
+ * Two windows. An untouchable window partially occludes a touchable region below it.
+ * Use the stylus to hover over the bottom window.
+ * When the hover happens over the occluded area, the window below should receive a motion
+ * event with the FLAG_WINDOW_IS_OBSCURED flag. When the hover event moves to the non-occluded area,
+ * the window below should receive a motion event with the FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ */
+TEST_F(InputDispatcherObscuredFlagTest, StylusHoverObscuredTest) {
+ mWindow->setFrame({0, 0, 100, 100});
+ mOcclusionWindow->setFrame({0, 0, 100, 40});
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mOcclusionWindow->getInfo(), *mWindow->getInfo()}, {}, 0, 0});
+
+ // TODO(b/328160937): The window should receive a motion event with the FLAG_WINDOW_IS_OBSCURED
+ // flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(20))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(30))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ // TODO(b/328160937): The window should receive a motion event with the
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED flag.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithFlags(0)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(50).y(70))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithFlags(0)));
+}
+
+class TransferOrDontTransferFixture : public InputDispatcherTest,
+ public ::testing::WithParamInterface<bool> {
+public:
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ mFromWindow =
+ sp<FakeWindowHandle>::make(app, mDispatcher, "From", ui::LogicalDisplayId::DEFAULT);
+ mToWindow =
+ sp<FakeWindowHandle>::make(app, mDispatcher, "To", ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*mFromWindow->getInfo(), *mToWindow->getInfo()}, {}, 0, 0});
+ }
+
+protected:
+ sp<FakeWindowHandle> mFromWindow;
+ sp<FakeWindowHandle> mToWindow;
+};
+
+// Start a touch gesture and then continue hovering the mouse at the same time.
+// After the mouse is hovering, invoke transferTouch API. Check the events that
+// are received by each of the windows.
+TEST_P(TransferOrDontTransferFixture, TouchDownAndMouseHover) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+
+ const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const int32_t mouseDeviceId = 6;
+ const int32_t touchDeviceId = 4;
+
+ // Send touch down to the first window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 10)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Send touch move to the first window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 20)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(100))
+ .build());
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Start mouse hover on the first window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .downTime(baseTime + 30)
+ .eventTime(baseTime + 30)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(200).y(200))
+ .build());
+
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ if (GetParam()) {
+ // Call transferTouchGesture
+ const bool transferred =
+ mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false);
+ ASSERT_TRUE(transferred);
+
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ // b/382473355: For some reason, mToWindow also receives HOVER_EXIT first
+ mToWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Further touch events should be delivered to mTowindow (?)
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 40)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 50)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+ .build());
+ // b/382473355: Even though the window got ACTION_DOWN, it's no longer receiving the
+ // remainder of the touch gesture.
+
+ mFromWindow->assertNoEvents();
+ mToWindow->assertNoEvents();
+ } else {
+ // Don't call transferTouchGesture
+
+ // Further touch events should be dropped
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 40)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+ .build());
+ mFromWindow->assertNoEvents();
+ mToWindow->assertNoEvents();
+ }
+}
+
+TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevice) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+
+ const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const int32_t mouseDeviceId = 6;
+ const int32_t touchDeviceId = 4;
+
+ // Send touch down to the 'From' window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 10)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Send touch move to the 'From' window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 20)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(110).y(100))
+ .build());
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Start mouse hover on the 'From' window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .downTime(baseTime + 30)
+ .eventTime(baseTime + 30)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(200).y(200))
+ .build());
+
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ if (GetParam()) {
+ // Call transferTouchGesture
+ const bool transferred =
+ mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(),
+ /*isDragDrop=*/false,
+ /*transferEntireGesture=*/false);
+ ASSERT_TRUE(transferred);
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Further touch events should be delivered to mToWindow
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 40)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 50)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+ .build());
+ // b/382473355: Even though the window got ACTION_DOWN, it's receiving another DOWN!
+ mToWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ mToWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ mToWindow->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ mFromWindow->assertNoEvents();
+ mToWindow->assertNoEvents();
+ } else {
+ // Don't call transferTouchGesture
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 40)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(120).y(100))
+ .build());
+ mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ mFromWindow->assertNoEvents();
+ mToWindow->assertNoEvents();
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool());
+
+class InputDispatcherConnectedDisplayTest : public InputDispatcherDragTests {
+ constexpr static int DENSITY_MEDIUM = 160;
+
+ const DisplayTopologyGraph
+ mTopology{.primaryDisplayId = DISPLAY_ID,
+ .graph = {{DISPLAY_ID,
+ {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}},
+ {SECOND_DISPLAY_ID,
+ {{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}},
+ .displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM},
+ {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}};
+
+protected:
+ void SetUp() override {
+ addDisplay(DISPLAY_ID, ui::Transform());
+ addDisplay(SECOND_DISPLAY_ID,
+ ui::Transform(ui::Transform::ROT_270, /*logicalDisplayWidth=*/
+ 500, /*logicalDisplayHeight=*/500));
+
+ InputDispatcherDragTests::SetUp();
+
+ mDispatcher->setDisplayTopology(mTopology);
+ }
+};
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ // pointer-down
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+ .build());
+ mWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+ // pointer-move
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
+
+ // pointer-move with different display
+ // TODO (b/383092013): by default windows will not be topology aware and receive events as it
+ // was in the same display. This behaviour has not been implemented yet.
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+ .build());
+ // events should be delivered with the second displayId and in corrosponding coordinate space
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430)));
+
+ // pointer-up
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(0)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 430)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(0)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
+ .build());
+ mWindow->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE));
+ // Move on window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->assertNoEvents();
+
+ // Move to another window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(150).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+ mWindowOnSecondDisplay->assertNoEvents();
+
+ // Move to window on the second display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->assertNoEvents();
+ mSecondWindow->consumeDragEvent(true, -50, 50);
+ mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50);
+
+ // drop on the second display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionUp(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindowOnSecondDisplay->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->assertNoEvents();
+}
+
+TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID));
+ // Move on window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50);
+
+ // Move to window on the primary display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50);
+
+ // drop on the primary display
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .displayId(DISPLAY_ID)
+ .buttonState(0)
+ .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50))
+ .build());
+ mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+ mWindowOnSecondDisplay->assertNoEvents();
+}
+
+using InputDispatcherConnectedDisplayPointerInWindowTest = InputDispatcherConnectedDisplayTest;
+
+TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .build());
+
+ mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ mWindowOnSecondDisplay->assertNoEvents();
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(),
+ SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0));
+}
+
+TEST_F(InputDispatcherConnectedDisplayPointerInWindowTest, MouseOnWindowOnNonPrimaryDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .displayId(SECOND_DISPLAY_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .build());
+
+ mWindow->assertNoEvents();
+ mSpyWindow->assertNoEvents();
+ mWindowOnSecondDisplay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(mWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(mSpyWindow->getToken(), DISPLAY_ID, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(mWindowOnSecondDisplay->getToken(),
+ SECOND_DISPLAY_ID, DEVICE_ID, /*pointerId=*/0));
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 8235c90..652a658 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);
}
@@ -185,8 +186,10 @@
const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
- uniqueId, physicalPort, viewportType);
+ DisplayViewport viewport =
+ createViewport(displayId, width, height, orientation, /* isActive= */ true, uniqueId,
+ physicalPort, viewportType);
+ mFakePolicy->addDisplayViewport(viewport);
configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
}
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 9d2256f..d1d8192 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -28,6 +28,7 @@
#include <MultiTouchInputMapper.h>
#include <NotifyArgsBuilders.h>
#include <PeripheralController.h>
+#include <ScopedFlagOverride.h>
#include <SingleTouchInputMapper.h>
#include <TestEventMatchers.h>
#include <TestInputListener.h>
@@ -384,30 +385,29 @@
static const std::string uniqueId = "local:0";
// We didn't add any viewports yet, so there shouldn't be any.
- std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
- ASSERT_FALSE(internalViewport);
+ ASSERT_FALSE(mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL));
// Add an internal viewport, then clear it
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, uniqueId, NO_PORT, ViewportType::INTERNAL);
-
+ DisplayViewport internalViewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, uniqueId, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(internalViewport);
// Check matching by uniqueId
- internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
- ASSERT_TRUE(internalViewport);
- ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type);
+ std::optional<DisplayViewport> receivedInternalViewport =
+ mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
+ ASSERT_TRUE(receivedInternalViewport.has_value());
+ ASSERT_EQ(internalViewport, *receivedInternalViewport);
// Check matching by viewport type
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
- ASSERT_TRUE(internalViewport);
- ASSERT_EQ(uniqueId, internalViewport->uniqueId);
+ receivedInternalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(receivedInternalViewport.has_value());
+ ASSERT_EQ(internalViewport, *receivedInternalViewport);
mFakePolicy->clearViewports();
+
// Make sure nothing is found after clear
- internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
- ASSERT_FALSE(internalViewport);
- internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
- ASSERT_FALSE(internalViewport);
+ ASSERT_FALSE(mFakePolicy->getDisplayViewportByUniqueId(uniqueId));
+ ASSERT_FALSE(mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL));
}
TEST_F(InputReaderPolicyTest, Viewports_GetByType) {
@@ -419,49 +419,49 @@
constexpr ui::LogicalDisplayId virtualDisplayId2 = ui::LogicalDisplayId{3};
// Add an internal viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, internalUniqueId, NO_PORT,
- ViewportType::INTERNAL);
+ DisplayViewport internalViewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, internalUniqueId, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(internalViewport);
// Add an external viewport
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, externalUniqueId, NO_PORT,
- ViewportType::EXTERNAL);
+ DisplayViewport externalViewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, externalUniqueId, NO_PORT, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(externalViewport);
// Add an virtual viewport
- mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, virtualUniqueId1, NO_PORT,
- ViewportType::VIRTUAL);
+ DisplayViewport virtualViewport1 =
+ createViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, virtualUniqueId1, NO_PORT, ViewportType::VIRTUAL);
+ mFakePolicy->addDisplayViewport(virtualViewport1);
// Add another virtual viewport
- mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, virtualUniqueId2, NO_PORT,
- ViewportType::VIRTUAL);
+ DisplayViewport virtualViewport2 =
+ createViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, virtualUniqueId2, NO_PORT, ViewportType::VIRTUAL);
+ mFakePolicy->addDisplayViewport(virtualViewport2);
// Check matching by type for internal
- std::optional<DisplayViewport> internalViewport =
+ std::optional<DisplayViewport> receivedInternalViewport =
mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
- ASSERT_TRUE(internalViewport);
- ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
+ ASSERT_TRUE(receivedInternalViewport.has_value());
+ ASSERT_EQ(internalViewport, *receivedInternalViewport);
// Check matching by type for external
- std::optional<DisplayViewport> externalViewport =
+ std::optional<DisplayViewport> receivedExternalViewport =
mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL);
- ASSERT_TRUE(externalViewport);
- ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
+ ASSERT_TRUE(receivedExternalViewport.has_value());
+ ASSERT_EQ(externalViewport, *receivedExternalViewport);
// Check matching by uniqueId for virtual viewport #1
- std::optional<DisplayViewport> virtualViewport1 =
+ std::optional<DisplayViewport> receivedVirtualViewport1 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
- ASSERT_TRUE(virtualViewport1);
- ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type);
- ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
- ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
+ ASSERT_TRUE(receivedVirtualViewport1.has_value());
+ ASSERT_EQ(virtualViewport1, *receivedVirtualViewport1);
// Check matching by uniqueId for virtual viewport #2
- std::optional<DisplayViewport> virtualViewport2 =
+ std::optional<DisplayViewport> receivedVirtualViewport2 =
mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
- ASSERT_TRUE(virtualViewport2);
- ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type);
- ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
- ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
+ ASSERT_TRUE(receivedVirtualViewport2.has_value());
+ ASSERT_EQ(virtualViewport2, *receivedVirtualViewport2);
}
@@ -481,24 +481,26 @@
for (const ViewportType& type : types) {
mFakePolicy->clearViewports();
// Add a viewport
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, uniqueId1, NO_PORT, type);
+ DisplayViewport viewport1 =
+ createViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, uniqueId1, NO_PORT, type);
+ mFakePolicy->addDisplayViewport(viewport1);
// Add another viewport
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, uniqueId2, NO_PORT, type);
+ DisplayViewport viewport2 =
+ createViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, uniqueId2, NO_PORT, type);
+ mFakePolicy->addDisplayViewport(viewport2);
// Check that correct display viewport was returned by comparing the display IDs.
- std::optional<DisplayViewport> viewport1 =
+ std::optional<DisplayViewport> receivedViewport1 =
mFakePolicy->getDisplayViewportByUniqueId(uniqueId1);
- ASSERT_TRUE(viewport1);
- ASSERT_EQ(displayId1, viewport1->displayId);
- ASSERT_EQ(type, viewport1->type);
+ ASSERT_TRUE(receivedViewport1.has_value());
+ ASSERT_EQ(viewport1, *receivedViewport1);
- std::optional<DisplayViewport> viewport2 =
+ std::optional<DisplayViewport> receivedViewport2 =
mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
- ASSERT_TRUE(viewport2);
- ASSERT_EQ(displayId2, viewport2->displayId);
- ASSERT_EQ(type, viewport2->type);
+ ASSERT_TRUE(receivedViewport2.has_value());
+ ASSERT_EQ(viewport2, *receivedViewport2);
// When there are multiple viewports of the same kind, and uniqueId is not specified
// in the call to getDisplayViewport, then that situation is not supported.
@@ -524,32 +526,27 @@
// Add the default display first and ensure it gets returned.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(ui::LogicalDisplayId::DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, uniqueId1, NO_PORT,
- ViewportType::INTERNAL);
- mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, uniqueId2, NO_PORT,
- ViewportType::INTERNAL);
-
- std::optional<DisplayViewport> viewport =
+ DisplayViewport viewport1 = createViewport(ui::LogicalDisplayId::DEFAULT, DISPLAY_WIDTH,
+ DISPLAY_HEIGHT, ui::ROTATION_0, /*isActive=*/true,
+ uniqueId1, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport1);
+ DisplayViewport viewport2 =
+ createViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, uniqueId2, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport2);
+ std::optional<DisplayViewport> receivedViewport =
mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
- ASSERT_TRUE(viewport);
- ASSERT_EQ(ui::LogicalDisplayId::DEFAULT, viewport->displayId);
- ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+ ASSERT_TRUE(receivedViewport.has_value());
+ ASSERT_EQ(viewport1, *receivedViewport);
// Add the default display second to make sure order doesn't matter.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, uniqueId2, NO_PORT,
- ViewportType::INTERNAL);
- mFakePolicy->addDisplayViewport(ui::LogicalDisplayId::DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, uniqueId1, NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport2);
+ mFakePolicy->addDisplayViewport(viewport1);
- viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
- ASSERT_TRUE(viewport);
- ASSERT_EQ(ui::LogicalDisplayId::DEFAULT, viewport->displayId);
- ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+ receivedViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(receivedViewport.has_value());
+ ASSERT_EQ(viewport1, *receivedViewport);
}
/**
@@ -567,28 +564,27 @@
mFakePolicy->clearViewports();
// Add a viewport that's associated with some display port that's not of interest.
- mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, uniqueId1, hdmi3, type);
+ DisplayViewport viewport1 =
+ createViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, uniqueId1, hdmi3, type);
+ mFakePolicy->addDisplayViewport(viewport1);
// Add another viewport, connected to HDMI1 port
- mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, uniqueId2, hdmi1, type);
-
+ DisplayViewport viewport2 =
+ createViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, uniqueId2, hdmi1, type);
+ mFakePolicy->addDisplayViewport(viewport2);
// Check that correct display viewport was returned by comparing the display ports.
std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
- ASSERT_TRUE(hdmi1Viewport);
- ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
- ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+ ASSERT_TRUE(hdmi1Viewport.has_value());
+ ASSERT_EQ(viewport2, *hdmi1Viewport);
// Check that we can still get the same viewport using the uniqueId
hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
- ASSERT_TRUE(hdmi1Viewport);
- ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
- ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
- ASSERT_EQ(type, hdmi1Viewport->type);
+ ASSERT_TRUE(hdmi1Viewport.has_value());
+ ASSERT_EQ(viewport2, *hdmi1Viewport);
// Check that we cannot find a port with "HDMI2", because we never added one
- std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2);
- ASSERT_FALSE(hdmi2Viewport);
+ ASSERT_FALSE(mFakePolicy->getDisplayViewportByPort(hdmi2));
}
// --- InputReaderTest ---
@@ -615,8 +611,10 @@
}
void addDevice(int32_t eventHubId, const std::string& name,
- ftl::Flags<InputDeviceClass> classes, const PropertyMap* configuration) {
+ ftl::Flags<InputDeviceClass> classes, const PropertyMap* configuration,
+ std::string sysfsRootPath = "") {
mFakeEventHub->addDevice(eventHubId, name, classes);
+ mFakeEventHub->setSysfsRootPath(eventHubId, sysfsRootPath);
if (configuration) {
mFakeEventHub->addConfigurationMap(eventHubId, configuration);
@@ -668,6 +666,18 @@
ASSERT_EQ(0U, inputDevices[0].getMotionRanges().size());
}
+TEST_F(InputReaderTest, GetSysfsRootPath) {
+ constexpr std::string SYSFS_ROOT = "xyz";
+ ASSERT_NO_FATAL_FAILURE(
+ addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr, SYSFS_ROOT));
+
+ // Should also have received a notification describing the new input device.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+ InputDeviceInfo inputDevice = mFakePolicy->getInputDevices()[0];
+
+ ASSERT_EQ(SYSFS_ROOT, mReader->getSysfsRootPath(inputDevice.getId()).string());
+}
+
TEST_F(InputReaderTest, InputDeviceRecreatedOnSysfsNodeChanged) {
ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr));
mFakeEventHub->setSysfsRootPath(1, "xyz");
@@ -1045,11 +1055,14 @@
// Add default and second display.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
- mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, "local:1", hdmi1,
- ViewportType::EXTERNAL);
+ DisplayViewport internalViewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(internalViewport);
+ DisplayViewport externalViewport =
+ createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:1", hdmi1, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(externalViewport);
mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO);
mReader->loopOnce();
@@ -1406,6 +1419,68 @@
ASSERT_EQ(mFakeEventHub->fakeReadKernelWakeup(3), false);
}
+TEST_F(InputReaderTest, MergeableInputDevices) {
+ constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why we expect them to be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::JOYSTICK, nullptr));
+
+ // The two devices will be merged to one input device as they have same identifier, and none are
+ // pointer devices.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, MergeableDevicesWithTouch) {
+ constexpr int32_t eventHubIds[3] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why we expect them to be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH_MT, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::KEYBOARD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "3rd", InputDeviceClass::GAMEPAD, nullptr));
+
+ // The three devices will be merged to one input device as they have same identifier, and only
+ // one is a pointer device.
+ ASSERT_EQ(1U, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, UnmergeableTouchDevices) {
+ SCOPED_FLAG_OVERRIDE(prevent_merging_input_pointer_devices, true);
+
+ constexpr int32_t eventHubIds[3] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why they can potentially be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::TOUCH_MT, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "2nd", InputDeviceClass::CURSOR, nullptr));
+
+ // The three devices will not be merged, as they have same identifier, but are all pointer
+ // devices.
+ ASSERT_EQ(3U, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderTest, MergeableMixedDevices) {
+ SCOPED_FLAG_OVERRIDE(prevent_merging_input_pointer_devices, true);
+
+ constexpr int32_t eventHubIds[4] = {END_RESERVED_ID, END_RESERVED_ID + 1, END_RESERVED_ID + 2,
+ END_RESERVED_ID + 3};
+
+ // By default, all of the default-created eventhub devices will have the same identifier
+ // (implicitly vid 0, pid 0, etc.), which is why they can potentially be merged.
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "1st", InputDeviceClass::TOUCH, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "2nd", InputDeviceClass::TOUCH_MT, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[2], "3rd", InputDeviceClass::DPAD, nullptr));
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[3], "4th", InputDeviceClass::JOYSTICK, nullptr));
+
+ // Non-touch devices can be merged with one of the touch devices, as they have same identifier,
+ // but the two touch devices will not combine with each other. It is not specified which touch
+ // device the non-touch devices merge with.
+ ASSERT_EQ(2U, mFakePolicy->getInputDevices().size());
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
@@ -1652,7 +1727,7 @@
mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
const auto info = waitForDevice(mDevice->getName());
- ASSERT_TRUE(info);
+ ASSERT_TRUE(info.has_value());
mDeviceInfo = *info;
}
@@ -1660,8 +1735,10 @@
ui::Rotation orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /*isActive=*/true,
- uniqueId, physicalPort, viewportType);
+ DisplayViewport viewport =
+ createViewport(displayId, width, height, orientation, /*isActive=*/true, uniqueId,
+ physicalPort, viewportType);
+ mFakePolicy->addDisplayViewport(viewport);
mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::DISPLAY_INFO);
}
@@ -1720,7 +1797,7 @@
ViewportType::INTERNAL);
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
const auto info = waitForDevice(mDevice->getName());
- ASSERT_TRUE(info);
+ ASSERT_TRUE(info.has_value());
mDeviceInfo = *info;
}
};
@@ -2052,7 +2129,7 @@
auto externalStylus = createUinputDevice<UinputExternalStylus>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
const auto stylusInfo = waitForDevice(externalStylus->getName());
- ASSERT_TRUE(stylusInfo);
+ ASSERT_TRUE(stylusInfo.has_value());
// Move
mDevice->sendMove(centerPoint + Point(2, 2));
@@ -2121,7 +2198,7 @@
mStylus = mStylusDeviceLifecycleTracker.get();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
const auto info = waitForDevice(mStylus->getName());
- ASSERT_TRUE(info);
+ ASSERT_TRUE(info.has_value());
mStylusInfo = *info;
}
@@ -2394,7 +2471,7 @@
// Connecting an external stylus changes the source of the touchscreen.
const auto deviceInfo = waitForDevice(mDevice->getName());
- ASSERT_TRUE(deviceInfo);
+ ASSERT_TRUE(deviceInfo.has_value());
ASSERT_TRUE(isFromSource(deviceInfo->getSources(), STYLUS_FUSION_SOURCE));
}
@@ -2407,7 +2484,7 @@
createUinputDevice<UinputExternalStylusWithPressure>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
const auto stylusInfo = waitForDevice(stylus->getName());
- ASSERT_TRUE(stylusInfo);
+ ASSERT_TRUE(stylusInfo.has_value());
ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
@@ -2452,7 +2529,7 @@
createUinputDevice<UinputExternalStylusWithPressure>();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
const auto stylusInfo = waitForDevice(stylus->getName());
- ASSERT_TRUE(stylusInfo);
+ ASSERT_TRUE(stylusInfo.has_value());
ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
@@ -2474,10 +2551,10 @@
const auto syncTime = std::chrono::system_clock::now();
// After 72 ms, the event *will* be generated. If we wait the full 72 ms to check that NO event
// is generated in that period, there will be a race condition between the event being generated
- // and the test's wait timeout expiring. Thus, we wait for a shorter duration in the test, which
- // will reduce the liklihood of the race condition occurring.
- const auto waitUntilTimeForNoEvent =
- syncTime + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT / 2));
+ // and the test's wait timeout expiring. Thus, we wait for a shorter duration in the test to
+ // ensure the event is not immediately generated, which should reduce the liklihood of the race
+ // condition occurring.
+ const auto waitUntilTimeForNoEvent = syncTime + std::chrono::milliseconds(1);
mDevice->sendSync();
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntilTimeForNoEvent));
@@ -2879,9 +2956,10 @@
ASSERT_FALSE(mDevice->isEnabled());
// Prepare displays.
- mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, UNIQUE_ID, hdmi,
- ViewportType::INTERNAL);
+ DisplayViewport viewport =
+ createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, UNIQUE_ID, hdmi, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
@@ -2916,9 +2994,12 @@
ASSERT_FALSE(mDevice->isEnabled());
// Device should be enabled when a display is found.
- mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
- NO_PORT, ViewportType::INTERNAL);
+
+ DisplayViewport secondViewport =
+ createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(secondViewport);
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_TRUE(mDevice->isEnabled());
@@ -2944,9 +3025,12 @@
/*changes=*/{});
mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
- mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
- NO_PORT, ViewportType::INTERNAL);
+
+ DisplayViewport secondViewport =
+ createViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /* isActive= */ true, DISPLAY_UNIQUE_ID, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(secondViewport);
const auto initialGeneration = mDevice->getGeneration();
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::Change::DISPLAY_INFO);
@@ -4526,6 +4610,10 @@
NotifyMotionArgs motionArgs;
+ // Hold down the mouse button for the duration of the test, since the mouse tools require
+ // the button to be pressed to make sure they are not hovering.
+ processKey(mapper, BTN_MOUSE, 1);
+
// default tool type is finger
processDown(mapper, 100, 200);
processSync(mapper);
@@ -4533,6 +4621,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)));
+
// eraser
processKey(mapper, BTN_TOOL_RUBBER, 1);
processSync(mapper);
@@ -7175,6 +7266,10 @@
NotifyMotionArgs motionArgs;
+ // Hold down the mouse button for the duration of the test, since the mouse tools require
+ // the button to be pressed to make sure they are not hovering.
+ processKey(mapper, BTN_MOUSE, 1);
+
// default tool type is finger
processId(mapper, 1);
processPosition(mapper, 100, 200);
@@ -7183,6 +7278,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(ToolType::FINGER, motionArgs.pointerProperties[0].toolType);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)));
+
// eraser
processKey(mapper, BTN_TOOL_RUBBER, 1);
processSync(mapper);
@@ -7349,35 +7447,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);
@@ -7385,7 +7487,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) {
@@ -7420,35 +7522,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);
@@ -7456,7 +7562,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));
}
/**
@@ -7520,6 +7626,7 @@
}
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
prepareDisplay(ui::ROTATION_0);
@@ -7532,9 +7639,9 @@
processPosition(mapper, 100, 100);
processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(ui::LogicalDisplayId::INVALID, motionArgs.displayId);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithDisplayId(DISPLAY_ID),
+ WithSource(AINPUT_SOURCE_MOUSE), WithToolType(ToolType::FINGER))));
}
/**
@@ -7570,8 +7677,10 @@
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreDropped) {
addConfigurationProperty("touch.deviceType", "touchScreen");
// Don't set touch.enableForInactiveViewport to verify the default behavior.
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ DisplayViewport viewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport);
configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
@@ -7590,8 +7699,10 @@
TEST_F(MultiTouchInputMapperTest, WhenViewportIsNotActive_TouchesAreProcessed) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "1");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ DisplayViewport viewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/false, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport);
configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
@@ -7612,8 +7723,10 @@
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "0");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ DisplayViewport viewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport);
std::optional<DisplayViewport> optionalDisplayViewport =
mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
ASSERT_TRUE(optionalDisplayViewport.has_value());
@@ -7668,12 +7781,10 @@
TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_TouchesNotAborted) {
addConfigurationProperty("touch.deviceType", "touchScreen");
addConfigurationProperty("touch.enableForInactiveViewport", "1");
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
- std::optional<DisplayViewport> optionalDisplayViewport =
- mFakePolicy->getDisplayViewportByUniqueId(UNIQUE_ID);
- ASSERT_TRUE(optionalDisplayViewport.has_value());
- DisplayViewport displayViewport = *optionalDisplayViewport;
+ DisplayViewport displayViewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(displayViewport);
configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
prepareAxes(POSITION);
@@ -8406,41 +8517,6 @@
WithToolType(ToolType::STYLUS))));
}
-// --- MultiTouchInputMapperTest_ExternalDevice ---
-
-class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
-protected:
- void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
-};
-
-/**
- * Expect fallback to internal viewport if device is external and external viewport is not present.
- */
-TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
- prepareAxes(POSITION);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(ui::ROTATION_0);
- MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
-
- NotifyMotionArgs motionArgs;
-
- // Expect the event to be sent to the internal viewport,
- // because an external viewport is not present.
- processPosition(mapper, 100, 100);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ui::LogicalDisplayId::DEFAULT, motionArgs.displayId);
-
- // Expect the event to be sent to the external viewport if it is present.
- prepareSecondaryDisplay(ViewportType::EXTERNAL);
- processPosition(mapper, 100, 100);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
-}
-
// TODO(b/281840344): Remove the test when the old touchpad stack is removed. It is currently
// unclear what the behavior of the touchpad logic in TouchInputMapper should do after the
// PointerChoreographer refactor.
@@ -8604,6 +8680,8 @@
* fingers start to move downwards, the gesture should be swipe.
*/
TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthSwipe) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
// The min freeform gesture width is 25units/mm x 30mm = 750
// which is greater than fraction of the diagnal length of the touchpad (349).
// Thus, MaxSwipWidth is 750.
@@ -8664,6 +8742,8 @@
* the gesture should be swipe.
*/
TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthLowResolutionSwipe) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
// The min freeform gesture width is 5units/mm x 30mm = 150
// which is greater than fraction of the diagnal length of the touchpad (349).
// Thus, MaxSwipWidth is the fraction of the diagnal length, 349.
@@ -8723,6 +8803,8 @@
* freeform gestures after two fingers start to move downwards.
*/
TEST_F(MultiTouchPointerModeTest, PointerGestureMaxSwipeWidthFreeform) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
@@ -8818,6 +8900,8 @@
}
TEST_F(MultiTouchPointerModeTest, TwoFingerSwipeOffsets) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
NotifyMotionArgs motionArgs;
@@ -8864,6 +8948,8 @@
}
TEST_F(MultiTouchPointerModeTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, false);
+
preparePointerMode(/*xResolution=*/25, /*yResolution=*/25);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
index 110ca5f..53fc8a1 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.cpp
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -38,13 +38,14 @@
}
std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
- nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes) REQUIRES(mLock) {
if (!mNextDevices.empty()) {
std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
mNextDevices.pop();
return device;
}
- return InputReader::createDeviceLocked(when, eventHubId, identifier);
+ return InputReader::createDeviceLocked(when, eventHubId, identifier, classes);
}
} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index e9c7bb4..9abf30c 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -43,8 +43,9 @@
using InputReader::loopOnce;
protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(
- nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier);
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t eventHubId,
+ const InputDeviceIdentifier& identifier,
+ ftl::Flags<InputDeviceClass> classes);
class FakeInputReaderContext : public ContextImpl {
public:
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index ac616d0..8eded2e 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -50,6 +50,8 @@
class MockInputReaderContext : public InputReaderContext {
public:
+ std::string dump() override { return "(dump from MockInputReaderContext)"; }
+
MOCK_METHOD(void, updateGlobalMetaState, (), (override));
MOCK_METHOD(int32_t, getGlobalMetaState, (), (override));
@@ -68,7 +70,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));
@@ -179,6 +181,7 @@
MOCK_METHOD(bool, isDeviceEnabled, (int32_t deviceId), (const, override));
MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override));
MOCK_METHOD(status_t, disableDevice, (int32_t deviceId), (override));
+ MOCK_METHOD(std::filesystem::path, getSysfsRootPath, (int32_t deviceId), (const, override));
MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override));
MOCK_METHOD(bool, setKernelWakeEnabled, (int32_t deviceId, bool enabled), (override));
};
@@ -191,19 +194,22 @@
(ui::LogicalDisplayId displayId, const vec2& position), (override));
MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
+ MOCK_METHOD(std::optional<vec2>, filterPointerMotionForAccessibility,
+ (const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId),
+ (override));
};
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 +272,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..d4b15bc 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -176,16 +176,22 @@
return std::get<NotifyKeyArgs>(args.front());
}
- void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) {
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1);
+ std::list<NotifyArgs> processKeyAndSync(nsecs_t when, int32_t code, int32_t value) {
+ std::list<NotifyArgs> argsList = process(when, EV_KEY, code, value);
+ argsList += process(when, EV_SYN, SYN_REPORT, 0);
+ return argsList;
+ }
+
+ void testDPadKeyRotation(int32_t originalEvdevCode, int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId = DISPLAY_ID) {
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(originalEvdevCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
- argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, originalEvdevCode, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(originalEvdevCode, args.scanCode);
@@ -205,23 +211,16 @@
.With(Args<0>(when))
.Times(keyCodes.size());
for (int32_t keyCode : keyCodes) {
- process(when, EV_KEY, keyCode, 1);
- process(when, EV_SYN, SYN_REPORT, 0);
- process(when, EV_KEY, keyCode, 0);
- process(when, EV_SYN, SYN_REPORT, 0);
+ processKeyAndSync(when, keyCode, 1);
+ processKeyAndSync(when, keyCode, 0);
}
}
TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) {
std::list<NotifyArgs> args;
- args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 2);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
-
- args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 0);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 1);
+ args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 2);
+ args += processKeyAndSync(ARBITRARY_TIME, KEY_0, 0);
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
@@ -241,7 +240,7 @@
ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
// Key down by evdev code.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -255,7 +254,7 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by evdev code.
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -271,6 +270,7 @@
// Key down by usage code.
argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -284,8 +284,9 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up by usage code.
- argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+ argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_A);
argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+ argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -307,6 +308,7 @@
// Key down with unknown scan code or usage code.
std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -320,8 +322,9 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
// Key up with unknown scan code or usage code.
- argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList = process(ARBITRARY_TIME + 1, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ argsList += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -343,10 +346,12 @@
// Key down
std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1);
+ argsList += process(ARBITRARY_TIME, /*readTime=*/12, EV_SYN, SYN_REPORT, 0);
ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime);
// Key up
- argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1);
+ argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 0);
+ argsList += process(ARBITRARY_TIME, /*readTime=*/15, EV_SYN, SYN_REPORT, 0);
ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime);
}
@@ -360,22 +365,22 @@
ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
// Metakey down.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFTSHIFT, 1);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
// Key down.
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 1);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
// Key up.
- argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 2, KEY_A, 0);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState());
// Metakey up.
- argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 3, KEY_LEFTSHIFT, 0);
ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState);
ASSERT_EQ(AMETA_NONE, mMapper->getMetaState());
}
@@ -387,11 +392,10 @@
addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT);
setDisplayOrientation(ui::Rotation::Rotation90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT));
}
TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) {
@@ -405,43 +409,40 @@
AINPUT_SOURCE_KEYBOARD);
setDisplayOrientation(ui::ROTATION_0);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT));
setDisplayOrientation(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_DOWN));
setDisplayOrientation(ui::ROTATION_180);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_RIGHT));
setDisplayOrientation(ui::ROTATION_270);
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
- ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_UP));
// Special case: if orientation changes while key is down, we still emit the same keycode
// in the key up as we did in the key down.
setDisplayOrientation(ui::ROTATION_270);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
setDisplayOrientation(ui::ROTATION_180);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
args = expectSingleKeyArg(argsList);
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
ASSERT_EQ(KEY_UP, args.scanCode);
@@ -454,9 +455,9 @@
addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP);
// Display id should be LogicalDisplayId::INVALID without any display configuration.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
ASSERT_GT(argsList.size(), 0u);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
ASSERT_GT(argsList.size(), 0u);
ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
}
@@ -474,9 +475,9 @@
// ^--- already checked by the previous test
setDisplayOrientation(ui::ROTATION_0);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
ASSERT_GT(argsList.size(), 0u);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
ASSERT_GT(argsList.size(), 0u);
ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId);
@@ -487,9 +488,9 @@
argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_EQ(0u, argsList.size());
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 1);
ASSERT_GT(argsList.size(), 0u);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_UP, 0);
ASSERT_GT(argsList.size(), 0u);
ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId);
}
@@ -565,48 +566,48 @@
ASSERT_FALSE(scrollLockLed);
// Toggle caps lock on.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0);
ASSERT_TRUE(capsLockLed);
ASSERT_FALSE(numLockLed);
ASSERT_FALSE(scrollLockLed);
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState());
// Toggle num lock on.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0);
ASSERT_TRUE(capsLockLed);
ASSERT_TRUE(numLockLed);
ASSERT_FALSE(scrollLockLed);
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState());
// Toggle caps lock off.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_CAPSLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_TRUE(numLockLed);
ASSERT_FALSE(scrollLockLed);
ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState());
// Toggle scroll lock on.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_TRUE(numLockLed);
ASSERT_TRUE(scrollLockLed);
ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
// Toggle num lock off.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_NUMLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_FALSE(numLockLed);
ASSERT_TRUE(scrollLockLed);
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState());
// Toggle scroll lock off.
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(capsLockLed);
ASSERT_FALSE(numLockLed);
ASSERT_FALSE(scrollLockLed);
@@ -618,8 +619,8 @@
addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
- // Key down by scan code.
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ // Key down by evdev code.
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
NotifyKeyArgs args = expectSingleKeyArg(argsList);
ASSERT_EQ(DEVICE_ID, args.deviceId);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -685,37 +686,168 @@
addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
// Key down
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_LEFT, 1);
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE,
expectSingleKeyArg(argsList).flags);
}
-TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- enable_alphabetic_keyboard_wake))) {
- // For internal alphabetic devices, keys will trigger wake on key down.
+// --- KeyboardInputMapperUnitTest_WakeFlagOverride ---
+
+class KeyboardInputMapperUnitTest_WakeFlagOverride : public KeyboardInputMapperUnitTest {
+protected:
+ virtual void SetUp() override {
+ SetUp(/*wakeFlag=*/com::android::input::flags::enable_alphabetic_keyboard_wake());
+ }
+
+ void SetUp(bool wakeFlag) {
+ mWakeFlagInitialValue = com::android::input::flags::enable_alphabetic_keyboard_wake();
+ com::android::input::flags::enable_alphabetic_keyboard_wake(wakeFlag);
+ KeyboardInputMapperUnitTest::SetUp();
+ }
+
+ void TearDown() override {
+ com::android::input::flags::enable_alphabetic_keyboard_wake(mWakeFlagInitialValue);
+ KeyboardInputMapperUnitTest::TearDown();
+ }
+
+ bool mWakeFlagInitialValue;
+};
+
+// --- KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled ---
+
+class KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled
+ : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+ void SetUp() override {
+ KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true);
+ }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_NonAlphabeticKeyboard_WakeFlagEnabled,
+ NonAlphabeticDevice_WakeBehavior) {
+ // For internal non-alphabetic devices keys will not trigger wake.
addKeyByEvdevCode(KEY_A, AKEYCODE_A);
addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1);
- ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0);
- ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
- ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
- ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
- ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
- ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+// --- KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled ---
+
+class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled
+ : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+ void SetUp() override {
+ KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/true);
+
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+ }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior) {
+ // For internal alphabetic devices, keys will trigger wake on key down when
+ // flag is enabled.
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagEnabled, WakeBehavior_UnknownKey) {
+ // For internal alphabetic devices, unknown keys will trigger wake on key down when
+ // flag is enabled.
+
+ const int32_t USAGE_UNKNOWN = 0x07ffff;
+ EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _))
+ .WillRepeatedly(Return(NAME_NOT_FOUND));
+
+ // Key down with unknown scan code or usage code.
+ std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(POLICY_FLAG_WAKE))));
+
+ // Key up with unknown scan code or usage code.
+ argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+ argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+}
+
+// --- KeyboardInputMapperUnitTest_AlphabeticDevice_AlphabeticKeyboardWakeDisabled ---
+
+class KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled
+ : public KeyboardInputMapperUnitTest_WakeFlagOverride {
+protected:
+ void SetUp() override {
+ KeyboardInputMapperUnitTest_WakeFlagOverride::SetUp(/*wakeFlag=*/false);
+
+ ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC));
+ }
+};
+
+TEST_F(KeyboardInputMapperUnitTest_AlphabeticKeyboard_WakeFlagDisabled, WakeBehavior) {
+ // For internal alphabetic devices, keys will not trigger wake when flag is
+ // disabled.
+
+ addKeyByEvdevCode(KEY_A, AKEYCODE_A);
+ addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME);
+ addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE);
+
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_A, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_A, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
+
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
+ EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyKeyArgs>(WithPolicyFlags(0U))));
}
// --- KeyboardInputMapperTest ---
@@ -730,28 +862,33 @@
}
const std::string UNIQUE_ID = "local:0";
- void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
- int32_t originalKeyCode, int32_t rotatedKeyCode,
+ void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalEvdevCode,
+ int32_t rotatedKeyCode,
ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
+
+ void processKeyAndSync(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t code,
+ int32_t value) {
+ process(mapper, when, readTime, EV_KEY, code, value);
+ process(mapper, when, readTime, EV_SYN, SYN_REPORT, 0);
+ }
};
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
- int32_t originalScanCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode,
+ int32_t originalEvdevCode, int32_t rotatedKeyCode,
ui::LogicalDisplayId displayId) {
NotifyKeyArgs args;
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, originalEvdevCode, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(originalEvdevCode, args.scanCode);
ASSERT_EQ(rotatedKeyCode, args.keyCode);
ASSERT_EQ(displayId, args.displayId);
}
@@ -819,23 +956,19 @@
ASSERT_TRUE(device2->isEnabled());
// Test pad key events
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, DISPLAY_ID));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+ testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, newDisplayId));
ASSERT_NO_FATAL_FAILURE(
- testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
- AKEYCODE_DPAD_RIGHT, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
- AKEYCODE_DPAD_DOWN, newDisplayId));
- ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
- AKEYCODE_DPAD_LEFT, newDisplayId));
+ testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, newDisplayId));
+ ASSERT_NO_FATAL_FAILURE(
+ testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, newDisplayId));
}
TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
@@ -857,20 +990,20 @@
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
@@ -937,16 +1070,16 @@
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
// Toggle caps lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
// Toggle num lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
// Toggle scroll lock on.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
mReader->resetLockedModifierState();
@@ -996,40 +1129,40 @@
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
// Toggle num lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState());
ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState());
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_NUMLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
// Toggle caps lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState());
ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState());
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_CAPSLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
// Toggle scroll lock on and off.
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState());
ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState());
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
- process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 1);
+ processKeyAndSync(mapper1, ARBITRARY_TIME, READ_TIME, KEY_SCROLLLOCK, 0);
ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
ASSERT_EQ(AMETA_NONE, mapper1.getMetaState());
ASSERT_EQ(AMETA_NONE, mapper2.getMetaState());
@@ -1048,10 +1181,10 @@
KeyboardInputMapper& keyboardMapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
- process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ processKeyAndSync(keyboardMapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
@@ -1059,10 +1192,10 @@
KeyboardInputMapper& dpadMapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
}
@@ -1071,10 +1204,10 @@
KeyboardInputMapper& gamepadMapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
- process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ processKeyAndSync(*mapper, ARBITRARY_TIME, 0, KEY_HOME, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
}
@@ -1085,10 +1218,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 +1234,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,
@@ -1121,22 +1252,22 @@
addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
}
@@ -1147,16 +1278,16 @@
addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY);
addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAYPAUSE, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAYPAUSE, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
}
@@ -1171,22 +1302,22 @@
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
- std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ std::list<NotifyArgs> argsList = processKeyAndSync(ARBITRARY_TIME, KEY_HOME, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_HOME, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_DOWN, 1);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_DOWN, 0);
ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ argsList = processKeyAndSync(ARBITRARY_TIME, KEY_PLAY, 1);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
- argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ argsList = processKeyAndSync(ARBITRARY_TIME + 1, KEY_PLAY, 0);
ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags);
}
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index ca0f1e8..b7ca24b 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -19,10 +19,13 @@
#include "NotifyArgsBuilders.h"
#include "android/input.h"
+#include <vector>
+
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
+#include <input/InputDevice.h>
#include <input/PrintTools.h>
#include <inttypes.h>
#include <linux/input.h>
@@ -51,12 +54,8 @@
return info;
}
-void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
- InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID);
- tracker.setInputDevices({deviceInfo});
-}
-
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
@@ -79,8 +78,9 @@
<< "Received timeline with productId=" << received.productId
<< " instead of expected productId=" << expected.productId;
LOG_IF(ERROR, expected.sources != received.sources)
- << "Received timeline with sources=" << dumpSet(received.sources, ftl::enum_string)
- << " instead of expected sources=" << dumpSet(expected.sources, ftl::enum_string);
+ << "Received timeline with sources="
+ << dumpContainer(received.sources, ftl::enum_string)
+ << " instead of expected sources=" << dumpContainer(expected.sources, ftl::enum_string);
LOG_IF(ERROR, expected.inputEventActionType != received.inputEventActionType)
<< "Received timeline with inputEventActionType="
<< ftl::enum_string(received.inputEventActionType)
@@ -118,13 +118,14 @@
std::unique_ptr<LatencyTracker> mTracker;
sp<IBinder> connection1;
sp<IBinder> connection2;
+ std::vector<InputDeviceInfo> inputDevices;
void SetUp() override {
connection1 = sp<BBinder>::make();
connection2 = sp<BBinder>::make();
- mTracker = std::make_unique<LatencyTracker>(*this);
- setDefaultInputDeviceInfo(*mTracker);
+ inputDevices.push_back(generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID));
+ mTracker = std::make_unique<LatencyTracker>(*this, inputDevices);
}
void TearDown() override {}
@@ -138,6 +139,10 @@
*/
void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines);
+ void updateInputDevices(const std::vector<InputDeviceInfo>& inputDevicesUpdated) {
+ inputDevices = inputDevicesUpdated;
+ }
+
private:
void processTimeline(const InputEventTimeline& timeline) override {
mReceivedTimelines.push_back(timeline);
@@ -446,7 +451,7 @@
deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN);
deviceInfo2.addSource(AINPUT_SOURCE_STYLUS);
- mTracker->setInputDevices({deviceInfo1, deviceInfo2});
+ updateInputDevices({deviceInfo1, deviceInfo2});
mTracker->trackListener(
MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL,
AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId)
@@ -491,8 +496,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 +539,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 +558,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 9a6b266..cc3d123 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -23,6 +23,7 @@
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
+#include "ScopedFlagOverride.h"
#include "TestEventMatchers.h"
#define TAG "MultiTouchpadInputMapperUnit_test"
@@ -30,29 +31,44 @@
namespace android {
using testing::_;
+using testing::AllOf;
using testing::IsEmpty;
using testing::Return;
using testing::SetArgPointee;
using testing::VariantWith;
-static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
-static constexpr int32_t DISPLAY_WIDTH = 480;
-static constexpr int32_t DISPLAY_HEIGHT = 800;
-static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
-static constexpr int32_t SLOT_COUNT = 5;
+namespace {
-static constexpr int32_t ACTION_POINTER_0_UP =
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
+constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+constexpr int32_t SLOT_COUNT = 5;
+
+constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
+constexpr int32_t ACTION_POINTER_0_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-static constexpr int32_t ACTION_POINTER_1_DOWN =
+constexpr int32_t ACTION_POINTER_1_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+template <typename... Args>
+void assertNotifyArgs(const std::list<NotifyArgs>& args, Args... matchers) {
+ ASSERT_THAT(args, ElementsAre(matchers...))
+ << "Got instead: " << dumpContainer(args, streamableToString);
+}
+
+} // namespace
+
/**
* Unit tests for MultiTouchInputMapper.
*/
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,
@@ -81,6 +97,17 @@
EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, _)).WillRepeatedly(Return(false));
EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
.WillRepeatedly(Return(true));
+ // The following EXPECT_CALL lines are not load-bearing, but without them gtest prints
+ // warnings about "uninteresting mocked call", which are distracting when developing the
+ // tests because this text is interleaved with logs of interest.
+ EXPECT_CALL(mMockEventHub, getVirtualKeyDefinitions(EVENTHUB_ID, _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, _))
+ .WillRepeatedly(testing::Return(false));
+ EXPECT_CALL(mMockEventHub, getVideoFrames(EVENTHUB_ID))
+ .WillRepeatedly(testing::Return(std::vector<TouchVideoFrame>{}));
+ EXPECT_CALL(mMockInputReaderContext, getExternalStylusDevices(_)).WillRepeatedly(Return());
+ EXPECT_CALL(mMockInputReaderContext, getGlobalMetaState()).WillRepeatedly(Return(0));
// Axes that the device has
setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/SLOT_COUNT - 1, /*resolution=*/0);
@@ -106,9 +133,10 @@
mockSlotValues({});
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT,
- ViewportType::INTERNAL);
+ DisplayViewport internalViewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(internalViewport);
mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
mFakePolicy->getReaderConfiguration());
}
@@ -147,24 +175,97 @@
});
}
- std::list<NotifyArgs> processPosition(int32_t x, int32_t y) {
+ [[nodiscard]] std::list<NotifyArgs> processPosition(int32_t x, int32_t y) {
std::list<NotifyArgs> args;
args += process(EV_ABS, ABS_MT_POSITION_X, x);
args += process(EV_ABS, ABS_MT_POSITION_Y, y);
return args;
}
- std::list<NotifyArgs> processId(int32_t id) { return process(EV_ABS, ABS_MT_TRACKING_ID, id); }
+ [[nodiscard]] std::list<NotifyArgs> processId(int32_t id) {
+ return process(EV_ABS, ABS_MT_TRACKING_ID, id);
+ }
- std::list<NotifyArgs> processKey(int32_t code, int32_t value) {
+ [[nodiscard]] std::list<NotifyArgs> processKey(int32_t code, int32_t value) {
return process(EV_KEY, code, value);
}
- std::list<NotifyArgs> processSlot(int32_t slot) { return process(EV_ABS, ABS_MT_SLOT, slot); }
+ [[nodiscard]] std::list<NotifyArgs> processSlot(int32_t slot) {
+ return process(EV_ABS, ABS_MT_SLOT, slot);
+ }
- std::list<NotifyArgs> processSync() { return process(EV_SYN, SYN_REPORT, 0); }
+ [[nodiscard]] std::list<NotifyArgs> processSync() { return process(EV_SYN, SYN_REPORT, 0); }
};
+/**
+ * While a gesture is active, change the display that the device is associated with. Make sure that
+ * the CANCEL event that's generated has the display id of the original DOWN event, rather than the
+ * new display id.
+ */
+TEST_F(MultiTouchInputMapperUnitTest, ChangeAssociatedDisplayIdWhenTouchIsActive) {
+ std::list<NotifyArgs> args;
+
+ // Add a second viewport that later will be associated with our device.
+ DisplayViewport secondViewport =
+ createViewport(SECOND_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:1", NO_PORT, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(secondViewport);
+ std::optional<DisplayViewport> firstViewport =
+ mFakePolicy->getDisplayViewportByUniqueId("local:0");
+
+ // InputReaderConfiguration contains information about how devices are associated with displays.
+ // The mapper receives this information. However, it doesn't actually parse it - that's done by
+ // InputDevice. The mapper asks InputDevice about the associated viewport, so that's what we
+ // need to mock here to simulate association. This abstraction is confusing and should be
+ // refactored.
+
+ // Start with the first viewport
+ ON_CALL((*mDevice), getAssociatedViewport).WillByDefault(Return(firstViewport));
+ args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ int32_t x1 = 100, y1 = 125;
+ args += processKey(BTN_TOUCH, 1);
+ args += processPosition(x1, y1);
+ args += processId(1);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID)))));
+ args.clear();
+
+ // Now associate with the second viewport, and reconfigure.
+ ON_CALL((*mDevice), getAssociatedViewport).WillByDefault(Return(secondViewport));
+ args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ assertNotifyArgs(args,
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(DISPLAY_ID))),
+ VariantWith<NotifyDeviceResetArgs>(WithDeviceId(DEVICE_ID)));
+
+ // The remainder of the gesture is ignored
+ // Move.
+ x1 += 10;
+ y1 += 15;
+ args = processPosition(x1, y1);
+ args += processSync();
+ // Up
+ args += processKey(BTN_TOUCH, 0);
+ args += processId(-1);
+ args += processSync();
+
+ ASSERT_THAT(args, IsEmpty());
+
+ // New touch is delivered with the new display id.
+ args += processId(2);
+ args += processKey(BTN_TOUCH, 1);
+ args += processPosition(x1 + 20, y1 + 40);
+ args += processSync();
+ assertNotifyArgs(args,
+ VariantWith<NotifyMotionArgs>(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDisplayId(SECOND_DISPLAY_ID))));
+}
+
// This test simulates a multi-finger gesture with unexpected reset in between. This might happen
// due to buffer overflow and device with report a SYN_DROPPED. In this case we expect mapper to be
// reset, MT slot state to be re-populated and the gesture should be cancelled and restarted.
@@ -174,7 +275,7 @@
// Two fingers down at once.
constexpr int32_t FIRST_TRACKING_ID = 1, SECOND_TRACKING_ID = 2;
int32_t x1 = 100, y1 = 125, x2 = 200, y2 = 225;
- processKey(BTN_TOUCH, 1);
+ args += processKey(BTN_TOUCH, 1);
args += processPosition(x1, y1);
args += processId(FIRST_TRACKING_ID);
args += processSlot(1);
@@ -182,7 +283,7 @@
args += processId(SECOND_TRACKING_ID);
ASSERT_THAT(args, IsEmpty());
- args = processSync();
+ args += processSync();
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
@@ -255,8 +356,8 @@
ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_POINTER_0_UP))));
// Second finger up.
- processKey(BTN_TOUCH, 0);
- args = processSlot(1);
+ args = processKey(BTN_TOUCH, 0);
+ args += processSlot(1);
args += processId(-1);
ASSERT_THAT(args, IsEmpty());
@@ -266,4 +367,146 @@
VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP))));
}
+class ExternalMultiTouchInputMapperTest : public MultiTouchInputMapperUnitTest {
+protected:
+ void SetUp() override { MultiTouchInputMapperUnitTest::SetUp(/*bus=*/0, /*isExternal=*/true); }
+};
+
+/**
+ * Expect fallback to internal viewport if device is external and external viewport is not present.
+ */
+TEST_F(ExternalMultiTouchInputMapperTest, Viewports_Fallback) {
+ std::list<NotifyArgs> args;
+
+ // Expect the event to be sent to the internal viewport,
+ // because an external viewport is not present.
+ args += processKey(BTN_TOUCH, 1);
+ args += processId(1);
+ args += processPosition(100, 200);
+ args += processSync();
+
+ assertNotifyArgs(args,
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID))));
+
+ // Expect the event to be sent to the external viewport if it is present.
+ DisplayViewport externalViewport =
+ createViewport(SECOND_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:1", NO_PORT, ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(externalViewport);
+ std::optional<DisplayViewport> internalViewport =
+ mFakePolicy->getDisplayViewportByUniqueId("local:0");
+ mReaderConfiguration.setDisplayViewports({*internalViewport, externalViewport});
+ args = mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ assertNotifyArgs(args,
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(DISPLAY_ID))),
+ VariantWith<NotifyDeviceResetArgs>(WithDeviceId(DEVICE_ID)));
+ // Lift up the old pointer.
+ args = processKey(BTN_TOUCH, 0);
+ args += processId(-1);
+ args += processSync();
+
+ // Send new pointer
+ args += processKey(BTN_TOUCH, 1);
+ args += processId(2);
+ args += processPosition(111, 211);
+ args += processSync();
+ assertNotifyArgs(args,
+ VariantWith<NotifyMotionArgs>(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDisplayId(SECOND_DISPLAY_ID))));
+}
+
+class MultiTouchInputMapperPointerModeUnitTest : public MultiTouchInputMapperUnitTest {
+protected:
+ void SetUp() override {
+ MultiTouchInputMapperUnitTest::SetUp();
+
+ // TouchInputMapper goes into POINTER mode whenever INPUT_PROP_DIRECT is not set.
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
+ .WillRepeatedly(Return(false));
+
+ mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+ }
+};
+
+TEST_F(MultiTouchInputMapperPointerModeUnitTest, MouseToolOnlyDownWhenMouseButtonsAreDown) {
+ SCOPED_FLAG_OVERRIDE(disable_touch_input_mapper_pointer_usage, true);
+
+ std::list<NotifyArgs> args;
+
+ // Set the tool type to mouse.
+ args += processKey(BTN_TOOL_MOUSE, 1);
+
+ args += processPosition(100, 100);
+ args += processId(1);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithToolType(ToolType::MOUSE)))));
+
+ // Setting BTN_TOUCH does not make a mouse pointer go down.
+ args = processKey(BTN_TOUCH, 1);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
+
+ // The mouse button is pressed, so the mouse goes down.
+ args = processKey(BTN_MOUSE, 1);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(ToolType::MOUSE),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithToolType(ToolType::MOUSE),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+
+ // The mouse button is released, so the mouse starts hovering.
+ args = processKey(BTN_MOUSE, 0);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithToolType(ToolType::MOUSE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithToolType(ToolType::MOUSE), WithButtonState(0))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithToolType(ToolType::MOUSE)))));
+
+ // Change the tool type so that it is no longer a mouse.
+ // The default tool type is finger, and the finger is already down.
+ args = processKey(BTN_TOOL_MOUSE, 0);
+ args += processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithToolType(ToolType::MOUSE))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithToolType(ToolType::FINGER)))));
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 27da3d3..2b469c5 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -24,6 +24,7 @@
#include "FakePointerController.h"
#include "InterfaceMocks.h"
#include "NotifyArgsBuilders.h"
+#include "ScopedFlagOverride.h"
#include "TestEventMatchers.h"
#include "TestInputListener.h"
@@ -114,6 +115,10 @@
}) {}
class PointerChoreographerTest : public testing::Test {
+public:
+ static constexpr int DENSITY_MEDIUM = 160;
+ static constexpr int DENSITY_HIGH = 320;
+
protected:
TestInputListener mTestListener;
sp<gui::WindowInfosListener> mRegisteredWindowInfoListener;
@@ -124,9 +129,6 @@
mInjectedInitialWindowInfos};
void SetUp() override {
- // flag overrides
- input_flags::hide_pointer_indicators_for_secure_windows(true);
-
ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) {
std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
EXPECT_FALSE(pc->isPointerShown());
@@ -140,6 +142,22 @@
});
}
+ void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
+ if (input_flags::connected_displays_cursor()) {
+ // setDefaultMouseDisplayId is no-op if connected displays are enabled, mouse display is
+ // set based on primary display of the topology.
+ // Setting topology with the primary display should have same effect as calling
+ // setDefaultMouseDisplayId without topology.
+ // For this reason in tests we mock this behavior by creating topology with a single
+ // display.
+ mChoreographer.setDisplayTopology({.primaryDisplayId = displayId,
+ .graph{{displayId, {}}},
+ .displaysDensity = {{displayId, DENSITY_MEDIUM}}});
+ } else {
+ mChoreographer.setDefaultMouseDisplayId(displayId);
+ }
+ }
+
std::shared_ptr<FakePointerController> assertPointerControllerCreated(
ControllerType expectedType) {
EXPECT_FALSE(mCreatedControllers.empty()) << "No PointerController was created";
@@ -292,7 +310,7 @@
TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// For a mouse event without a target display, default viewport should be set for
// the PointerController.
@@ -309,7 +327,7 @@
WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) {
// Set one display as a default mouse display and emit mouse event to create PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -320,7 +338,7 @@
// Change default mouse display. Existing PointerController should be removed and a new one
// should be created.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -329,7 +347,7 @@
}
TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -340,8 +358,19 @@
assertPointerDisplayIdNotified(DISPLAY_ID);
}
+TEST_F(PointerChoreographerTest, NoDefaultMouseSetFallbackToDefaultDisplayId) {
+ mChoreographer.setDisplayViewports(createViewports({ui::LogicalDisplayId::DEFAULT}));
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ assertPointerControllerCreated(ControllerType::MOUSE);
+
+ assertPointerDisplayIdNotified(ui::LogicalDisplayId::DEFAULT);
+}
+
TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -354,7 +383,7 @@
}
TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -373,7 +402,7 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
// Set one viewport as a default mouse display ID.
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -382,7 +411,7 @@
assertPointerDisplayIdNotified(DISPLAY_ID);
// Set another viewport as a default mouse display ID. The mouse is moved to the other display.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
assertPointerControllerCreated(ControllerType::MOUSE);
@@ -391,7 +420,7 @@
TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -421,7 +450,7 @@
TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -457,7 +486,7 @@
AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
// Add two displays and set one to default.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Add two devices, one unassociated and the other associated with non-default mouse display.
mChoreographer.notifyInputDevicesChanged(
@@ -496,7 +525,7 @@
TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -543,7 +572,7 @@
TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -562,7 +591,7 @@
TEST_F(PointerChoreographerTest, MultipleMiceConnectionAndRemoval) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// A mouse is connected, and the pointer is shown.
mChoreographer.notifyInputDevicesChanged(
@@ -599,7 +628,7 @@
TEST_F(PointerChoreographerTest, UnrelatedChangeDoesNotUnfadePointer) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -628,7 +657,7 @@
TEST_F(PointerChoreographerTest, DisabledMouseConnected) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
InputDeviceInfo mouseDeviceInfo =
generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
// Disable this mouse device.
@@ -641,7 +670,7 @@
TEST_F(PointerChoreographerTest, MouseDeviceDisableLater) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
InputDeviceInfo mouseDeviceInfo =
generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
@@ -660,7 +689,7 @@
TEST_F(PointerChoreographerTest, MultipleEnabledAndDisabledMiceConnectionAndRemoval) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
InputDeviceInfo disabledMouseDeviceInfo =
generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID);
disabledMouseDeviceInfo.setEnabled(false);
@@ -1008,6 +1037,34 @@
pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
}
+TEST_F(PointerChoreographerTest, StylusHoverEnterFadesMouseOnDisplay) {
+ // Make sure there are PointerControllers for a mouse and a stylus.
+ mChoreographer.setStylusPointerIconEnabled(true);
+ setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+ auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(mousePc->isPointerShown());
+
+ // Start hovering with a stylus. This should fade the mouse cursor.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ ASSERT_FALSE(mousePc->isPointerShown());
+}
+
using StylusFixtureParam =
std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>;
@@ -1378,7 +1435,7 @@
TEST_F(PointerChoreographerTest, SetsDefaultTouchpadViewportForPointerController) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// For a touchpad event without a target display, default viewport should be set for
// the PointerController.
@@ -1394,7 +1451,7 @@
WhenDefaultTouchpadDisplayChangesSetsDefaultTouchpadViewportForPointerController) {
// Set one display as a default touchpad display and create PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1403,7 +1460,7 @@
firstDisplayPc->assertViewportSet(DISPLAY_ID);
// Change default mouse display. Existing PointerController should be removed.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1411,7 +1468,7 @@
}
TEST_F(PointerChoreographerTest, TouchpadCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -1423,7 +1480,7 @@
}
TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterTouchpadCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1436,7 +1493,7 @@
}
TEST_F(PointerChoreographerTest, WhenTouchpadIsRemovedCallsNotifyPointerDisplayIdChanged) {
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -1456,7 +1513,7 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
// Set one viewport as a default mouse display ID.
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1466,7 +1523,7 @@
// Set another viewport as a default mouse display ID. ui::LogicalDisplayId::INVALID will be
// notified before a touchpad event.
- mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
+ setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1475,7 +1532,7 @@
TEST_F(PointerChoreographerTest, TouchpadMovesPointerAndReturnsNewArgs) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1505,7 +1562,7 @@
TEST_F(PointerChoreographerTest, TouchpadAddsPointerPositionToTheCoords) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1582,7 +1639,7 @@
AssociatedTouchpadMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
// Add two displays and set one to default.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Add two devices, one unassociated and the other associated with non-default mouse display.
mChoreographer.notifyInputDevicesChanged(
@@ -1623,7 +1680,7 @@
TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1660,7 +1717,7 @@
TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
@@ -1680,7 +1737,7 @@
TEST_F(PointerChoreographerTest, SetsPointerIconForMouse) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1696,7 +1753,7 @@
TEST_F(PointerChoreographerTest, DoesNotSetMousePointerIconForWrongDisplayId) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1713,7 +1770,7 @@
TEST_F(PointerChoreographerTest, DoesNotSetPointerIconForWrongDeviceId) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1730,7 +1787,7 @@
TEST_F(PointerChoreographerTest, SetsCustomPointerIconForMouse) {
// Make sure there is a PointerController.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
@@ -1754,7 +1811,7 @@
TEST_F(PointerChoreographerTest, SetsPointerIconForMouseOnTwoDisplays) {
// Make sure there are two PointerControllers on different displays.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -1776,6 +1833,89 @@
firstMousePc->assertPointerIconNotSet();
}
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterMouse) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ pc->setPosition(100, 200);
+ mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+ EXPECT_CALL(mMockPolicy,
+ filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
+ testing::Eq(vec2{10.f, 20.f}),
+ testing::Eq(DISPLAY_ID)))
+ .Times(1)
+ .WillOnce(testing::Return(vec2{4, 13}));
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
+ pc->assertPosition(104, 213);
+ mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
+ WithCursorPosition(104, 213),
+ WithRelativeMotion(10, 20)));
+}
+
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ pc->setPosition(100, 200);
+ mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+ EXPECT_CALL(mMockPolicy,
+ filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}),
+ testing::Eq(vec2{10.f, 20.f}),
+ testing::Eq(DISPLAY_ID)))
+ .Times(1)
+ .WillOnce(testing::Return(vec2{4, 13}));
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(TOUCHPAD_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Cursor position is decided by filtered delta, but pointer coord's relative values are kept.
+ pc->assertPosition(104, 213);
+ mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(104, 213), WithDisplayId(DISPLAY_ID),
+ WithCursorPosition(104, 213),
+ WithRelativeMotion(10, 20)));
+}
+
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) {
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setAccessibilityPointerMotionFilterEnabled(true);
+
+ EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility).Times(0);
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+}
+
using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam =
std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder,
std::function<void(PointerChoreographer&)>, int32_t /*action*/>;
@@ -1966,10 +2106,7 @@
pc->assertSkipScreenshotFlagNotChanged();
}
-TEST_F_WITH_FLAGS(
- PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
// Add a first mouse device
@@ -2127,7 +2264,7 @@
// Make sure there are PointerControllers for a mouse and a stylus.
mChoreographer.setStylusPointerIconEnabled(true);
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -2162,7 +2299,7 @@
TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerOnDisplay) {
// Make sure there are two PointerControllers on different displays.
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID),
@@ -2216,7 +2353,7 @@
TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerWhenDeviceConnected) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Hide the pointer on the display, and then connect the mouse.
mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
@@ -2233,7 +2370,7 @@
TEST_F(PointerChoreographerTest, SetPointerIconVisibilityHidesPointerForTouchpad) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Hide the pointer on the display.
mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
@@ -2282,7 +2419,7 @@
TEST_F(PointerChoreographerTest, DrawingTabletCanReportMouseEvent) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
@@ -2309,7 +2446,7 @@
TEST_F(PointerChoreographerTest, MultipleDrawingTabletsReportMouseEvents) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// First drawing tablet is added
mChoreographer.notifyInputDevicesChanged(
@@ -2357,7 +2494,7 @@
TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) {
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
- mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ setDefaultMouseDisplayId(DISPLAY_ID);
// Mouse and drawing tablet connected
mChoreographer.notifyInputDevicesChanged(
@@ -2601,50 +2738,8 @@
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
}
-using PointerChoreographerDisplayTopologyTestFixtureParam =
- std::tuple<std::string_view /*name*/, int32_t /*source device*/,
- ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
- vec2 /*source position*/, vec2 /*hover move X/Y*/,
- ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>;
-
-class PointerChoreographerDisplayTopologyTestFixture
- : public PointerChoreographerTest,
- public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> {
-public:
- static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
- static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
- static constexpr ui::LogicalDisplayId DISPLAY_RIGHT_ID = ui::LogicalDisplayId{30};
- static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
- static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
- static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
-
- PointerChoreographerDisplayTopologyTestFixture() {
- com::android::input::flags::connected_displays_cursor(true);
- }
-
+class PointerChoreographerDisplayTopologyTests : public PointerChoreographerTest {
protected:
- std::vector<DisplayViewport> mViewports{
- createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0),
- createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0),
- createViewport(DISPLAY_RIGHT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_90),
- createViewport(DISPLAY_BOTTOM_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_180),
- createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270),
- createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90,
- ui::ROTATION_0),
- };
-
- std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>>
- mTopology{
- {DISPLAY_CENTER_ID,
- {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f},
- {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f},
- {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f},
- {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f},
- {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT,
- -90.0f}}},
- };
-
-private:
DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
ui::Rotation orientation) {
DisplayViewport viewport;
@@ -2656,21 +2751,75 @@
}
};
-TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) {
+using PointerChoreographerDisplayTopologyCursorTestFixtureParam =
+ std::tuple<std::string_view /*name*/, int32_t /*source device*/,
+ ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
+ vec2 /*source position*/, vec2 /*hover move X/Y*/,
+ ui::LogicalDisplayId /*destination display*/, vec2 /*destination position*/>;
+
+class PointerChoreographerDisplayTopologyCursorTestFixture
+ : public PointerChoreographerDisplayTopologyTests,
+ public testing::WithParamInterface<
+ PointerChoreographerDisplayTopologyCursorTestFixtureParam> {
+public:
+ static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
+ static constexpr ui::LogicalDisplayId DISPLAY_RIGHT_ID = ui::LogicalDisplayId{30};
+ static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
+ static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
+ static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70};
+
+protected:
+ // Note: viewport size is in pixels and offsets in topology are in dp
+ std::vector<DisplayViewport> mViewports{
+ createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0),
+ createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0),
+ createViewport(DISPLAY_RIGHT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_90),
+ createViewport(DISPLAY_BOTTOM_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_180),
+ createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270),
+ createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90,
+ ui::ROTATION_0),
+ // Create a high density display size 100x100 dp i.e. 200x200 px
+ createViewport(DISPLAY_HIGH_DENSITY_ID, /*width*/ 200, /*height*/ 200, ui::ROTATION_0),
+ };
+
+ DisplayTopologyGraph
+ mTopology{DISPLAY_CENTER_ID,
+ {{DISPLAY_CENTER_ID,
+ {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 50.0f},
+ // Place a high density display on the left of DISPLAY_TOP_ID with 25 dp
+ // gap
+ {DISPLAY_HIGH_DENSITY_ID, DisplayTopologyPosition::TOP, -75.0f},
+ {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
+ {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
+ {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
+ {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}},
+ {{DISPLAY_CENTER_ID, DENSITY_MEDIUM},
+ {DISPLAY_TOP_ID, DENSITY_MEDIUM},
+ {DISPLAY_RIGHT_ID, DENSITY_MEDIUM},
+ {DISPLAY_BOTTOM_ID, DENSITY_MEDIUM},
+ {DISPLAY_LEFT_ID, DENSITY_MEDIUM},
+ {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM},
+ {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}};
+};
+
+TEST_P(PointerChoreographerDisplayTopologyCursorTestFixture,
+ PointerChoreographerDisplayTopologyTest) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove,
destinationDisplay, destinationPosition] = GetParam();
mChoreographer.setDisplayViewports(mViewports);
- mChoreographer.setDefaultMouseDisplayId(
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID);
+ setDefaultMouseDisplayId(DISPLAY_CENTER_ID);
mChoreographer.setDisplayTopology(mTopology);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(pointerControllerType);
- ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- pc->getDisplayId());
+ ASSERT_EQ(DISPLAY_CENTER_ID, pc->getDisplayId());
// Set initial position of the PointerController.
pc->setPosition(initialPosition.x, initialPosition.y);
@@ -2702,79 +2851,320 @@
}
INSTANTIATE_TEST_SUITE_P(
- PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture,
+ PointerChoreographerTest, PointerChoreographerDisplayTopologyCursorTestFixture,
testing::Values(
// Note: Upon viewport transition cursor will be positioned at the boundary of the
// destination, as we drop any unconsumed delta.
- std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
- ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
- vec2(25, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(75, 75) /* destination x/y */),
- std::make_tuple("TransitionToRightDisplay", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(50, 50) /* initial x/y */, vec2(100, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID,
- vec2(0,
- 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
+ std::make_tuple(
+ "PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(50, 50) /* destination x/y */),
+ std::make_tuple(
+ "UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+ vec2(25, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(75, 75) /* destination x/y */),
+ std::make_tuple(
+ "TransitionToRightDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
+ vec2(100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_RIGHT_ID,
+ vec2(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */),
std::make_tuple(
"TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
ToolType::MOUSE, vec2(50, 50) /* initial x/y */,
vec2(-100, 25) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_LEFT_ID,
vec2(90, 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/),
- std::make_tuple("TransitionToTopDisplay",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(50, 50) /* initial x/y */,
- vec2(25, -100) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
- vec2(50 + 25 - 10,
- 90) /* Bottom edge: (source + delta - offset, height) */),
- std::make_tuple("TransitionToBottomDisplay",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(50, 50) /* initial x/y */,
- vec2(25, 100) /* delta x/y */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
- vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
- std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(5, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(5, 0) /* Top edge */),
- std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
- ControllerType::MOUSE, ToolType::MOUSE,
- vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(99, 5) /* Top edge */),
- std::make_tuple("NoTransitionAtBottomOffset",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(5, 95) /* initial x/y */,
- vec2(0, 100) /* Move Down */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(5, 99) /* Bottom edge */),
- std::make_tuple("NoTransitionAtLeftOffset",
- AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
- ToolType::FINGER, vec2(5, 5) /* initial x/y */,
- vec2(-100, 0) /* Move Left */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
- vec2(0, 5) /* Left edge */),
std::make_tuple(
- "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */,
- vec2(10, -10) /* Move dignally to top right corner */,
- PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
- vec2(0, 90) /* bottom left corner */)),
- [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
- return std::string{std::get<0>(p.param)};
- });
+ "TransitionToTopDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+ vec2(25, -100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_TOP_ID,
+ vec2(50 + 25 - 50,
+ 90) /* Bottom edge: (source + delta - offset, height) */),
+ std::make_tuple(
+ "TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(50, 50) /* initial x/y */,
+ vec2(25, 100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_BOTTOM_ID,
+ vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
+ // move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID
+ std::make_tuple(
+ "NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(35, 50) /* initial x/y */,
+ vec2(0, -100) /* Move Up */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(35, 0) /* Top edge */),
+ std::make_tuple(
+ "NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(95, 5) /* initial x/y */,
+ vec2(100, 0) /* Move Right */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(99, 5) /* Top edge */),
+ std::make_tuple(
+ "NoTransitionAtBottomOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(5, 95) /* initial x/y */,
+ vec2(0, 100) /* Move Down */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(5, 99) /* Bottom edge */),
+ std::make_tuple(
+ "NoTransitionAtLeftOffset", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
+ ControllerType::MOUSE, ToolType::FINGER, vec2(5, 5) /* initial x/y */,
+ vec2(-100, 0) /* Move Left */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::DISPLAY_CENTER_ID,
+ vec2(0, 5) /* Left edge */),
+ std::make_tuple("TransitionAtTopRightCorner",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, vec2(95, 5) /* initial x/y */,
+ vec2(10, -10) /* Move diagonally to top right corner */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::
+ DISPLAY_TOP_RIGHT_CORNER_ID,
+ vec2(0, 90) /* bottom left corner */),
+ std::make_tuple("TransitionToHighDpDisplay",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
+ vec2(0, -50) /* delta x/y */,
+ PointerChoreographerDisplayTopologyCursorTestFixture::
+ DISPLAY_HIGH_DENSITY_ID,
+ /* Bottom edge: ((source + delta - offset) * density, height) */
+ vec2((20 + 0 + 75) * 2, 200))),
+ [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyCursorTestFixtureParam>&
+ p) { return std::string{std::get<0>(p.param)}; });
+
+class PointerChoreographerDisplayTopologyDefaultMouseDisplayTests
+ : public PointerChoreographerDisplayTopologyTests {
+protected:
+ static constexpr ui::LogicalDisplayId FIRST_DISPLAY_ID = ui::LogicalDisplayId{10};
+ static constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{20};
+ static constexpr ui::LogicalDisplayId THIRD_DISPLAY_ID = ui::LogicalDisplayId{30};
+
+ DisplayViewport createViewport(ui::LogicalDisplayId displayId) {
+ return PointerChoreographerDisplayTopologyTests::createViewport(displayId, /*width=*/100,
+ /*height=*/100,
+ ui::ROTATION_0);
+ }
+
+ void setDisplayTopologyWithDisplays(
+ ui::LogicalDisplayId primaryDisplayId,
+ const std::vector<ui::LogicalDisplayId>& adjacentDisplays = {}) {
+ // Prepare a topology with all display connected from left to right.
+ ui::LogicalDisplayId previousDisplay = primaryDisplayId;
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>
+ topologyGraph;
+ topologyGraph[primaryDisplayId] = {};
+
+ std::unordered_map<ui::LogicalDisplayId, int> displaysDensity;
+ displaysDensity[primaryDisplayId] = DENSITY_MEDIUM;
+
+ for (ui::LogicalDisplayId adjacentDisplayId : adjacentDisplays) {
+ topologyGraph[previousDisplay].push_back({.displayId = adjacentDisplayId,
+ .position = DisplayTopologyPosition::RIGHT,
+ .offsetDp = 0.0f});
+ topologyGraph[adjacentDisplayId].push_back({.displayId = previousDisplay,
+ .position = DisplayTopologyPosition::LEFT,
+ .offsetDp = 0.0f});
+
+ displaysDensity[adjacentDisplayId] = DENSITY_MEDIUM;
+ }
+
+ mChoreographer.setDisplayTopology({primaryDisplayId, topologyGraph, displaysDensity});
+ }
+};
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ UnrelatedTopologyUpdatesDoNotChangeCursorDisplay) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ // Set first display as primary display and emit mouse event to create PointerController.
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Add another display keeping the primary display unchanged
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Move cursor to second display and add a third display
+ auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+ createViewport(SECOND_DISPLAY_ID),
+ createViewport(THIRD_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID, /*adjacentDisplays=*/
+ {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID});
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Change the primary display to the third display
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/THIRD_DISPLAY_ID, /*adjacentDisplays=*/
+ {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID});
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ PrimaryDisplayIsFallbackOnPointerDisplayRemoved) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+
+ // Add two displays and move cursor to the secondary display
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Remove the secondary display
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID);
+
+ assertPointerControllerRemoved(pc);
+ pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ UsePrimaryDisplayIfAssociatedDisplayIsInTopology) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+ SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+ // Add two displays
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/SECOND_DISPLAY_ID,
+ /*adjacentDisplays=*/{FIRST_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, FIRST_DISPLAY_ID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ AllowCrossingDisplayEvenWithAssociatedDisplaySet) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+ SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+ // Add two displays
+ mChoreographer.setDisplayViewports(
+ {createViewport(FIRST_DISPLAY_ID), createViewport(SECOND_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Move cursor to the secondary display
+ auto pointerBuilder = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, /*x=*/100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, /*y=*/0);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ assertPointerControllerNotCreated();
+ pc->assertViewportSet(SECOND_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests,
+ AddAssociatedDisplayCursorOutsideOfDisplayTopology) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
+ SCOPED_FLAG_OVERRIDE(connected_displays_associated_display_cursor_bugfix, true);
+
+ // Add three displays, with only first and second display in DisplayTopolgoy
+ mChoreographer.setDisplayViewports({createViewport(FIRST_DISPLAY_ID),
+ createViewport(SECOND_DISPLAY_ID),
+ createViewport(THIRD_DISPLAY_ID)});
+ setDisplayTopologyWithDisplays(/*primaryDisplayId=*/FIRST_DISPLAY_ID,
+ /*adjacentDisplays=*/{SECOND_DISPLAY_ID});
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(FIRST_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Adds a new mouse associated with third display
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/1, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, THIRD_DISPLAY_ID)}});
+
+ pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertViewportSet(THIRD_DISPLAY_ID);
+ ASSERT_TRUE(pc->isPointerShown());
+}
class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
-TEST_F_WITH_FLAGS(
- PointerChoreographerWindowInfoListenerTest,
- doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
- hide_pointer_indicators_for_secure_windows))) {
+TEST_F(PointerChoreographerWindowInfoListenerTest,
+ doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed) {
sp<android::gui::WindowInfosListener> registeredListener;
sp<android::gui::WindowInfosListener> localListenerCopy;
{
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/ScopedFlagOverride.h b/services/inputflinger/tests/ScopedFlagOverride.h
new file mode 100644
index 0000000..883673c
--- /dev/null
+++ b/services/inputflinger/tests/ScopedFlagOverride.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <com_android_input_flags.h>
+#include <functional>
+
+namespace android {
+
+/**
+ * Provide a local override for a flag value. The value is restored when the object of this class
+ * goes out of scope.
+ * This class is not intended to be used directly, because its usage is cumbersome.
+ * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided.
+ */
+class ScopedFlagOverride {
+public:
+ ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value)
+ : mInitialValue(read()), mWriteValue(write) {
+ mWriteValue(value);
+ }
+ ~ScopedFlagOverride() { mWriteValue(mInitialValue); }
+
+private:
+ const bool mInitialValue;
+ std::function<void(bool)> mWriteValue;
+};
+
+typedef bool (*ReadFlagValueFunction)();
+typedef void (*WriteFlagValueFunction)(bool);
+
+/**
+ * Use this macro to locally override a flag value.
+ * Example usage:
+ * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ * Note: this works by creating a local variable in your current scope. Don't call this twice for
+ * the same flag, because the variable names will clash!
+ */
+#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \
+ ReadFlagValueFunction read##NAME = com::android::input::flags::NAME; \
+ WriteFlagValueFunction write##NAME = com::android::input::flags::NAME; \
+ ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE))
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 7078e49..1262af7 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -38,10 +38,23 @@
auto operator<=>(const PointF&) const = default;
};
+namespace internal {
+
+template <typename T>
+static bool valuesMatch(T value1, T value2) {
+ if constexpr (std::is_floating_point_v<T>) {
+ return std::abs(value1 - value2) < EPSILON;
+ } else {
+ return value1 == value2;
+ }
+}
+
inline std::string pointFToString(const PointF& p) {
return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
}
+} // namespace internal
+
/// Source
class WithSourceMatcher {
public:
@@ -440,8 +453,10 @@
}
if (mPointers != actualPointers) {
- *os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
- << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
+ *os << "expected pointers "
+ << dumpMap(mPointers, constToString, internal::pointFToString)
+ << ", but got "
+ << dumpMap(actualPointers, constToString, internal::pointFToString);
return false;
}
return true;
@@ -456,15 +471,17 @@
}
if (mPointers != actualPointers) {
- *os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
- << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
+ *os << "expected pointers "
+ << dumpMap(mPointers, constToString, internal::pointFToString)
+ << ", but got "
+ << dumpMap(actualPointers, constToString, internal::pointFToString);
return false;
}
return true;
}
void DescribeTo(std::ostream* os) const {
- *os << "with pointers " << dumpMap(mPointers, constToString, pointFToString);
+ *os << "with pointers " << dumpMap(mPointers, constToString, internal::pointFToString);
}
void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointers"; }
@@ -492,8 +509,8 @@
}
if (mPointerIds != actualPointerIds) {
- *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got "
- << dumpSet(actualPointerIds);
+ *os << "expected pointer ids " << dumpContainer(mPointerIds) << ", but got "
+ << dumpContainer(actualPointerIds);
return false;
}
return true;
@@ -506,14 +523,16 @@
}
if (mPointerIds != actualPointerIds) {
- *os << "expected pointer ids " << dumpSet(mPointerIds) << ", but got "
- << dumpSet(actualPointerIds);
+ *os << "expected pointer ids " << dumpContainer(mPointerIds) << ", but got "
+ << dumpContainer(actualPointerIds);
return false;
}
return true;
}
- void DescribeTo(std::ostream* os) const { *os << "with pointer ids " << dumpSet(mPointerIds); }
+ void DescribeTo(std::ostream* os) const {
+ *os << "with pointer ids " << dumpContainer(mPointerIds);
+ }
void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer ids"; }
@@ -706,8 +725,9 @@
}
const PointerCoords& coords = event.pointerCoords[mPointerIndex];
- bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) &&
- mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ bool matches =
+ internal::valuesMatch(mRelX, coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)) &&
+ internal::valuesMatch(mRelY, coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
if (!matches) {
*os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
<< mPointerIndex << ", but got ("
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index ea69fff..5f5aa63 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -18,10 +18,13 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <input/AccelerationCurve.h>
+#include <log/log.h>
#include <thread>
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
+#include "TestConstants.h"
#include "TestEventMatchers.h"
#define TAG "TouchpadInputMapper_test"
@@ -121,9 +124,10 @@
*/
TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) {
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
-
+ DisplayViewport viewport =
+ createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(viewport);
std::list<NotifyArgs> args;
args += mMapper->reconfigure(systemTime(SYSTEM_TIME_MONOTONIC), mReaderConfiguration,
@@ -190,4 +194,67 @@
mFakePolicy->assertTouchpadHardwareStateNotified();
}
+TEST_F(TouchpadInputMapperTest, TouchpadAccelerationDisabled) {
+ mReaderConfiguration.touchpadAccelerationEnabled = false;
+ mReaderConfiguration.touchpadPointerSpeed = 3;
+
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+ auto* touchpadMapper = static_cast<TouchpadInputMapper*>(mMapper.get());
+
+ const auto accelCurvePropsDisabled =
+ touchpadMapper->getGesturePropertyForTesting("Pointer Accel Curve");
+ ASSERT_TRUE(accelCurvePropsDisabled.has_value());
+ std::vector<double> curveValuesDisabled = accelCurvePropsDisabled.value().getRealValues();
+ std::vector<AccelerationCurveSegment> curve =
+ createFlatAccelerationCurve(mReaderConfiguration.touchpadPointerSpeed);
+ double expectedBaseGain = curve[0].baseGain;
+ ASSERT_EQ(curveValuesDisabled[0], std::numeric_limits<double>::infinity());
+ ASSERT_EQ(curveValuesDisabled[1], 0);
+ ASSERT_NEAR(curveValuesDisabled[2], expectedBaseGain, EPSILON);
+ ASSERT_EQ(curveValuesDisabled[3], 0);
+}
+
+TEST_F(TouchpadInputMapperTest, TouchpadAccelerationEnabled) {
+ // Enable touchpad acceleration.
+ mReaderConfiguration.touchpadAccelerationEnabled = true;
+ mReaderConfiguration.touchpadPointerSpeed = 3;
+
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ auto* touchpadMapper = static_cast<TouchpadInputMapper*>(mMapper.get());
+
+ // Get the acceleration curve properties when acceleration is enabled.
+ const auto accelCurvePropsEnabled =
+ touchpadMapper->getGesturePropertyForTesting("Pointer Accel Curve");
+ ASSERT_TRUE(accelCurvePropsEnabled.has_value());
+
+ // Get the curve values.
+ std::vector<double> curveValuesEnabled = accelCurvePropsEnabled.value().getRealValues();
+
+ // Use createAccelerationCurveForPointerSensitivity to get expected curve segments.
+ std::vector<AccelerationCurveSegment> expectedCurveSegments =
+ createAccelerationCurveForPointerSensitivity(mReaderConfiguration.touchpadPointerSpeed);
+
+ // Iterate through the segments and compare the values.
+ for (size_t i = 0; i < expectedCurveSegments.size(); ++i) {
+ // Check max speed.
+ if (std::isinf(expectedCurveSegments[i].maxPointerSpeedMmPerS)) {
+ ASSERT_TRUE(std::isinf(curveValuesEnabled[i * 4 + 0]));
+ } else {
+ ASSERT_NEAR(curveValuesEnabled[i * 4 + 0],
+ expectedCurveSegments[i].maxPointerSpeedMmPerS, EPSILON);
+ }
+
+ // Check that the x^2 term is zero.
+ ASSERT_NEAR(curveValuesEnabled[i * 4 + 1], 0, EPSILON);
+ ASSERT_NEAR(curveValuesEnabled[i * 4 + 2], expectedCurveSegments[i].baseGain, EPSILON);
+ ASSERT_NEAR(curveValuesEnabled[i * 4 + 3], expectedCurveSegments[i].reciprocal, EPSILON);
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 48e1954..5000db7 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -33,8 +33,8 @@
"frameworks/native/services/inputflinger",
],
shared_libs: [
- "libinputreader",
"libinputflinger_base",
+ "libinputreader",
],
sanitize: {
hwaddress: true,
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
index 812969b..43975f0 100644
--- a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -18,10 +18,16 @@
namespace android {
-namespace {
static constexpr int32_t MAX_RANDOM_POINTERS = 4;
static constexpr int32_t MAX_RANDOM_DEVICES = 4;
-} // namespace
+
+// The maximum value that we use for the action button field of NotifyMotionArgs. (We allow multiple
+// bits to be set for this since we're just trying to generate a fuzzed event stream that doesn't
+// cause crashes when enum values are converted to Rust — we don't necessarily want it to be valid.)
+//
+// AMOTION_EVENT_BUTTON_STYLUS_SECONDARY should be replaced with whatever AMOTION_EVENT_BUTTON_
+// value is highest if the enum is edited.
+static constexpr int8_t MAX_ACTION_BUTTON_VALUE = (AMOTION_EVENT_BUTTON_STYLUS_SECONDARY << 1) - 1;
int getFuzzedMotionAction(FuzzedDataProvider& fdp) {
int actionMasked = fdp.PickValueInArray<int>({
@@ -187,18 +193,16 @@
fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9);
const nsecs_t readTime = downTime;
const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9);
+ const int32_t actionButton = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_ACTION_BUTTON_VALUE);
const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId,
- POLICY_FLAG_PASS_TO_USER, action,
- /*actionButton=*/fdp.ConsumeIntegral<int32_t>(),
+ POLICY_FLAG_PASS_TO_USER, action, actionButton,
getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp),
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
- pointerProperties.data(), pointerCoords.data(),
- /*xPrecision=*/0,
- /*yPrecision=*/0, cursorX, cursorY, downTime,
- /*videoFrames=*/{});
+ pointerProperties.data(), pointerCoords.data(), /*xPrecision=*/0,
+ /*yPrecision=*/0, cursorX, cursorY, downTime, /*videoFrames=*/{});
}
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
index 79a5ff6..abce931 100644
--- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -48,9 +48,9 @@
auto [it, _] = mVerifiers.emplace(args.displayId, "Fuzz Verifier");
InputVerifier& verifier = it->second;
const Result<void> result =
- verifier.processMovement(args.deviceId, args.source, args.action,
+ verifier.processMovement(args.deviceId, args.source, args.action, args.actionButton,
args.getPointerCount(), args.pointerProperties.data(),
- args.pointerCoords.data(), args.flags);
+ args.pointerCoords.data(), args.flags, args.buttonState);
if (result.ok()) {
return args;
}
@@ -76,7 +76,6 @@
window.setDupTouchToWallpaper(fdp.ConsumeBool());
window.setIsWallpaper(fdp.ConsumeBool());
window.setVisible(fdp.ConsumeBool());
- window.setPreventSplitting(fdp.ConsumeBool());
const bool isTrustedOverlay = fdp.ConsumeBool();
window.setTrustedOverlay(isTrustedOverlay);
if (isTrustedOverlay) {
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 6be922d..0c8ba50 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -165,6 +165,10 @@
return reader->getBluetoothAddress(deviceId);
}
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const {
+ return reader->getSysfsRootPath(deviceId);
+ }
+
void sysfsNodeChanged(const std::string& sysfsNodePath) {
reader->sysfsNodeChanged(sysfsNodePath);
}
@@ -297,6 +301,7 @@
std::chrono::microseconds(fdp->ConsumeIntegral<size_t>()));
},
[&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); },
+ [&]() -> void { reader->getSysfsRootPath(fdp->ConsumeIntegral<int32_t>()); },
})();
}
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 157a333..9c027fa 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -17,6 +17,8 @@
#include <fuzzer/FuzzedDataProvider.h>
#include <linux/input.h>
+#include <vector>
+
#include "../../InputDeviceMetricsSource.h"
#include "../InputEventTimeline.h"
#include "NotifyArgsBuilders.h"
@@ -58,7 +60,8 @@
FuzzedDataProvider fdp(data, size);
EmptyProcessor emptyProcessor;
- LatencyTracker tracker(emptyProcessor);
+ std::vector<InputDeviceInfo> emptyDevices;
+ LatencyTracker tracker(emptyProcessor, emptyDevices);
// Make some pre-defined tokens to ensure that some timelines are complete.
std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index a1da39a..f619c48 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -34,6 +34,28 @@
android::EventHubInterface::DEVICE_ADDED,
android::EventHubInterface::DEVICE_REMOVED};
+static const android::InputDeviceClass kInputDeviceClasses[] = {
+ android::InputDeviceClass::KEYBOARD,
+ android::InputDeviceClass::ALPHAKEY,
+ android::InputDeviceClass::TOUCH,
+ android::InputDeviceClass::CURSOR,
+ android::InputDeviceClass::TOUCH_MT,
+ android::InputDeviceClass::DPAD,
+ android::InputDeviceClass::GAMEPAD,
+ android::InputDeviceClass::SWITCH,
+ android::InputDeviceClass::JOYSTICK,
+ android::InputDeviceClass::VIBRATOR,
+ android::InputDeviceClass::MIC,
+ android::InputDeviceClass::EXTERNAL_STYLUS,
+ android::InputDeviceClass::ROTARY_ENCODER,
+ android::InputDeviceClass::SENSOR,
+ android::InputDeviceClass::BATTERY,
+ android::InputDeviceClass::LIGHT,
+ android::InputDeviceClass::TOUCHPAD,
+ android::InputDeviceClass::VIRTUAL,
+ android::InputDeviceClass::EXTERNAL,
+};
+
constexpr size_t kValidCodes[] = {
SYN_REPORT,
ABS_MT_SLOT,
@@ -105,7 +127,13 @@
void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
- return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
+ uint32_t flags = 0;
+ for (auto inputDeviceClass : kInputDeviceClasses) {
+ if (mFdp->ConsumeBool()) {
+ flags |= static_cast<uint32_t>(inputDeviceClass);
+ }
+ }
+ return ftl::Flags<InputDeviceClass>(flags);
}
InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
return mIdentifier;
@@ -268,6 +296,7 @@
bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); }
status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); }
+ std::filesystem::path getSysfsRootPath(int32_t deviceId) const override { return {}; }
void sysfsNodeChanged(const std::string& sysfsNodePath) override {}
bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override {
return mFdp->ConsumeBool();
@@ -332,6 +361,7 @@
std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp)
: mEventHub(eventHub), mPolicy(sp<FuzzInputReaderPolicy>::make(fdp)), mFdp(fdp) {}
~FuzzInputReaderContext() {}
+ std::string dump() { return "(dump from FuzzInputReaderContext)"; }
void updateGlobalMetaState() override {}
int32_t getGlobalMetaState() { return mFdp->ConsumeIntegral<int32_t>(); }
void disableVirtualKeysUntil(nsecs_t time) override {}
@@ -346,7 +376,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>(); };
@@ -367,8 +397,8 @@
template <class Fdp>
InputDevice getFuzzedInputDevice(Fdp& fdp, FuzzInputReaderContext* context) {
InputDeviceIdentifier identifier;
- identifier.name = fdp.ConsumeRandomLengthString(16);
- identifier.location = fdp.ConsumeRandomLengthString(12);
+ identifier.name = fdp.ConsumeRandomLengthUtf8String(16);
+ identifier.location = fdp.ConsumeRandomLengthUtf8String(12);
int32_t deviceID = fdp.ConsumeIntegralInRange(0, 5);
int32_t deviceGeneration = fdp.ConsumeIntegralInRange(0, 5);
return InputDevice(context, deviceID, deviceGeneration, identifier);
diff --git a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
index 2f76f18..b258118 100644
--- a/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
+++ b/services/inputflinger/tests/fuzzers/ThreadSafeFuzzedDataProvider.h
@@ -15,7 +15,7 @@
*/
#include <fuzzer/FuzzedDataProvider.h>
-
+#include <algorithm>
/**
* A thread-safe interface to the FuzzedDataProvider
*/
@@ -60,6 +60,44 @@
return FuzzedDataProvider::ConsumeRandomLengthString();
}
+ // Converting the string to a UTF-8 string by setting the prefix bits of each
+ // byte according to UTF-8 encoding rules.
+ std::string ConsumeRandomLengthUtf8String(size_t max_length) {
+ std::scoped_lock _l(mLock);
+ std::string result = FuzzedDataProvider::ConsumeRandomLengthString(max_length);
+ size_t remaining_bytes = result.length(), idx = 0;
+ while (remaining_bytes > 0) {
+ size_t random_byte_size = FuzzedDataProvider::ConsumeIntegralInRange(1, 4);
+ size_t byte_size = std::min(random_byte_size, remaining_bytes);
+ switch (byte_size) {
+ // Prefix byte: 0xxxxxxx
+ case 1:
+ result[idx++] &= 0b01111111;
+ break;
+ // Prefix bytes: 110xxxxx 10xxxxxx
+ case 2:
+ result[idx++] = (result[idx] & 0b00011111) | 0b11000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ break;
+ // Prefix bytes: 1110xxxx 10xxxxxx 10xxxxxx
+ case 3:
+ result[idx++] = (result[idx] & 0b00001111) | 0b11100000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ break;
+ // Prefix bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ case 4:
+ result[idx++] = (result[idx] & 0b00000111) | 0b11110000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ result[idx++] = (result[idx] & 0b00111111) | 0b10000000;
+ break;
+ }
+ remaining_bytes -= byte_size;
+ }
+ return result;
+ }
+
std::string ConsumeRemainingBytesAsString() {
std::scoped_lock _l(mLock);
return FuzzedDataProvider::ConsumeRemainingBytesAsString();
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 4f65e77..78f8f7b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -52,6 +52,7 @@
],
whole_static_libs: [
+ "android.adpf.sessionmanager_aidl-ndk",
"android.os.hintmanager_aidl-ndk",
],
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index 0ba1909..a817a7b 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -173,6 +173,21 @@
return CACHE_SUPPORT(6, processHalResult(handle->getSupportInfo(), "getSupportInfo"));
}
+HalResult<void> PowerHalController::sendCompositionData(
+ const std::vector<hal::CompositionData>& data) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ return CACHE_SUPPORT(6,
+ processHalResult(handle->sendCompositionData(data),
+ "sendCompositionData"));
+}
+
+HalResult<void> PowerHalController::sendCompositionUpdate(const hal::CompositionUpdate& update) {
+ std::shared_ptr<HalWrapper> handle = initHal();
+ return CACHE_SUPPORT(6,
+ processHalResult(handle->sendCompositionUpdate(update),
+ "sendCompositionUpdate"));
+}
+
} // namespace power
} // namespace android
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 068c23f..9c83bf5 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -79,6 +79,16 @@
return HalResult<Aidl::SupportInfo>::unsupported();
}
+HalResult<void> EmptyHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>&) {
+ ALOGV("Skipped sendCompositionData because %s", getUnsupportedMessage());
+ return HalResult<void>::unsupported();
+}
+
+HalResult<void> EmptyHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate&) {
+ ALOGV("Skipped sendCompositionUpdate because %s", getUnsupportedMessage());
+ return HalResult<void>::unsupported();
+}
+
const char* EmptyHalWrapper::getUnsupportedMessage() {
return "Power HAL is not supported";
}
@@ -292,6 +302,14 @@
return HalResult<Aidl::SupportInfo>::fromStatus(result, std::move(support));
}
+HalResult<void> AidlHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>& data) {
+ return HalResult<void>::fromStatus(mHandle->sendCompositionData(data));
+}
+
+HalResult<void> AidlHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate& update) {
+ return HalResult<void>::fromStatus(mHandle->sendCompositionUpdate(update));
+}
+
const char* AidlHalWrapper::getUnsupportedMessage() {
return "Power HAL doesn't support it";
}
diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
index 61ab47a..3a1b426 100644
--- a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp
@@ -40,10 +40,10 @@
using namespace std::chrono_literals;
// Values from Boost.aidl and Mode.aidl.
-static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
-static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
-static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
-static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(*ndk::enum_range<Boost>().begin());
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(*(ndk::enum_range<Boost>().end()-1));
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(*ndk::enum_range<Mode>().begin());
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(*(ndk::enum_range<Mode>().end()-1));
class DurationWrapper : public WorkDuration {
public:
@@ -81,14 +81,17 @@
return;
}
- while (state.KeepRunning()) {
+ for (auto _ : state) {
ret = (*hal.*fn)(std::forward<Args1>(args1)...);
- state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
- if (delay > 0us) {
- testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ if (!ret.isOk()) {
+ state.SkipWithError(ret.getDescription().c_str());
+ break;
}
- state.ResumeTiming();
+ if (delay > 0us) {
+ state.PauseTiming();
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ state.ResumeTiming();
+ }
}
}
@@ -123,14 +126,15 @@
return;
}
- while (state.KeepRunning()) {
+ for (auto _ : state) {
ret = (*session.*fn)(std::forward<Args1>(args1)...);
- state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
- if (ONEWAY_API_DELAY > 0us) {
- testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
- .count());
+ if (!ret.isOk()) {
+ state.SkipWithError(ret.getDescription().c_str());
+ break;
}
+ state.PauseTiming();
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY)
+ .count());
state.ResumeTiming();
}
session->close();
@@ -150,11 +154,41 @@
static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) {
Boost boost = static_cast<Boost>(state.range(0));
+ bool isSupported;
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
+
+ if (hal == nullptr) {
+ ALOGV("Power HAL not available, skipping test...");
+ state.SkipWithMessage("Power HAL unavailable");
+ return;
+ }
+
+ ndk::ScopedAStatus ret = hal->isBoostSupported(boost, &isSupported);
+ if (!ret.isOk() || !isSupported) {
+ state.SkipWithMessage("operation unsupported");
+ return;
+ }
+
runBenchmark(state, ONEWAY_API_DELAY, &IPower::setBoost, boost, 1);
}
static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) {
Mode mode = static_cast<Mode>(state.range(0));
+ bool isSupported;
+ std::shared_ptr<IPower> hal = PowerHalLoader::loadAidl();
+
+ if (hal == nullptr) {
+ ALOGV("Power HAL not available, skipping test...");
+ state.SkipWithMessage("Power HAL unavailable");
+ return;
+ }
+
+ ndk::ScopedAStatus ret = hal->isModeSupported(mode, &isSupported);
+ if (!ret.isOk() || !isSupported) {
+ state.SkipWithMessage("operation unsupported");
+ return;
+ }
+
runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false);
}
@@ -178,12 +212,20 @@
ALOGV("Power HAL does not support this operation, skipping test...");
state.SkipWithMessage("operation unsupported");
return;
+ } else if (!ret.isOk()) {
+ state.SkipWithError(ret.getDescription().c_str());
+ return;
+ } else {
+ appSession->close();
}
- while (state.KeepRunning()) {
+ for (auto _ : state) {
ret = hal->createHintSession(tgid, uid, threadIds, durationNanos, &appSession);
+ if (!ret.isOk()) {
+ state.SkipWithError(ret.getDescription().c_str());
+ break;
+ }
state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.getDescription().c_str());
appSession->close();
state.ResumeTiming();
}
diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
index effddda..0fda686 100644
--- a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp
@@ -19,9 +19,9 @@
#include <aidl/android/hardware/power/Boost.h>
#include <aidl/android/hardware/power/Mode.h>
#include <benchmark/benchmark.h>
+#include <chrono>
#include <powermanager/PowerHalController.h>
#include <testUtil.h>
-#include <chrono>
using aidl::android::hardware::power::Boost;
using aidl::android::hardware::power::Mode;
@@ -32,10 +32,10 @@
using namespace std::chrono_literals;
// Values from Boost.aidl and Mode.aidl.
-static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION);
-static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT);
-static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE);
-static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH);
+static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(*ndk::enum_range<Boost>().begin());
+static constexpr int64_t LAST_BOOST = static_cast<int64_t>(*(ndk::enum_range<Boost>().end()-1));
+static constexpr int64_t FIRST_MODE = static_cast<int64_t>(*ndk::enum_range<Mode>().begin());
+static constexpr int64_t LAST_MODE = static_cast<int64_t>(*(ndk::enum_range<Mode>().end()-1));
// Delay between oneway method calls to avoid overflowing the binder buffers.
static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us;
@@ -43,11 +43,27 @@
template <typename T, class... Args0, class... Args1>
static void runBenchmark(benchmark::State& state, HalResult<T> (PowerHalController::*fn)(Args0...),
Args1&&... args1) {
- while (state.KeepRunning()) {
- PowerHalController controller;
+ PowerHalController initController;
+ HalResult<T> result = (initController.*fn)(std::forward<Args1>(args1)...);
+ if (result.isFailed()) {
+ state.SkipWithError(result.errorMessage());
+ return;
+ } else if (result.isUnsupported()) {
+ ALOGV("Power HAL does not support this operation, skipping test...");
+ state.SkipWithMessage("operation unsupported");
+ return;
+ }
+
+ for (auto _ : state) {
+ PowerHalController controller; // new controller to avoid caching
HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
+ if (ret.isFailed()) {
+ state.SkipWithError(ret.errorMessage());
+ break;
+ }
state.PauseTiming();
- if (ret.isFailed()) state.SkipWithError("Power HAL request failed");
+ testDelaySpin(
+ std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
state.ResumeTiming();
}
}
@@ -57,22 +73,27 @@
HalResult<T> (PowerHalController::*fn)(Args0...), Args1&&... args1) {
PowerHalController controller;
// First call out of test, to cache HAL service and isSupported result.
- (controller.*fn)(std::forward<Args1>(args1)...);
+ HalResult<T> result = (controller.*fn)(std::forward<Args1>(args1)...);
+ if (result.isFailed()) {
+ state.SkipWithError(result.errorMessage());
+ return;
+ } else if (result.isUnsupported()) {
+ ALOGV("Power HAL does not support this operation, skipping test...");
+ state.SkipWithMessage("operation unsupported");
+ return;
+ }
- while (state.KeepRunning()) {
+ for (auto _ : state) {
HalResult<T> ret = (controller.*fn)(std::forward<Args1>(args1)...);
- state.PauseTiming();
if (ret.isFailed()) {
- state.SkipWithError("Power HAL request failed");
+ state.SkipWithError(ret.errorMessage());
+ break;
}
- testDelaySpin(
- std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count());
- state.ResumeTiming();
}
}
static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) {
- while (state.KeepRunning()) {
+ for (auto _ : state) {
PowerHalController controller;
controller.init();
}
@@ -90,12 +111,12 @@
static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) {
Boost boost = static_cast<Boost>(state.range(0));
- runBenchmark(state, &PowerHalController::setBoost, boost, 0);
+ runBenchmark(state, &PowerHalController::setBoost, boost, 1);
}
static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) {
Boost boost = static_cast<Boost>(state.range(0));
- runCachedBenchmark(state, &PowerHalController::setBoost, boost, 0);
+ runCachedBenchmark(state, &PowerHalController::setBoost, boost, 1);
}
static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) {
diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
index bcb376b..95fd0c2 100644
--- a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
+++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp
@@ -54,14 +54,17 @@
return;
}
- while (state.KeepRunning()) {
+ for (auto _ : state) {
Return<R> ret = (*hal.*fn)(std::forward<Args1>(args1)...);
- state.PauseTiming();
- if (!ret.isOk()) state.SkipWithError(ret.description().c_str());
- if (delay > 0us) {
- testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ if (!ret.isOk()) {
+ state.SkipWithError(ret.description().c_str());
+ break;
}
- state.ResumeTiming();
+ if (delay > 0us) {
+ state.PauseTiming();
+ testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count());
+ state.ResumeTiming();
+ }
}
}
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 682d1f4..1c53496 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -26,9 +26,9 @@
#include <utils/Log.h>
#include <unistd.h>
+#include <memory>
#include <thread>
-
using android::binder::Status;
using namespace aidl::android::hardware::power;
@@ -347,3 +347,50 @@
result = mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
ASSERT_TRUE(result.isUnsupported());
}
+
+TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionData) {
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ std::vector<hal::CompositionData> dataOut;
+ dataOut.emplace_back(hal::CompositionData{
+ .timestampNanos = 0L,
+ .scheduledPresentTimestampsNanos = {100},
+ .latchTimestampNanos = 50,
+ .outputIds = {0},
+ });
+ dataOut.emplace_back(hal::CompositionData{
+ .timestampNanos = 200L,
+ .scheduledPresentTimestampsNanos = {300},
+ .latchTimestampNanos = 250,
+ .outputIds = {0},
+ });
+ EXPECT_CALL(*mMockHal.get(), sendCompositionData(_))
+ .Times(Exactly(1))
+ .WillOnce([&](const std::vector<hal::CompositionData>& passedData) {
+ if (!std::equal(passedData.begin(), passedData.end(), dataOut.begin())) {
+ ADD_FAILURE() << "Passed composition data not the same";
+ }
+ return ndk::ScopedAStatus::ok();
+ });
+
+ ASSERT_TRUE(mWrapper->sendCompositionData(dataOut).isOk());
+}
+
+TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionUpdate) {
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ hal::CompositionUpdate dataOut{
+ .timestampNanos = 123,
+ .deadOutputIds = {1, 2, 3},
+ };
+ EXPECT_CALL(*mMockHal.get(), sendCompositionUpdate(_))
+ .Times(Exactly(1))
+ .WillOnce([&](const hal::CompositionUpdate& passedData) {
+ if (passedData != dataOut) {
+ ADD_FAILURE() << "Passed composition update data not the same";
+ }
+ return ndk::ScopedAStatus::ok();
+ });
+
+ ASSERT_TRUE(mWrapper->sendCompositionUpdate(dataOut).isOk());
+}
\ No newline at end of file
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 8c80dd8..9ecd101 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -11,7 +11,7 @@
name: "sensorservice_flags",
package: "com.android.frameworks.sensorservice.flags",
container: "system",
- srcs: ["senserservice_flags.aconfig"],
+ srcs: ["sensorservice_flags.aconfig"],
}
cc_aconfig_library {
@@ -61,38 +61,38 @@
],
shared_libs: [
- "libcutils",
- "libhardware",
- "libhardware_legacy",
- "libutils",
- "liblog",
- "libactivitymanager_aidl",
- "libbatterystats_aidl",
- "libbinder",
- "libsensor",
- "libsensorprivacy",
- "libpermission",
- "libprotoutil",
- "libcrypto",
- "libbase",
- "libhidlbase",
- "libfmq",
- "libbinder_ndk",
- "packagemanager_aidl-cpp",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
"android.hardware.sensors@1.0",
"android.hardware.sensors@2.0",
"android.hardware.sensors@2.1",
- "android.hardware.common-V2-ndk",
- "android.hardware.common.fmq-V1-ndk",
- "server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libactivitymanager_aidl",
+ "libbase",
+ "libbatterystats_aidl",
+ "libbinder",
+ "libbinder_ndk",
+ "libcrypto",
+ "libcutils",
+ "libfmq",
+ "libhardware",
+ "libhardware_legacy",
+ "libhidlbase",
+ "liblog",
+ "libpermission",
+ "libprotoutil",
+ "libsensor",
+ "libsensorprivacy",
+ "libutils",
+ "packagemanager_aidl-cpp",
+ "server_configurable_flags",
],
static_libs: [
- "libaidlcommonsupport",
- "android.hardware.sensors@1.0-convert",
"android.hardware.sensors-V1-convert",
"android.hardware.sensors-V3-ndk",
+ "android.hardware.sensors@1.0-convert",
+ "libaidlcommonsupport",
"sensorservice_flags_c_lib",
],
@@ -100,9 +100,9 @@
export_shared_lib_headers: [
"libactivitymanager_aidl",
+ "libpermission",
"libsensor",
"libsensorprivacy",
- "libpermission",
],
afdo: true,
@@ -120,9 +120,9 @@
srcs: ["main_sensorservice.cpp"],
shared_libs: [
- "libsensorservice",
- "libsensorprivacy",
"libbinder",
+ "libsensorprivacy",
+ "libsensorservice",
"libutils",
],
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/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 880df08..f38cf5a 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -22,6 +22,7 @@
"android.hardware.sensors-V1-convert",
"android.hardware.sensors-V3-ndk",
"android.hardware.common-V2-ndk",
+ "framework-permission-aidl-cpp",
"libsensor",
"libfakeservicemanager",
"libcutils",
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/sensorservice_flags.aconfig
similarity index 66%
rename from services/sensorservice/senserservice_flags.aconfig
rename to services/sensorservice/sensorservice_flags.aconfig
index 7abfbaa..6308973 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/sensorservice_flags.aconfig
@@ -28,3 +28,23 @@
description: "When this flag is enabled, sensor service will only erase dynamic sensor data at the end of the threadLoop to prevent race condition."
bug: "329020894"
}
+
+flag {
+ name: "enforce_permissions_for_all_target_sdk"
+ namespace: "sensors"
+ description: "When this flag is enabled, sensor service will enforce permissions for all target sdks."
+ bug: "389176817"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "disable_vndk_forged_name"
+ namespace: "sensors"
+ description: "When this flag is enabled, sensor manager will not use forged name to determine if an access is from VNDK"
+ bug: "398253250"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
index f698515..d588017 100644
--- a/services/stats/Android.bp
+++ b/services/stats/Android.bp
@@ -29,10 +29,15 @@
"libexpresslog",
"libhidlbase",
"liblog",
- "libstatslog",
"libstatssocket",
"libutils",
],
+ generated_sources: [
+ "statslog_hidl.cpp",
+ ],
+ generated_headers: [
+ "statslog_hidl.h",
+ ],
export_include_dirs: [
"include/",
],
@@ -47,3 +52,28 @@
"android.frameworks.stats-service.xml",
],
}
+
+genrule {
+ name: "statslog_hidl.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen)" +
+ " --header $(genDir)/statslog_hidl.h" +
+ " --module statshidl" +
+ " --namespace android,util,statshidl",
+ out: [
+ "statslog_hidl.h",
+ ],
+}
+
+genrule {
+ name: "statslog_hidl.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen)" +
+ " --cpp $(genDir)/statslog_hidl.cpp" +
+ " --module statshidl" +
+ " --namespace android,util,statshidl" +
+ " --importHeader statslog_hidl.h",
+ out: [
+ "statslog_hidl.cpp",
+ ],
+}
diff --git a/services/stats/OWNERS b/services/stats/OWNERS
index a61babf..791b711 100644
--- a/services/stats/OWNERS
+++ b/services/stats/OWNERS
@@ -1,9 +1,7 @@
jeffreyhuang@google.com
joeo@google.com
-jtnguyen@google.com
muhammadq@google.com
ruchirr@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@google.com
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index b22f903..66f7682 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -26,9 +26,8 @@
#include <log/log.h>
#include <stats_annotations.h>
#include <stats_event.h>
-#include <statslog.h>
-#include <unordered_map>
+#include <map>
namespace {
static const char* g_AtomErrorMetricName =
@@ -118,8 +117,8 @@
}
}
- // populate map for quickier access for VendorAtomValue associated annotations by value index
- std::unordered_map<int, int> fieldIndexToAnnotationSetMap;
+ // populate map for quicker access for VendorAtomValue associated annotations by value index
+ std::map<int, int> fieldIndexToAnnotationSetMap;
if (vendorAtom.valuesAnnotations) {
const std::vector<std::optional<AnnotationSet>>& valuesAnnotations =
*vendorAtom.valuesAnnotations;
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index 19176d9..0ffa4c3 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -20,7 +20,7 @@
#include "StatsHal.h"
#include <log/log.h>
-#include <statslog.h>
+#include <statslog_hidl.h>
namespace android {
namespace frameworks {
@@ -32,24 +32,27 @@
}
hardware::Return<void> StatsHal::reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) {
- android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
- speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
+ android::util::statshidl::stats_write(android::util::statshidl::SPEAKER_IMPEDANCE_REPORTED,
+ speakerImpedance.speakerLocation,
+ speakerImpedance.milliOhms);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
- android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
- hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
+ android::util::statshidl::stats_write(
+ android::util::statshidl::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
+ hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
return hardware::Void();
}
hardware::Return<void> StatsHal::reportPhysicalDropDetected(
const PhysicalDropDetected& physicalDropDetected) {
- android::util::stats_write(
- android::util::PHYSICAL_DROP_DETECTED, int32_t(physicalDropDetected.confidencePctg),
- physicalDropDetected.accelPeak, physicalDropDetected.freefallDuration);
+ android::util::statshidl::stats_write(android::util::statshidl::PHYSICAL_DROP_DETECTED,
+ int32_t(physicalDropDetected.confidencePctg),
+ physicalDropDetected.accelPeak,
+ physicalDropDetected.freefallDuration);
return hardware::Void();
}
@@ -60,19 +63,19 @@
for (int i = 0; i < 10 - initialSize; i++) {
buckets.push_back(0); // Push 0 for buckets that do not exist.
}
- android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
- buckets[2], buckets[3], buckets[4], buckets[5], buckets[6],
- buckets[7], buckets[8], buckets[9]);
+ android::util::statshidl::stats_write(
+ android::util::statshidl::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], buckets[2],
+ buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], buckets[9]);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportBatteryHealthSnapshot(
const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
- android::util::stats_write(
- android::util::BATTERY_HEALTH_SNAPSHOT, int32_t(batteryHealthSnapshotArgs.type),
- batteryHealthSnapshotArgs.temperatureDeciC, batteryHealthSnapshotArgs.voltageMicroV,
- batteryHealthSnapshotArgs.currentMicroA,
+ android::util::statshidl::stats_write(
+ android::util::statshidl::BATTERY_HEALTH_SNAPSHOT,
+ int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
+ batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
@@ -80,23 +83,24 @@
}
hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) {
- android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count);
+ android::util::statshidl::stats_write(android::util::statshidl::SLOW_IO,
+ int32_t(slowIo.operation), slowIo.count);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportBatteryCausedShutdown(
const BatteryCausedShutdown& batteryCausedShutdown) {
- android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
- batteryCausedShutdown.voltageMicroV);
+ android::util::statshidl::stats_write(android::util::statshidl::BATTERY_CAUSED_SHUTDOWN,
+ batteryCausedShutdown.voltageMicroV);
return hardware::Void();
}
hardware::Return<void> StatsHal::reportUsbPortOverheatEvent(
const UsbPortOverheatEvent& usbPortOverheatEvent) {
- android::util::stats_write(
- android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
+ android::util::statshidl::stats_write(
+ android::util::statshidl::USB_PORT_OVERHEAT_EVENT_REPORTED,
usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
usbPortOverheatEvent.timeToInactive);
@@ -105,9 +109,10 @@
}
hardware::Return<void> StatsHal::reportSpeechDspStat(const SpeechDspStat& speechDspStat) {
- android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
- speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
- speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
+ android::util::statshidl::stats_write(
+ android::util::statshidl::SPEECH_DSP_STAT_REPORTED, speechDspStat.totalUptimeMillis,
+ speechDspStat.totalDowntimeMillis, speechDspStat.totalCrashCount,
+ speechDspStat.totalRecoverCount);
return hardware::Void();
}
diff --git a/services/surfaceflinger/ActivePictureTracker.cpp b/services/surfaceflinger/ActivePictureTracker.cpp
new file mode 100644
index 0000000..4e6fa66
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureTracker.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#include "ActivePictureTracker.h"
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "LayerFE.h"
+
+namespace android {
+
+using gui::ActivePicture;
+using gui::IActivePictureListener;
+
+void ActivePictureTracker::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+ const CompositionResult& result) {
+ if (result.wasPictureProfileCommitted) {
+ gui::ActivePicture picture;
+ picture.layerId = int32_t(layer.sequence);
+ picture.ownerUid = int32_t(layer.getOwnerUid());
+ // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+ if (layerFE.getCompositionState()) {
+ picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
+ } else {
+ picture.pictureProfileId = result.pictureProfileHandle.getId();
+ }
+ mNewActivePictures.push_back(picture);
+ }
+}
+
+void ActivePictureTracker::updateAndNotifyListeners(const Listeners& listenersToAdd,
+ const Listeners& listenersToRemove) {
+ Listeners newListeners = updateListeners(listenersToAdd, listenersToRemove);
+ if (updateAndHasChanged()) {
+ for (auto listener : mListeners) {
+ listener->onActivePicturesChanged(getActivePictures());
+ }
+ } else {
+ for (auto listener : newListeners) {
+ listener->onActivePicturesChanged(getActivePictures());
+ }
+ }
+}
+
+ActivePictureTracker::Listeners ActivePictureTracker::updateListeners(
+ const Listeners& listenersToAdd, const Listeners& listenersToRemove) {
+ Listeners newListeners;
+ for (auto listener : listenersToRemove) {
+ std::erase_if(mListeners, [listener](const sp<IActivePictureListener>& otherListener) {
+ return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+ });
+ }
+ for (auto listener : listenersToAdd) {
+ if (std::find_if(mListeners.begin(), mListeners.end(),
+ [listener](const sp<IActivePictureListener>& otherListener) {
+ return IInterface::asBinder(listener) ==
+ IInterface::asBinder(otherListener);
+ }) == mListeners.end()) {
+ newListeners.push_back(listener);
+ }
+ }
+ for (auto listener : newListeners) {
+ mListeners.push_back(listener);
+ }
+ return newListeners;
+}
+
+bool ActivePictureTracker::updateAndHasChanged() {
+ bool hasChanged = true;
+ if (mNewActivePictures.size() == mOldActivePictures.size()) {
+ auto compare = [](const ActivePicture& lhs, const ActivePicture& rhs) -> int {
+ if (lhs.layerId == rhs.layerId) {
+ return lhs.pictureProfileId < rhs.pictureProfileId;
+ }
+ return lhs.layerId < rhs.layerId;
+ };
+ std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
+ if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
+ mOldActivePictures.begin())) {
+ hasChanged = false;
+ }
+ }
+ std::swap(mOldActivePictures, mNewActivePictures);
+ mNewActivePictures.resize(0);
+ return hasChanged;
+}
+
+const std::vector<ActivePicture>& ActivePictureTracker::getActivePictures() const {
+ return mOldActivePictures;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureTracker.h
similarity index 76%
rename from services/surfaceflinger/ActivePictureUpdater.h
rename to services/surfaceflinger/ActivePictureTracker.h
index 20779bb..cb319a5 100644
--- a/services/surfaceflinger/ActivePictureUpdater.h
+++ b/services/surfaceflinger/ActivePictureTracker.h
@@ -19,6 +19,7 @@
#include <vector>
#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
namespace android {
@@ -27,21 +28,28 @@
struct CompositionResult;
// Keeps track of active pictures - layers that are undergoing picture processing.
-class ActivePictureUpdater {
+class ActivePictureTracker {
public:
+ typedef std::vector<sp<gui::IActivePictureListener>> Listeners;
+
// Called for each visible layer when SurfaceFlinger finishes composing.
void onLayerComposed(const Layer& layer, const LayerFE& layerFE,
const CompositionResult& result);
// Update internals and return whether the set of active pictures have changed.
- bool updateAndHasChanged();
+ void updateAndNotifyListeners(const Listeners& activePictureListenersToAdd,
+ const Listeners& activePictureListenersToRemove);
// The current set of active pictures.
const std::vector<gui::ActivePicture>& getActivePictures() const;
private:
+ Listeners updateListeners(const Listeners& listenersToAdd, const Listeners& listenersToRemove);
+ bool updateAndHasChanged();
+
std::vector<gui::ActivePicture> mOldActivePictures;
std::vector<gui::ActivePicture> mNewActivePictures;
+ Listeners mListeners;
};
} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp
deleted file mode 100644
index 210e948..0000000
--- a/services/surfaceflinger/ActivePictureUpdater.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-
-#include "ActivePictureUpdater.h"
-
-#include <algorithm>
-
-#include "Layer.h"
-#include "LayerFE.h"
-
-namespace android {
-
-void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
- const CompositionResult& result) {
- if (result.wasPictureProfileCommitted) {
- gui::ActivePicture picture;
- picture.layerId = int32_t(layer.sequence);
- picture.ownerUid = int32_t(layer.getOwnerUid());
- // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
- if (layerFE.getCompositionState()) {
- picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
- } else {
- picture.pictureProfileId = result.pictureProfileHandle.getId();
- }
- mNewActivePictures.push_back(picture);
- }
-}
-
-bool ActivePictureUpdater::updateAndHasChanged() {
- bool hasChanged = true;
- if (mNewActivePictures.size() == mOldActivePictures.size()) {
- auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
- if (lhs.layerId == rhs.layerId) {
- return lhs.pictureProfileId < rhs.pictureProfileId;
- }
- return lhs.layerId < rhs.layerId;
- };
- std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
- if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
- mOldActivePictures.begin())) {
- hasChanged = false;
- }
- }
- std::swap(mOldActivePictures, mNewActivePictures);
- mNewActivePictures.resize(0);
- return hasChanged;
-}
-
-const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const {
- return mOldActivePictures;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 92fae1e..22c6092 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",
],
}
@@ -42,59 +42,58 @@
name: "libsurfaceflinger_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "android.hardware.power-ndk_shared",
"librenderengine_deps",
- "libtimestats_deps",
"libsurfaceflinger_common_deps",
- "surfaceflinger_defaults",
"surfaceflinger_qcom_ext_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",
"libnativewindow",
- "libpowermanager",
"libprocessgroup",
"libprotobuf-cpp-lite",
"libstatslog_surfaceflinger",
"libsync",
"libui",
"libutils",
- "libSurfaceFlingerProp",
- "libaconfig_storage_read_api_cc",
],
static_libs: [
"iinputflinger_aidl_lib_static",
"libaidlcommonsupport",
"libcompositionengine",
- "libframetimeline",
"libgui_aidl_static",
"libperfetto_client_experimental",
"librenderengine",
@@ -106,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",
@@ -126,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
@@ -178,7 +177,6 @@
filegroup {
name: "libsurfaceflinger_backend_sources",
srcs: [
- "PowerAdvisor/*.cpp",
"DisplayHardware/AidlComposerHal.cpp",
"DisplayHardware/ComposerHal.cpp",
"DisplayHardware/FramebufferSurface.cpp",
@@ -186,6 +184,7 @@
"DisplayHardware/HWComposer.cpp",
"DisplayHardware/HidlComposerHal.cpp",
"DisplayHardware/VirtualDisplaySurface.cpp",
+ "PowerAdvisor/*.cpp",
],
}
@@ -200,45 +199,42 @@
name: "libsurfaceflinger_sources",
srcs: [
":libsurfaceflinger_backend_sources",
- "ActivePictureUpdater.cpp",
+ "ActivePictureTracker.cpp",
"BackgroundExecutor.cpp",
"Client.cpp",
"ClientCache.cpp",
"Display/DisplayModeController.cpp",
"Display/DisplaySnapshot.cpp",
"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",
+ "FrameTimeline/FrameTimeline.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",
- "LayerRenderArea.cpp",
"LayerVector.cpp",
"NativeWindowSurface.cpp",
"RefreshRateOverlay.cpp",
"RegionSamplingThread.cpp",
- "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",
@@ -254,19 +250,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\"",
@@ -332,9 +329,9 @@
"android.hardware.configstore@1.1",
"android.hardware.graphics.common@1.2",
"libhidlbase",
+ "liblog",
"libui",
"libutils",
- "liblog",
],
static_libs: [
"libSurfaceFlingerProperties",
@@ -355,10 +352,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/Client.cpp b/services/surfaceflinger/Client.cpp
index abeb2a9..6088e25 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -53,7 +53,6 @@
const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
gui::CreateSurfaceResult* outResult) {
// We rely on createLayer to check permissions.
- sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
static_cast<uint32_t>(flags), std::move(metadata));
args.parentHandle = parent;
@@ -101,7 +100,6 @@
binder::Status Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle,
gui::CreateSurfaceResult* outResult) {
- sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), "MirrorRoot",
0 /* flags */, gui::LayerMetadata());
status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, *outResult);
@@ -109,12 +107,11 @@
}
binder::Status Client::mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) {
- sp<IBinder> handle;
LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this),
"MirrorRoot-" + std::to_string(displayId), 0 /* flags */,
gui::LayerMetadata());
- std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
- status_t status = mFlinger->mirrorDisplay(*id, args, *outResult);
+ const DisplayId id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
+ status_t status = mFlinger->mirrorDisplay(id, args, *outResult);
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 2c3e50c..f277439 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -13,11 +13,11 @@
defaults: [
"aconfig_lib_cc_static_link.defaults",
"android.hardware.graphics.composer3-ndk_shared",
- "android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
"libsurfaceflinger_proto_deps",
+ "poweradvisor_deps",
],
cflags: [
"-DLOG_TAG=\"CompositionEngine\"",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index e32cc02..fd58191 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -53,7 +53,7 @@
createLayerFECompositionState() = 0;
virtual HWComposer& getHwComposer() const = 0;
- virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
+ virtual void setHwComposer(HWComposer*) = 0;
virtual renderengine::RenderEngine& getRenderEngine() const = 0;
virtual void setRenderEngine(renderengine::RenderEngine*) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 39748b8..acd9154 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -37,6 +37,9 @@
// Gets the DisplayId for the display
virtual DisplayId getId() const = 0;
+ // True if the display has a secure layer
+ virtual bool hasSecureLayers() const = 0;
+
// True if the display is secure
virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 252adaa..2c0a66f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -34,7 +34,7 @@
* A parameter object for creating Display instances
*/
struct DisplayCreationArgs {
- DisplayId id;
+ DisplayIdVariant idVariant;
// Size of the display in pixels
ui::Size pixels = ui::kInvalidSize;
@@ -68,8 +68,8 @@
public:
DisplayCreationArgs build() { return std::move(mArgs); }
- DisplayCreationArgsBuilder& setId(DisplayId id) {
- mArgs.id = id;
+ DisplayCreationArgsBuilder& setId(DisplayIdVariant idVariant) {
+ mArgs.idVariant = idVariant;
return *this;
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index cda4edc..e2ea0f1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -19,6 +19,7 @@
#include <optional>
#include <ostream>
#include <unordered_set>
+#include "aidl/android/hardware/graphics/composer3/Composition.h"
#include "ui/LayerStack.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -121,6 +122,8 @@
// True if layers with 170M dataspace should be overridden to sRGB.
const bool treat170mAsSrgb;
+
+ std::shared_ptr<gui::DisplayLuts> luts;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
@@ -131,6 +134,9 @@
// Currently latched frame number, 0 if invalid.
uint64_t frameNumber = 0;
+
+ // layer serial number, -1 if invalid.
+ int32_t sequence = -1;
};
// Describes the states of the release fence. Checking the states allows checks
@@ -161,6 +167,8 @@
// Checks if the buffer's release fence has been set
virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
+ virtual void setReleasedBuffer(sp<GraphicBuffer> buffer) = 0;
+
// Indicates that the picture profile request was applied to this layer.
virtual void onPictureProfileCommitted() = 0;
@@ -173,6 +181,28 @@
// Whether the layer should be rendered with rounded corners.
virtual bool hasRoundedCorners() const = 0;
virtual void setWasClientComposed(const sp<Fence>&) {}
+
+ // These fields are all copied from the last written HWC state.
+ // This state is only used for debugging purposes.
+ struct HwcLayerDebugState {
+ aidl::android::hardware::graphics::composer3::Composition lastCompositionType =
+ aidl::android::hardware::graphics::composer3::Composition::INVALID;
+ // Corresponds to passing an alpha of 0 to HWC2::Layer::setPlaneAlpha.
+ bool wasSkipped = false;
+
+ // Indicates whether the compositionengine::OutputLayer had properties overwritten.
+ // Not directly passed to HWC.
+ bool wasOverridden = false;
+
+ // Corresponds to the GraphicBuffer ID of the buffer passed to HWC2::Layer::setBuffer.
+ // This buffer corresponds to a CachedSet that the LayerFE was flattened to.
+ uint64_t overrideBufferId = 0;
+ };
+
+ // Used for debugging purposes, e.g. perfetto tracing, dumpsys.
+ virtual void setLastHwcState(const LayerFE::HwcLayerDebugState &hwcState) = 0;
+ virtual const HwcLayerDebugState &getLastHwcState() const = 0;
+
virtual const gui::LayerMetadata* getMetadata() const = 0;
virtual const gui::LayerMetadata* getRelativeMetadata() const = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index fb8fed0..34b0bb5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,6 +18,7 @@
#include <cstdint>
+#include <android/gui/BorderSettings.h>
#include <android/gui/CachingHint.h>
#include <gui/DisplayLuts.h>
#include <gui/HdrMetadata.h>
@@ -141,6 +142,9 @@
ShadowSettings shadowSettings;
+ // The settings to configure the outline of a layer.
+ gui::BorderSettings borderSettings;
+
// List of regions that require blur
std::vector<BlurRegion> blurRegions;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index bda7856..4266da4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -170,6 +170,7 @@
// Returns the DisplayId the output represents, if it has one
virtual ftl::Optional<DisplayId> getDisplayId() const = 0;
+ virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const = 0;
// Enables (or disables) composition on this output
virtual void setCompositionEnabled(bool) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 2e7a7d9..c0243b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -118,7 +118,8 @@
// isPeekingThrough specifies whether this layer will be shown through a
// hole punch in a layer above it.
virtual void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
- bool zIsOverridden, bool isPeekingThrough) = 0;
+ bool zIsOverridden, bool isPeekingThrough,
+ bool isLutSupported) = 0;
// Updates the cursor position with the HWC
virtual void writeCursorPositionToHWC() const = 0;
@@ -144,7 +145,7 @@
// Applies a HWC device layer lut
virtual void applyDeviceLayerLut(
- ndk::ScopedFileDescriptor,
+ ::android::base::unique_fd,
std::vector<std::pair<
int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 45208dd..2992b6d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -31,7 +31,7 @@
override;
HWComposer& getHwComposer() const override;
- void setHwComposer(std::unique_ptr<HWComposer>) override;
+ void setHwComposer(HWComposer*) override;
renderengine::RenderEngine& getRenderEngine() const override;
void setRenderEngine(renderengine::RenderEngine*) override;
@@ -59,7 +59,7 @@
void setNeedsAnotherUpdateForTest(bool);
private:
- std::unique_ptr<HWComposer> mHwComposer;
+ HWComposer* mHwComposer;
renderengine::RenderEngine* mRenderEngine;
std::shared_ptr<TimeStats> mTimeStats;
bool mNeedsAnotherUpdate = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 5519aaf..6ec7be8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -46,6 +46,7 @@
// compositionengine::Output overrides
ftl::Optional<DisplayId> getDisplayId() const override;
+ ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override;
bool isValid() const override;
void dump(std::string&) const override;
using compositionengine::impl::Output::setReleasedLayers;
@@ -67,7 +68,9 @@
// compositionengine::Display overrides
DisplayId getId() const override;
+ bool hasSecureLayers() const override;
bool isSecure() const override;
+ void setSecure(bool secure) override;
bool isVirtual() const override;
void disconnect() override;
void createDisplayColorProfile(
@@ -75,7 +78,6 @@
void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
void createClientCompositionCache(uint32_t cacheSize) override;
void applyDisplayBrightness(bool applyImmediately) override;
- void setSecure(bool secure) override;
// Internal helpers used by chooseCompositionStrategy()
using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
@@ -104,8 +106,11 @@
override;
bool hasPictureProcessing() const override;
int32_t getMaxLayerPictureProfiles() const override;
+ bool isGpuVirtualDisplay() const {
+ return std::holds_alternative<GpuVirtualDisplayId>(mIdVariant);
+ }
- DisplayId mId;
+ DisplayIdVariant mIdVariant;
bool mIsDisconnected = false;
adpf::PowerAdvisor* mPowerAdvisor = nullptr;
bool mHasPictureProcessing = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 0ccdd22..873764b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -45,6 +45,7 @@
// compositionengine::Output overrides
bool isValid() const override;
ftl::Optional<DisplayId> getDisplayId() const override;
+ ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override;
void setCompositionEnabled(bool) override;
void setLayerCachingEnabled(bool) override;
void setLayerCachingTexturePoolEnabled(bool) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 712b551..efddc85 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -58,7 +58,7 @@
const std::optional<std::vector<std::optional<LutProperties>>>
properties = std::nullopt) override;
void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
- bool isPeekingThrough) override;
+ bool isPeekingThrough, bool hasLutsProperties) override;
void writeCursorPositionToHWC() const override;
HWC2::Layer* getHwcLayer() const override;
@@ -68,7 +68,7 @@
aidl::android::hardware::graphics::composer3::Composition) override;
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
- void applyDeviceLayerLut(ndk::ScopedFileDescriptor,
+ void applyDeviceLayerLut(::android::base::unique_fd,
std::vector<std::pair<int, LutProperties>>) override;
bool needsFiltering() const override;
std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index f934cb2..e42b9b1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -253,7 +253,6 @@
std::unordered_map<size_t, size_t> mFinalLayerCounts;
size_t mCachedSetCreationCount = 0;
size_t mCachedSetCreationCost = 0;
- std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
};
} // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index a1b7282..bb1a222 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -37,7 +37,7 @@
std::unique_ptr<compositionengine::LayerFECompositionState>());
MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
- MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
+ MOCK_METHOD1(setHwComposer, void(HWComposer*));
MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 46cb95e..2d51b71 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -32,6 +32,7 @@
virtual ~Display();
MOCK_CONST_METHOD0(getId, DisplayId());
+ MOCK_CONST_METHOD0(hasSecureLayers, bool());
MOCK_CONST_METHOD0(isSecure, bool());
MOCK_METHOD1(setSecure, void(bool));
MOCK_CONST_METHOD0(isVirtual, bool());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 272fa3e..f65a908 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -52,6 +52,7 @@
MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
+ MOCK_METHOD1(setReleasedBuffer, void(sp<GraphicBuffer>));
MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
@@ -59,6 +60,10 @@
MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
MOCK_METHOD0(onPictureProfileCommitted, void());
+ MOCK_METHOD(void, setLastHwcState,
+ (const HwcLayerDebugState&), (override));
+ MOCK_METHOD(const HwcLayerDebugState&, getLastHwcState,
+ (), (const, override));
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index f2c265a..eaa3dd3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -35,6 +35,7 @@
MOCK_CONST_METHOD0(isValid, bool());
MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>());
+ MOCK_CONST_METHOD0(getDisplayIdVariant, ftl::Optional<DisplayIdVariant>());
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD1(setLayerCachingEnabled, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 9333ebb..be36db6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -47,7 +47,7 @@
(bool, bool, ui::Transform::RotationFlags,
(const std::optional<std::vector<std::optional<
aidl::android::hardware::graphics::composer3::LutProperties>>>)));
- MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
+ MOCK_METHOD(void, writeStateToHWC, (bool, bool, uint32_t, bool, bool, bool));
MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
@@ -60,7 +60,7 @@
MOCK_CONST_METHOD0(needsFiltering, bool());
MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
MOCK_METHOD(void, applyDeviceLayerLut,
- (ndk::ScopedFileDescriptor,
+ (::android::base::unique_fd,
(std::vector<std::pair<
int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const));
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index d9018bc..dc84195 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -38,7 +38,8 @@
lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
lhs.backgroundBlurRadius == rhs.backgroundBlurRadius &&
lhs.stretchEffect == rhs.stretchEffect &&
- lhs.edgeExtensionEffect == rhs.edgeExtensionEffect;
+ lhs.edgeExtensionEffect == rhs.edgeExtensionEffect &&
+ lhs.whitePointNits == rhs.whitePointNits;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index cfcce47..ab2a03c 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -58,11 +58,11 @@
}
HWComposer& CompositionEngine::getHwComposer() const {
- return *mHwComposer.get();
+ return *mHwComposer;
}
-void CompositionEngine::setHwComposer(std::unique_ptr<HWComposer> hwComposer) {
- mHwComposer = std::move(hwComposer);
+void CompositionEngine::setHwComposer(HWComposer* hwComposer) {
+ mHwComposer = hwComposer;
}
renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
@@ -91,13 +91,13 @@
namespace {
void offloadOutputs(Outputs& outputs) {
- if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) {
+ if (outputs.size() < 2) {
return;
}
ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload;
for (const auto& output : outputs) {
- if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) {
+ if (!output->getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
// Not HWC-enabled, so it is always client-composited. No need to offload.
continue;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index e37ce0a..531cab6 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -52,7 +52,7 @@
Display::~Display() = default;
void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
- mId = args.id;
+ mIdVariant = args.idVariant;
mPowerAdvisor = args.powerAdvisor;
mHasPictureProcessing = args.hasPictureProcessing;
mMaxLayerPictureProfiles = args.maxLayerPictureProfiles;
@@ -67,7 +67,15 @@
}
DisplayId Display::getId() const {
- return mId;
+ return asDisplayId(mIdVariant);
+}
+
+bool Display::hasSecureLayers() const {
+ const auto layers = getOutputLayersOrderedByZ();
+ return std::any_of(layers.begin(), layers.end(), [](const auto& layer) {
+ const auto* state = layer->getLayerFE().getCompositionState();
+ return state && state->isSecure;
+ });
}
bool Display::isSecure() const {
@@ -79,11 +87,15 @@
}
bool Display::isVirtual() const {
- return mId.isVirtual();
+ return !std::holds_alternative<PhysicalDisplayId>(mIdVariant);
}
ftl::Optional<DisplayId> Display::getDisplayId() const {
- return mId;
+ return getId();
+}
+
+ftl::Optional<DisplayIdVariant> Display::getDisplayIdVariant() const {
+ return mIdVariant;
}
void Display::disconnect() {
@@ -93,14 +105,14 @@
mIsDisconnected = true;
- if (const auto id = HalDisplayId::tryCast(mId)) {
+ if (const auto id = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
getCompositionEngine().getHwComposer().disconnectDisplay(*id);
}
}
void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
Output::setColorTransform(args);
- const auto halDisplayId = HalDisplayId::tryCast(mId);
+ const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
return;
}
@@ -108,7 +120,7 @@
auto& hwc = getCompositionEngine().getHwComposer();
status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
- to_string(mId).c_str(), result);
+ to_string(*halDisplayId).c_str(), result);
}
void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -125,7 +137,7 @@
Output::setColorProfile(colorProfile);
- const auto physicalId = PhysicalDisplayId::tryCast(mId);
+ const auto physicalId = getDisplayIdVariant().and_then(asPhysicalDisplayId);
LOG_FATAL_IF(!physicalId);
getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
colorProfile.renderIntent);
@@ -133,7 +145,7 @@
void Display::dump(std::string& out) const {
const char* const type = isVirtual() ? "virtual" : "physical";
- base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(mId).c_str(), type,
+ base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(getId()).c_str(), type,
getName().c_str());
out.append("\n Composition Display State:\n");
@@ -157,7 +169,7 @@
const sp<compositionengine::LayerFE>& layerFE) const {
auto outputLayer = impl::createOutputLayer(*this, layerFE);
- if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+ if (const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
outputLayer && !mIsDisconnected && halDisplayId) {
auto& hwc = getCompositionEngine().getHwComposer();
auto hwcLayer = hwc.createLayer(*halDisplayId);
@@ -171,8 +183,7 @@
void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
Output::setReleasedLayers(refreshArgs);
- if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
- refreshArgs.layersWithQueuedFrames.empty()) {
+ if (mIsDisconnected || isGpuVirtualDisplay() || refreshArgs.layersWithQueuedFrames.empty()) {
return;
}
@@ -208,7 +219,7 @@
if (!getState().displayBrightness) {
return;
}
- if (auto displayId = PhysicalDisplayId::tryCast(mId)) {
+ if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
auto& hwc = getCompositionEngine().getHwComposer();
status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
getState().displayBrightnessNits,
@@ -226,7 +237,7 @@
Output::beginFrame();
// If we don't have a HWC display, then we are done.
- const auto halDisplayId = HalDisplayId::tryCast(mId);
+ const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
if (!halDisplayId) {
return;
}
@@ -244,7 +255,7 @@
}
// If we don't have a HWC display, then we are done.
- const auto halDisplayId = HalDisplayId::tryCast(mId);
+ const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
if (!halDisplayId) {
return false;
}
@@ -266,9 +277,9 @@
}
if (isPowerHintSessionEnabled()) {
- mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
- if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
- mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+ mPowerAdvisor->setHwcValidateTiming(getId(), hwcValidateStartTime, TimePoint::now());
+ if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
+ mPowerAdvisor->setSkippedValidate(*halDisplayId, hwc.getValidateSkipped(*halDisplayId));
}
}
@@ -292,7 +303,7 @@
bool Display::getSkipColorTransform() const {
auto& hwc = getCompositionEngine().getHwComposer();
- if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
return hwc.hasDisplayCapability(*halDisplayId,
DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
}
@@ -373,7 +384,7 @@
if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) {
if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) {
- layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()),
+ layer->applyDeviceLayerLut(::android::base::unique_fd(mapperIt->second.release()),
lutsIt->second);
}
}
@@ -383,7 +394,7 @@
}
void Display::executeCommands() {
- const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+ const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
if (mIsDisconnected || !halDisplayIdOpt) {
return;
}
@@ -394,7 +405,7 @@
compositionengine::Output::FrameFences Display::presentFrame() {
auto fences = impl::Output::presentFrame();
- const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+ const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
if (mIsDisconnected || !halDisplayIdOpt) {
return fences;
}
@@ -404,13 +415,13 @@
const TimePoint startTime = TimePoint::now();
if (isPowerHintSessionEnabled() && getState().earliestPresentTime) {
- mPowerAdvisor->setHwcPresentDelayedTime(mId, *getState().earliestPresentTime);
+ mPowerAdvisor->setHwcPresentDelayedTime(*halDisplayIdOpt, *getState().earliestPresentTime);
}
hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
if (isPowerHintSessionEnabled()) {
- mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now());
+ mPowerAdvisor->setHwcPresentTiming(*halDisplayIdOpt, startTime, TimePoint::now());
}
fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
@@ -433,8 +444,8 @@
void Display::setExpensiveRenderingExpected(bool enabled) {
Output::setExpensiveRenderingExpected(enabled);
- if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
- mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
+ if (mPowerAdvisor && !isGpuVirtualDisplay()) {
+ mPowerAdvisor->setExpensiveRenderingExpected(getId(), enabled);
}
}
@@ -449,15 +460,15 @@
// For ADPF GPU v0 this is expected to set start time to when the GPU commands are submitted with
// fence returned, i.e. when RenderEngine flushes the commands and returns the draw fence.
void Display::setHintSessionGpuStart(TimePoint startTime) {
- mPowerAdvisor->setGpuStartTime(mId, startTime);
+ mPowerAdvisor->setGpuStartTime(getId(), startTime);
}
void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
- mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
+ mPowerAdvisor->setGpuFenceTime(getId(), std::move(gpuFence));
}
void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) {
- mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine);
+ mPowerAdvisor->setRequiresRenderEngine(getId(), requiresRenderEngine);
}
const aidl::android::hardware::graphics::composer3::OverlayProperties*
@@ -478,7 +489,7 @@
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
+ if (isGpuVirtualDisplay() && !mustRecompose()) {
ALOGV("Skipping display composition");
return;
}
@@ -487,7 +498,7 @@
}
bool Display::supportsOffloadPresent() const {
- if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+ if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
auto& hwc = getCompositionEngine().getHwComposer();
return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 348111d..294b167 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -70,6 +70,9 @@
out.append(" ");
dumpVal(out, "shadowLength", shadowSettings.length);
+ out.append(" ");
+ dumpVal(out, "borderSettings", borderSettings.toString());
+
out.append("\n ");
dumpVal(out, "blend", toString(blendMode), blendMode);
dumpVal(out, "alpha", alpha);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ac252aa..cf0be8e 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -119,6 +119,10 @@
return {};
}
+ftl::Optional<DisplayIdVariant> Output::getDisplayIdVariant() const {
+ return {};
+}
+
const std::string& Output::getName() const {
return mName;
}
@@ -437,8 +441,8 @@
ftl::Future<std::monostate> Output::present(
const compositionengine::CompositionRefreshArgs& refreshArgs) {
const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
- return getDisplayId()
- .and_then(PhysicalDisplayId::tryCast)
+ return getDisplayIdVariant()
+ .and_then(asPhysicalDisplayId)
.and_then([&refreshArgs](PhysicalDisplayId id) {
return refreshArgs.frameTargets.get(id);
})
@@ -811,7 +815,7 @@
}
auto compare = [](const ::android::compositionengine::OutputLayer* lhs,
const ::android::compositionengine::OutputLayer* rhs) {
- return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority();
+ return lhs->getPictureProfilePriority() < rhs->getPictureProfilePriority();
};
std::priority_queue<::android::compositionengine::OutputLayer*,
std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)>
@@ -891,8 +895,8 @@
return;
}
- if (auto frameTargetPtrOpt = getDisplayId()
- .and_then(PhysicalDisplayId::tryCast)
+ if (auto frameTargetPtrOpt = getDisplayIdVariant()
+ .and_then(asPhysicalDisplayId)
.and_then([&refreshArgs](PhysicalDisplayId id) {
return refreshArgs.frameTargets.get(id);
})) {
@@ -910,6 +914,9 @@
applyPictureProfile();
+ auto* properties = getOverlaySupport();
+ bool hasLutsProperties = properties && properties->lutProperties.has_value();
+
compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
@@ -941,7 +948,7 @@
includeGeometry = true;
constexpr bool isPeekingThrough = true;
peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ,
- isPeekingThrough);
+ isPeekingThrough, hasLutsProperties);
outputLayerHash ^= android::hashCombine(
reinterpret_cast<uint64_t>(&peekThroughLayer->getLayerFE()),
z, includeGeometry, overrideZ, isPeekingThrough,
@@ -953,7 +960,8 @@
}
constexpr bool isPeekingThrough = false;
- layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
+ layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough,
+ hasLutsProperties);
if (!skipLayer) {
outputLayerHash ^= android::hashCombine(
reinterpret_cast<uint64_t>(&layer->getLayerFE()),
@@ -1399,7 +1407,8 @@
// or complex GPU shaders and it's expensive. We boost the GPU frequency so that
// GPU composition can finish in time. We must reset GPU frequency afterwards,
// because high frequency consumes extra battery.
- const bool expensiveRenderingExpected =
+ const bool expensiveBlurs = mLayerRequestingBackgroundBlur != nullptr;
+ const bool expensiveRenderingExpected = expensiveBlurs ||
std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
[outputDataspace =
clientCompositionDisplay.outputDataspace](const auto& layer) {
@@ -1574,7 +1583,9 @@
.clearContent = !clientComposition,
.blurSetting = blurSetting,
.whitePointNits = layerState.whitePointNits,
- .treat170mAsSrgb = outputState.treat170mAsSrgb};
+ .treat170mAsSrgb = outputState.treat170mAsSrgb,
+ .luts = layer->getState().hwc ? layer->getState().hwc->luts
+ : nullptr};
if (auto clientCompositionSettings =
layerFE.prepareClientComposition(targetSettings)) {
clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
@@ -1679,6 +1690,7 @@
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
layer->getLayerFE().setReleaseFence(releaseFence);
+ layer->getLayerFE().setReleasedBuffer(layer->getLayerFE().getCompositionState()->buffer);
}
// We've got a list of layers needing fences, that are disjoint with
@@ -1848,7 +1860,7 @@
if (!getDisplayId()) {
return;
}
- if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) {
+ if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
auto& hwc = getCompositionEngine().getHwComposer();
const status_t error =
hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index c21e7d1..e9151c7 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -238,6 +238,16 @@
geomLayerBounds.bottom += outset;
}
+ // Similar to above
+ if (layerState.forceClientComposition && layerState.borderSettings.strokeWidth > 0.0f) {
+ // Antialiasing should never add more than 2 pixels.
+ const auto outset = layerState.borderSettings.strokeWidth + 2;
+ geomLayerBounds.left -= outset;
+ geomLayerBounds.top -= outset;
+ geomLayerBounds.right += outset;
+ geomLayerBounds.bottom += outset;
+ }
+
geomLayerBounds = layerTransform.transform(geomLayerBounds);
FloatRect frame = reduce(geomLayerBounds, activeTransparentRegion);
frame = frame.intersect(outputState.layerStackSpace.getContent().toFloatRect());
@@ -370,8 +380,11 @@
layerFEState->buffer->getPixelFormat()))
: std::nullopt;
- auto hdrRenderType =
- getHdrRenderType(outputState.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+ // prefer querying this from gralloc instead to catch 2094-10 metadata
+ const bool hasHdrMetadata = layerFEState->hdrMetadata.validTypes != 0;
+
+ auto hdrRenderType = getHdrRenderType(outputState.dataspace, pixelFormat,
+ layerFEState->desiredHdrSdrRatio, hasHdrMetadata);
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
@@ -394,8 +407,8 @@
}
// re-get HdrRenderType after the dataspace gets changed.
- hdrRenderType =
- getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+ hdrRenderType = getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio,
+ hasHdrMetadata);
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
@@ -417,12 +430,20 @@
state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f);
state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio;
} else {
+ const bool isLayerFp16 = pixelFormat && *pixelFormat == ui::PixelFormat::RGBA_FP16;
float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
// RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
// range that we may need to re-adjust to the current display conditions
+ // Do NOT do this when we may render fp16 to an fp16 client target, to avoid applying
+ // and additional gain to the layer. This is because the fp16 client target should
+ // already be adapted to remap 1.0 to the SDR white point in the panel's luminance
+ // space.
if (hdrRenderType == HdrRenderType::DISPLAY_HDR) {
- layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+ if (!FlagManager::getInstance().fp16_client_target() || !isLayerFp16) {
+ layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+ }
}
+
state.dimmingRatio =
std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
1.f);
@@ -450,7 +471,8 @@
}
void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
- bool zIsOverridden, bool isPeekingThrough) {
+ bool zIsOverridden, bool isPeekingThrough,
+ bool hasLutsProperties) {
const auto& state = getState();
// Skip doing this if there is no HWC interface
if (!state.hwc) {
@@ -492,8 +514,9 @@
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
skipLayer);
-
- writeLutToHWC(hwcLayer.get(), *outputIndependentState);
+ if (hasLutsProperties) {
+ writeLutToHWC(hwcLayer.get(), *outputIndependentState);
+ }
if (requestedCompositionType == Composition::SOLID_COLOR) {
writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
@@ -501,6 +524,15 @@
editState().hwc->stateOverridden = isOverridden;
editState().hwc->layerSkipped = skipLayer;
+
+
+ // Save the final HWC state for debugging purposes, e.g. perfetto tracing, dumpsys.
+ getLayerFE().setLastHwcState({.lastCompositionType = editState().hwc->hwcCompositionType,
+ .wasSkipped = skipLayer,
+ .wasOverridden = isOverridden,
+ .overrideBufferId = editState().overrideInfo.buffer
+ ? editState().overrideInfo.buffer.get()->getId()
+ : 0});
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
@@ -600,28 +632,29 @@
void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
- if (!outputIndependentState.luts) {
- return;
- }
- auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
- auto lutOffsets = outputIndependentState.luts->offsets;
- auto& lutProperties = outputIndependentState.luts->lutProperties;
-
- std::vector<LutProperties> aidlProperties;
- aidlProperties.reserve(lutProperties.size());
- for (size_t i = 0; i < lutOffsets.size(); i++) {
- LutProperties properties;
- properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension);
- properties.size = lutProperties[i].size;
- properties.samplingKeys = {
- static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)};
- aidlProperties.emplace_back(properties);
- }
-
Luts luts;
- luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
- luts.offsets = lutOffsets;
- luts.lutProperties = std::move(aidlProperties);
+ // if outputIndependentState.luts is nullptr, it means we want to clear the LUTs
+ // and we pass an empty Luts object to the HWC.
+ if (outputIndependentState.luts) {
+ auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
+ auto lutOffsets = outputIndependentState.luts->offsets;
+ auto& lutProperties = outputIndependentState.luts->lutProperties;
+
+ std::vector<LutProperties> aidlProperties;
+ aidlProperties.reserve(lutProperties.size());
+ for (size_t i = 0; i < lutOffsets.size(); i++) {
+ aidlProperties.emplace_back(
+ LutProperties{.dimension = static_cast<LutProperties::Dimension>(
+ lutProperties[i].dimension),
+ .size = lutProperties[i].size,
+ .samplingKeys = {static_cast<LutProperties::SamplingKey>(
+ lutProperties[i].samplingKey)}});
+ }
+
+ luts.pfd.set(dup(lutFileDescriptor.get()));
+ luts.offsets = lutOffsets;
+ luts.lutProperties = std::move(aidlProperties);
+ }
switch (auto error = hwcLayer->setLuts(luts)) {
case hal::Error::NONE:
@@ -972,6 +1005,13 @@
}
hwcState.hwcCompositionType = compositionType;
+
+ getLayerFE().setLastHwcState({.lastCompositionType = hwcState.hwcCompositionType,
+ .wasSkipped = hwcState.layerSkipped,
+ .wasOverridden = hwcState.stateOverridden,
+ .overrideBufferId = state.overrideInfo.buffer
+ ? state.overrideInfo.buffer.get()->getId()
+ : 0});
}
void OutputLayer::prepareForDeviceLayerRequests() {
@@ -994,7 +1034,7 @@
}
void OutputLayer::applyDeviceLayerLut(
- ndk::ScopedFileDescriptor lutFileDescriptor,
+ ::android::base::unique_fd lutFd,
std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) {
auto& state = editState();
LOG_FATAL_IF(!state.hwc);
@@ -1013,9 +1053,9 @@
samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0]));
}
}
- hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()),
- std::move(offsets), std::move(dimensions),
- std::move(sizes), std::move(samplingKeys));
+ hwcState.luts = std::make_shared<gui::DisplayLuts>(std::move(lutFd), std::move(offsets),
+ std::move(dimensions), std::move(sizes),
+ std::move(samplingKeys));
}
bool OutputLayer::needsFiltering() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 783209c..2081cd5 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -243,17 +243,9 @@
mCurrentGeometry = hash;
mLastGeometryUpdate = now;
-
- for (const CachedSet& cachedSet : mLayers) {
- if (cachedSet.getLayerCount() > 1) {
- ++mInvalidatedCachedSetAges[cachedSet.getAge()];
- }
- }
-
mLayers.clear();
if (mNewCachedSet) {
- ++mInvalidatedCachedSetAges[mNewCachedSet->getAge()];
mNewCachedSet = std::nullopt;
}
}
@@ -312,7 +304,6 @@
mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
if (mNewCachedSet->hasBufferUpdate()) {
ALOGV("[%s] Dropping new cached set", __func__);
- ++mInvalidatedCachedSetAges[0];
mNewCachedSet = std::nullopt;
} else if (mNewCachedSet->hasReadyBuffer()) {
ALOGV("[%s] Found ready buffer", __func__);
@@ -325,6 +316,7 @@
priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
OutputLayer::CompositionState& state =
(*incomingLayerIter)->getOutputLayer()->editState();
+
state.overrideInfo = {
.buffer = mNewCachedSet->getBuffer(),
.acquireFence = mNewCachedSet->getDrawFence(),
@@ -338,10 +330,6 @@
};
++incomingLayerIter;
}
-
- if (currentLayerIter->getLayerCount() > 1) {
- ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
- }
++currentLayerIter;
skipCount -= layerCount;
@@ -378,9 +366,9 @@
};
++incomingLayerIter;
}
+ priorBlurLayer = currentLayerIter->getBlurLayer();
} else if (currentLayerIter->getLayerCount() > 1) {
// Break the current layer into its constituent layers
- ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
for (CachedSet& layer : currentLayerIter->decompose()) {
bool disableBlur =
priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
@@ -400,8 +388,8 @@
currentLayerIter->updateAge(now);
merged.emplace_back(*currentLayerIter);
++incomingLayerIter;
+ priorBlurLayer = currentLayerIter->getBlurLayer();
}
- priorBlurLayer = currentLayerIter->getBlurLayer();
++currentLayerIter;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 3e0c390..34c09db 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -43,6 +43,10 @@
using ::testing::SaveArg;
using ::testing::StrictMock;
+static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u);
+static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u);
+static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(567u);
+
struct CompositionEngineTest : public testing::Test {
std::shared_ptr<TimeStats> mTimeStats;
@@ -52,6 +56,31 @@
std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
+
+ std::array<impl::OutputCompositionState, 3> mOutputStates;
+
+ void SetUp() override {
+ EXPECT_CALL(*mOutput1, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+ EXPECT_CALL(*mOutput1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
+ EXPECT_CALL(*mOutput2, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+ EXPECT_CALL(*mOutput2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
+ EXPECT_CALL(*mOutput3, getDisplayId)
+ .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId3)));
+ EXPECT_CALL(*mOutput3, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId3));
+
+ // Most tests will depend on the outputs being enabled.
+ for (auto& state : mOutputStates) {
+ state.isEnabled = true;
+ }
+
+ EXPECT_CALL(*mOutput1, getState).WillRepeatedly(ReturnRef(mOutputStates[0]));
+ EXPECT_CALL(*mOutput2, getState).WillRepeatedly(ReturnRef(mOutputStates[1]));
+ EXPECT_CALL(*mOutput3, getState).WillRepeatedly(ReturnRef(mOutputStates[2]));
+ }
};
TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
@@ -61,7 +90,7 @@
TEST_F(CompositionEngineTest, canSetHWComposer) {
android::mock::HWComposer* hwc = new StrictMock<android::mock::HWComposer>();
- mEngine.setHwComposer(std::unique_ptr<android::HWComposer>(hwc));
+ mEngine.setHwComposer(static_cast<android::HWComposer*>(hwc));
EXPECT_EQ(hwc, &mEngine.getHwComposer());
}
@@ -94,7 +123,7 @@
StrictMock<CompositionEnginePartialMock> mEngine;
};
-TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
+TEST_F(CompositionEnginePresentTest, zeroOutputs) {
// present() always calls preComposition() and postComposition()
EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs)));
@@ -102,7 +131,7 @@
mEngine.present(mRefreshArgs);
}
-TEST_F(CompositionEnginePresentTest, worksAsExpected) {
+TEST_F(CompositionEnginePresentTest, threeOutputs) {
// Expect calls to in a certain sequence
InSequence seq;
@@ -114,9 +143,7 @@
EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
- // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce
- // calls to getDisplayId, which are not relevant to this test.
- SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
+ EXPECT_CALL(*mOutput1, supportsOffloadPresent).WillOnce(Return(false));
// The last step is to actually present each output.
EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)))
@@ -284,8 +311,6 @@
std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()};
std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()};
- static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u);
- static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u);
static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u};
static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u};
@@ -294,12 +319,23 @@
void SetUp() override {
EXPECT_CALL(*mDisplay1, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+ EXPECT_CALL(*mDisplay1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
EXPECT_CALL(*mDisplay2, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+ EXPECT_CALL(*mDisplay2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
EXPECT_CALL(*mVirtualDisplay, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId)));
+ const DisplayIdVariant gpuVariant =
+ GpuVirtualDisplayId::fromValue(kGpuVirtualDisplayId.value);
+ EXPECT_CALL(*mVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(gpuVariant));
+
EXPECT_CALL(*mHalVirtualDisplay, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId)));
+ const DisplayIdVariant halVariant =
+ HalVirtualDisplayId::fromValue(kHalVirtualDisplayId.value);
+ EXPECT_CALL(*mHalVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(halVariant));
// Most tests will depend on the outputs being enabled.
for (auto& state : mOutputStates) {
@@ -332,7 +368,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mDisplay2});
mEngine.present(mRefreshArgs);
@@ -345,7 +380,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mDisplay2});
mEngine.present(mRefreshArgs);
@@ -358,20 +392,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
- setOutputs({mDisplay1, mDisplay2});
-
- mEngine.present(mRefreshArgs);
-}
-
-TEST_F(CompositionEngineOffloadTest, dependsOnFlag) {
- EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0);
- EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0);
-
- EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
- EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
-
- SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
setOutputs({mDisplay1, mDisplay2});
mEngine.present(mRefreshArgs);
@@ -382,7 +402,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1});
mEngine.present(mRefreshArgs);
@@ -397,7 +416,6 @@
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mDisplay2, mVirtualDisplay});
mEngine.present(mRefreshArgs);
@@ -410,7 +428,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mVirtualDisplay});
mEngine.present(mRefreshArgs);
@@ -423,7 +440,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mHalVirtualDisplay});
mEngine.present(mRefreshArgs);
@@ -440,7 +456,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1);
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2});
mEngine.present(mRefreshArgs);
@@ -458,7 +473,6 @@
EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0);
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mDisplay2});
mEngine.present(mRefreshArgs);
@@ -478,7 +492,6 @@
EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0);
EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0);
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
setOutputs({mDisplay1, mDisplay2, mHalVirtualDisplay});
mEngine.present(mRefreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index c1e59d0..77fd446 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -278,7 +278,7 @@
impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForGpuVirtualDisplay());
EXPECT_FALSE(display->isSecure());
EXPECT_TRUE(display->isVirtual());
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
+ EXPECT_TRUE(display->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
}
/*
@@ -318,6 +318,7 @@
EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
+ EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<HalVirtualDisplayId>));
EXPECT_FALSE(mDisplay->isValid());
const auto& filter = mDisplay->getState().layerFilter;
@@ -337,6 +338,7 @@
EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
+ EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
EXPECT_FALSE(mDisplay->isValid());
const auto& filter = mDisplay->getState().layerFilter;
@@ -572,7 +574,7 @@
auto args = getDisplayCreationArgsForGpuVirtualDisplay();
std::shared_ptr<Display> gpuDisplay =
createPartialMockDisplay<Display>(mCompositionEngine, args);
- EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
+ EXPECT_TRUE(gpuDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
chooseCompositionStrategy(gpuDisplay.get());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index dbffe80..2f531f1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -355,6 +355,26 @@
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
+TEST_F(OutputLayerDisplayFrameTest, outlineExpandsDisplayFrame) {
+ const int kStrokeWidth = 3;
+ mLayerFEState.borderSettings.strokeWidth = kStrokeWidth;
+ mLayerFEState.forceClientComposition = true;
+
+ mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
+ Rect expected{mLayerFEState.geomLayerBounds};
+ expected.inset(-kStrokeWidth - 2, -kStrokeWidth - 2, -kStrokeWidth - 2, -kStrokeWidth - 2);
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
+}
+TEST_F(OutputLayerDisplayFrameTest, outlineExpandsDisplayFrame_onlyIfForcingClientComposition) {
+ const int kStrokeWidth = 3;
+ mLayerFEState.borderSettings.strokeWidth = kStrokeWidth;
+ mLayerFEState.forceClientComposition = false;
+
+ mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
+ Rect expected{mLayerFEState.geomLayerBounds};
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
+}
+
/*
* OutputLayer::calculateOutputRelativeBufferTransform()
*/
@@ -541,6 +561,9 @@
MOCK_CONST_METHOD1(calculateOutputSourceCrop, FloatRect(uint32_t));
MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
MOCK_CONST_METHOD1(calculateOutputRelativeBufferTransform, uint32_t(uint32_t));
+ MOCK_METHOD(void, updateLuts,
+ (const LayerFECompositionState&,
+ const std::optional<std::vector<std::optional<LutProperties>>>&));
// compositionengine::OutputLayer overrides
const compositionengine::Output& getOutput() const override { return mOutput; }
@@ -985,21 +1008,24 @@
EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
mOutputLayer.editState().hwc.reset();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
@@ -1010,7 +1036,8 @@
EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
@@ -1041,7 +1068,8 @@
expectSetColorCall();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
@@ -1052,7 +1080,8 @@
expectSetCompositionTypeCall(Composition::SIDEBAND);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
@@ -1063,7 +1092,8 @@
expectSetCompositionTypeCall(Composition::CURSOR);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
@@ -1074,7 +1104,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
@@ -1087,7 +1118,8 @@
expectNoSetCompositionTypeCall();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
@@ -1098,7 +1130,8 @@
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
@@ -1111,7 +1144,8 @@
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
@@ -1125,7 +1159,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
@@ -1137,7 +1172,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) {
@@ -1152,7 +1188,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSendBuffer) {
@@ -1167,7 +1204,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
@@ -1182,7 +1220,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) {
@@ -1197,7 +1236,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
@@ -1211,7 +1251,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
@@ -1227,7 +1268,8 @@
expectSetCompositionTypeCall(Composition::DEVICE);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
@@ -1244,7 +1286,8 @@
expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
@@ -1258,7 +1301,8 @@
expectPerFrameCommonCalls();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) {
@@ -1266,7 +1310,8 @@
expectPerFrameCommonCalls();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ true);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ true,
+ /*hasLutsProperties*/ false);
EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
}
@@ -1276,7 +1321,7 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ true, /*isPeekingThrough*/
- false);
+ false, /*hasLutsProperties*/ false);
EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden);
}
@@ -1288,7 +1333,7 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
- false);
+ false, /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) {
@@ -1301,7 +1346,7 @@
mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
- true);
+ true, /*hasLutsProperties*/ false);
EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType);
}
@@ -1318,7 +1363,7 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
- false);
+ false, /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) {
@@ -1330,7 +1375,8 @@
expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) {
@@ -1349,7 +1395,8 @@
mOutputLayer.commitPictureProfileToCompositionState();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) {
@@ -1367,7 +1414,8 @@
EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) {
@@ -1386,7 +1434,8 @@
mOutputLayer.commitPictureProfileToCompositionState();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
@@ -1395,7 +1444,8 @@
EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0);
// No committing of picture profile before writing the state
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
}
/*
@@ -1441,21 +1491,24 @@
mLayerFEState.buffer = kBuffer1;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer1, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer2 is stored in slot 1
mLayerFEState.buffer = kBuffer2;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer2, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer3 is stored in slot 2
mLayerFEState.buffer = kBuffer3;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1
@@ -1463,7 +1516,8 @@
sp<GraphicBuffer> nullBuffer = nullptr;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
// Buffer slots are cleared
@@ -1481,7 +1535,8 @@
mLayerFEState.buffer = kBuffer1;
EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false);
Mock::VerifyAndClearExpectations(&mHwcLayer);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 442b603..590626a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -148,20 +148,23 @@
virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+ virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override {
+ return DisplayIdVariant(mId);
+ }
virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
virtual int32_t getMaxLayerPictureProfiles() const override {
return mMaxLayerPictureProfiles;
}
- void setDisplayIdForTest(DisplayId value) { mId = value; }
+ void setDisplayIdForTest(PhysicalDisplayId value) { mId = value; }
void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
private:
- ftl::Optional<DisplayId> mId;
+ PhysicalDisplayId mId;
bool mHasPictureProcessing;
int32_t mMaxLayerPictureProfiles;
};
@@ -813,19 +816,22 @@
updateCompositionState(false, false, ui::Transform::ROT_180, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer,
updateCompositionState(false, false, ui::Transform::ROT_180, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer,
updateCompositionState(false, false, ui::Transform::ROT_180, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
@@ -852,17 +858,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
@@ -888,17 +897,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
@@ -932,7 +944,8 @@
uint32_t z = 0;
EXPECT_CALL(*layer0.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer0.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
// After calling planComposition (which clears overrideInfo), this test sets
@@ -942,15 +955,17 @@
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/
- true));
+ true, /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
- /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ true, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer0);
@@ -3299,6 +3314,17 @@
sp<Fence> layer2Fence = sp<Fence>::make();
sp<Fence> layer3Fence = sp<Fence>::make();
+ // Set up layerfe buffers
+ LayerFECompositionState layer1State;
+ layer1State.buffer = sp<GraphicBuffer>::make();
+ LayerFECompositionState layer2State;
+ layer2State.buffer = sp<GraphicBuffer>::make();
+ LayerFECompositionState layer3State;
+ layer3State.buffer = nullptr;
+ EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State));
+ EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State));
+ EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State));
+
Output::FrameFences frameFences;
frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
@@ -3315,14 +3341,23 @@
.WillOnce([&layer1Fence](FenceResult releaseFence) {
EXPECT_EQ(FenceResult(layer1Fence), releaseFence);
});
+ EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+ EXPECT_EQ(layer1State.buffer, buffer);
+ });
EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_))
.WillOnce([&layer2Fence](FenceResult releaseFence) {
EXPECT_EQ(FenceResult(layer2Fence), releaseFence);
});
+ EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+ EXPECT_EQ(layer2State.buffer, buffer);
+ });
EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_))
.WillOnce([&layer3Fence](FenceResult releaseFence) {
EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
});
+ EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+ EXPECT_EQ(layer3State.buffer, buffer);
+ });
constexpr bool kFlushEvenWhenDisabled = false;
mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
@@ -3338,6 +3373,17 @@
frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
+ // Set up layerfe buffers
+ LayerFECompositionState layer1State;
+ layer1State.buffer = sp<GraphicBuffer>::make();
+ LayerFECompositionState layer2State;
+ layer2State.buffer = sp<GraphicBuffer>::make();
+ LayerFECompositionState layer3State;
+ layer3State.buffer = nullptr;
+ EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State));
+ EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State));
+ EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State));
+
EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
@@ -3347,6 +3393,15 @@
EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
+ EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+ EXPECT_EQ(layer1State.buffer, buffer);
+ });
+ EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+ EXPECT_EQ(layer2State.buffer, buffer);
+ });
+ EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+ EXPECT_EQ(layer3State.buffer, buffer);
+ });
constexpr bool kFlushEvenWhenDisabled = false;
mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
@@ -3389,7 +3444,6 @@
.WillOnce([&presentFence](FenceResult fenceResult) {
EXPECT_EQ(FenceResult(presentFence), fenceResult);
});
-
constexpr bool kFlushEvenWhenDisabled = false;
mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
@@ -4962,12 +5016,14 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -4996,17 +5052,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
@@ -5036,17 +5095,20 @@
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false,
+ /*hasLutsProperties*/ false));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
BlurRegion region;
@@ -5080,14 +5142,14 @@
InjectedLayer layer1;
injectOutputLayer(layer1);
PictureProfileHandle profileForLayer1(1);
- EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer1));
InjectedLayer layer2;
injectOutputLayer(layer2);
PictureProfileHandle profileForLayer2(2);
- EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer2));
@@ -5101,13 +5163,13 @@
// Because StrictMock
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _, _));
// No layer picture profiles should be committed
EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
@@ -5143,14 +5205,14 @@
InjectedLayer layer1;
injectOutputLayer(layer1);
PictureProfileHandle profileForLayer1(1);
- EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+ EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer1));
InjectedLayer layer2;
injectOutputLayer(layer2);
PictureProfileHandle profileForLayer2(2);
- EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
.WillRepeatedly(ReturnRef(profileForLayer2));
@@ -5164,13 +5226,13 @@
// Because StrictMock
EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _, _));
EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
- EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _, _));
// The two highest priority layers should have their picture profiles committed
EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index a086aee..7c19885 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -46,11 +46,21 @@
renderRateFpsTrace(concatId("RenderRateFps")),
hasDesiredModeTrace(concatId("HasDesiredMode"), false) {}
+DisplayModeController::DisplayModeController() {
+ using namespace std::string_literals;
+ mSupportsHdcp = base::GetBoolProperty("debug.sf.hdcp_support"s, false);
+}
+
void DisplayModeController::registerDisplay(PhysicalDisplayId displayId,
DisplaySnapshotRef snapshotRef,
RefreshRateSelectorPtr selectorPtr) {
+ DisplayPtr displayPtr = std::make_unique<Display>(snapshotRef, selectorPtr);
+ // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced
+ displayPtr->setSecure(!supportsHdcp() ||
+ snapshotRef.get().connectionType() ==
+ ui::DisplayConnectionType::Internal);
std::lock_guard lock(mDisplayLock);
- mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr));
+ mDisplays.emplace_or_replace(displayId, std::move(displayPtr));
}
void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
@@ -58,11 +68,14 @@
scheduler::RefreshRateSelector::Config config) {
const auto& snapshot = snapshotRef.get();
const auto displayId = snapshot.displayId();
-
+ DisplayPtr displayPtr =
+ std::make_unique<Display>(snapshotRef, snapshot.displayModes(), activeModeId, config);
+ // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced
+ displayPtr->setSecure(!supportsHdcp() ||
+ snapshotRef.get().connectionType() ==
+ ui::DisplayConnectionType::Internal);
std::lock_guard lock(mDisplayLock);
- mDisplays.emplace_or_replace(displayId,
- std::make_unique<Display>(snapshotRef, snapshot.displayModes(),
- activeModeId, config));
+ mDisplays.emplace_or_replace(displayId, std::move(displayPtr));
}
void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
@@ -97,9 +110,7 @@
const bool force = desiredModeOpt->force;
desiredModeOpt = std::move(desiredMode);
desiredModeOpt->emitEvent |= emitEvent;
- if (FlagManager::getInstance().connected_display()) {
- desiredModeOpt->force |= force;
- }
+ desiredModeOpt->force |= force;
return DesiredModeAction::None;
}
@@ -191,7 +202,7 @@
// cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been
// consumed at this point, so clear the `force` flag to prevent an endless loop of
// `initiateModeChange`.
- if (FlagManager::getInstance().connected_display()) {
+ {
std::scoped_lock lock(displayPtr->desiredModeLock);
if (displayPtr->desiredModeOpt) {
displayPtr->desiredModeOpt->force = false;
@@ -306,5 +317,30 @@
return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
}
+bool DisplayModeController::supportsHdcp() const {
+ return mSupportsHdcp && FlagManager::getInstance().hdcp_level_hal() &&
+ FlagManager::getInstance().hdcp_negotiation();
+}
+
+void DisplayModeController::startHdcpNegotiation(PhysicalDisplayId displayId) {
+ using aidl::android::hardware::drm::HdcpLevel;
+ using aidl::android::hardware::drm::HdcpLevels;
+ constexpr HdcpLevels kLevels = {.connectedLevel = HdcpLevel::HDCP_V2_1,
+ .maxLevel = HdcpLevel::HDCP_V2_3};
+
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+ if (displayPtr->hdcpState == HdcpState::Desired) {
+ const auto status = mComposerPtr->startHdcpNegotiation(displayId, kLevels);
+ displayPtr->hdcpState = (status == NO_ERROR) ? HdcpState::Enabled : HdcpState::Undesired;
+ }
+}
+
+void DisplayModeController::setSecure(PhysicalDisplayId displayId, bool secure) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+ displayPtr->setSecure(secure);
+}
+
#pragma clang diagnostic pop
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index af3e909..f204348 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -46,7 +46,7 @@
public:
using ActiveModeListener = ftl::Function<void(PhysicalDisplayId, Fps vsyncRate, Fps renderFps)>;
- DisplayModeController() = default;
+ DisplayModeController();
void setHwComposer(HWComposer* composerPtr) { mComposerPtr = composerPtr; }
void setActiveModeListener(const ActiveModeListener& listener) {
@@ -109,7 +109,16 @@
KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const
REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void setSecure(PhysicalDisplayId displayId, bool secure) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ bool supportsHdcp() const;
+
+ void startHdcpNegotiation(PhysicalDisplayId displayId) REQUIRES(kMainThreadContext);
+
private:
+ enum class HdcpState { Undesired, Desired, Enabled };
+
struct Display {
template <size_t N>
std::string concatId(const char (&)[N]) const;
@@ -120,6 +129,11 @@
: Display(snapshot,
std::make_shared<scheduler::RefreshRateSelector>(std::move(modes),
activeModeId, config)) {}
+
+ void setSecure(bool secure) {
+ hdcpState = secure ? HdcpState::Undesired : HdcpState::Desired;
+ }
+
const DisplaySnapshotRef snapshot;
const RefreshRateSelectorPtr selectorPtr;
@@ -135,6 +149,8 @@
bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false;
+
+ HdcpState hdcpState = HdcpState::Desired;
};
using DisplayPtr = std::unique_ptr<Display>;
@@ -153,6 +169,8 @@
mutable std::mutex mDisplayLock;
ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayPtr> mDisplays GUARDED_BY(mDisplayLock);
+
+ bool mSupportsHdcp = false;
};
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
index ec3ec52..2e9dc1e 100644
--- a/services/surfaceflinger/Display/DisplayModeRequest.h
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -26,7 +26,8 @@
struct DisplayModeRequest {
scheduler::FrameRateMode mode;
- // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
+ // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE for a change in refresh rate
+ // or render rate. Ignored for resolution changes, which always emit the event.
bool emitEvent = false;
// Whether to force the request to be applied, even if the mode is unchanged.
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp
index 0c7a58e..3960740 100644
--- a/services/surfaceflinger/Display/DisplaySnapshot.cpp
+++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp
@@ -26,11 +26,12 @@
namespace android::display {
-DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId,
+DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId, uint8_t port,
ui::DisplayConnectionType connectionType,
DisplayModes&& displayModes, ui::ColorModes&& colorModes,
std::optional<DeviceProductInfo>&& deviceProductInfo)
: mDisplayId(displayId),
+ mPort(port),
mConnectionType(connectionType),
mDisplayModes(std::move(displayModes)),
mColorModes(std::move(colorModes)),
@@ -62,6 +63,8 @@
void DisplaySnapshot::dump(utils::Dumper& dumper) const {
using namespace std::string_view_literals;
+ dumper.dump("port"sv, mPort);
+
dumper.dump("connectionType"sv, ftl::enum_string(mConnectionType));
dumper.dump("colorModes"sv);
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h
index 23471f5..0030aad 100644
--- a/services/surfaceflinger/Display/DisplaySnapshot.h
+++ b/services/surfaceflinger/Display/DisplaySnapshot.h
@@ -16,6 +16,7 @@
#pragma once
+#include <cstdint>
#include <optional>
#include <ui/ColorMode.h>
@@ -30,13 +31,14 @@
// Immutable state of a physical display, captured on hotplug.
class DisplaySnapshot {
public:
- DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&, ui::ColorModes&&,
- std::optional<DeviceProductInfo>&&);
+ DisplaySnapshot(PhysicalDisplayId, uint8_t, ui::DisplayConnectionType, DisplayModes&&,
+ ui::ColorModes&&, std::optional<DeviceProductInfo>&&);
DisplaySnapshot(const DisplaySnapshot&) = delete;
DisplaySnapshot(DisplaySnapshot&&) = default;
PhysicalDisplayId displayId() const { return mDisplayId; }
+ uint8_t port() const { return mPort; }
ui::DisplayConnectionType connectionType() const { return mConnectionType; }
std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
@@ -51,6 +53,7 @@
private:
const PhysicalDisplayId mDisplayId;
+ const uint8_t mPort;
const ui::DisplayConnectionType mConnectionType;
// Effectively const except in move constructor.
diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
index c68020c..71d9f2e 100644
--- a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
+++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
@@ -35,6 +35,7 @@
VirtualDisplayId displayId() const { return mVirtualId; }
bool isGpu() const { return mIsGpu; }
+ const std::string& uniqueId() const { return mUniqueId; }
void dump(utils::Dumper& dumper) const {
using namespace std::string_view_literals;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c743ea2..bad5e2e 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -26,7 +26,6 @@
#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/DisplayCreationArgs.h>
@@ -51,6 +50,17 @@
namespace hal = hardware::graphics::composer::hal;
+namespace gui {
+inline std::string_view to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+ switch (optimizationPolicy) {
+ case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
+ return "optimizeForPower";
+ case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
+ return "optimizeForPerformance";
+ }
+}
+} // namespace gui
+
DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay)
@@ -169,8 +179,7 @@
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
- // TODO(b/241285876): Skip this for virtual displays.
- if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) {
+ if (!isVirtual() && (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON)) {
if (mStagedBrightness && mBrightness != mStagedBrightness) {
getCompositionDisplay()->setNextBrightness(*mStagedBrightness);
mBrightness = *mStagedBrightness;
@@ -223,9 +232,7 @@
mFlags = flags;
}
-void DisplayDevice::setDisplaySize(int width, int height) {
- LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
- const auto size = ui::Size(width, height);
+void DisplayDevice::setDisplaySize(ui::Size size) {
mCompositionDisplay->setDisplaySize(size);
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setViewport(size);
@@ -285,6 +292,7 @@
dumper.dump("name"sv, '"' + mDisplayName + '"');
dumper.dump("powerMode"sv, mPowerMode);
+ dumper.dump("optimizationPolicy"sv, mOptimizationPolicy);
if (mRefreshRateSelector) {
mRefreshRateSelector->dump(dumper);
@@ -299,6 +307,10 @@
return mCompositionDisplay->getId();
}
+bool DisplayDevice::isVirtual() const {
+ return mCompositionDisplay->isVirtual();
+}
+
bool DisplayDevice::isSecure() const {
return mCompositionDisplay->isSecure();
}
@@ -307,6 +319,15 @@
mCompositionDisplay->setSecure(secure);
}
+gui::ISurfaceComposer::OptimizationPolicy DisplayDevice::getOptimizationPolicy() const {
+ return mOptimizationPolicy;
+}
+
+void DisplayDevice::setOptimizationPolicy(
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+ mOptimizationPolicy = optimizationPolicy;
+}
+
const Rect DisplayDevice::getBounds() const {
return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index af2b48f..7d7c8ad 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -23,6 +23,8 @@
#include <android-base/thread_annotations.h>
#include <android/native_window.h>
#include <binder/IBinder.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
#include <gui/LayerState.h>
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
@@ -61,11 +63,6 @@
struct CompositionInfo;
struct DisplayDeviceCreationArgs;
-namespace compositionengine {
-class Display;
-class DisplaySurface;
-} // namespace compositionengine
-
namespace display {
class DisplaySnapshot;
} // namespace display
@@ -85,7 +82,7 @@
return mCompositionDisplay;
}
- bool isVirtual() const { return getId().isVirtual(); }
+ bool isVirtual() const;
bool isPrimary() const { return mIsPrimary; }
// isSecure indicates whether this display can be trusted to display
@@ -93,12 +90,17 @@
bool isSecure() const;
void setSecure(bool secure);
+ // The optimization policy influences whether this display is optimized for power or
+ // performance.
+ gui::ISurfaceComposer::OptimizationPolicy getOptimizationPolicy() const;
+ void setOptimizationPolicy(gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy);
+
int getWidth() const;
int getHeight() const;
ui::Size getSize() const { return {getWidth(), getHeight()}; }
void setLayerFilter(ui::LayerFilter);
- void setDisplaySize(int width, int height);
+ void setDisplaySize(ui::Size);
void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
void persistBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -118,17 +120,30 @@
DisplayId getId() const;
+ DisplayIdVariant getDisplayIdVariant() const {
+ const auto idVariant = mCompositionDisplay->getDisplayIdVariant();
+ LOG_FATAL_IF(!idVariant);
+ return *idVariant;
+ }
+
+ std::optional<VirtualDisplayIdVariant> getVirtualDisplayIdVariant() const {
+ return ftl::match(
+ getDisplayIdVariant(),
+ [](PhysicalDisplayId) { return std::optional<VirtualDisplayIdVariant>(); },
+ [](auto id) { return std::optional<VirtualDisplayIdVariant>(id); });
+ }
+
// Shorthand to upcast the ID of a display whose type is known as a precondition.
PhysicalDisplayId getPhysicalId() const {
- const auto id = PhysicalDisplayId::tryCast(getId());
- LOG_FATAL_IF(!id);
- return *id;
+ const auto physicalDisplayId = asPhysicalDisplayId(getDisplayIdVariant());
+ LOG_FATAL_IF(!physicalDisplayId);
+ return *physicalDisplayId;
}
VirtualDisplayId getVirtualId() const {
- const auto id = VirtualDisplayId::tryCast(getId());
- LOG_FATAL_IF(!id);
- return *id;
+ const auto virtualDisplayId = asVirtualDisplayId(getDisplayIdVariant());
+ LOG_FATAL_IF(!virtualDisplayId);
+ return *virtualDisplayId;
}
const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
@@ -236,6 +251,9 @@
// TODO(b/182939859): Remove special cases for primary display.
const bool mIsPrimary;
+ gui::ISurfaceComposer::OptimizationPolicy mOptimizationPolicy =
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
+
uint32_t mFlags = 0;
// Requested refresh rate in fps, supported only for virtual displays.
@@ -260,6 +278,7 @@
struct Physical {
PhysicalDisplayId id;
hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
+ uint8_t port;
DisplayModePtr activeMode;
bool operator==(const Physical& other) const {
@@ -282,11 +301,16 @@
std::string displayName;
std::string uniqueId;
bool isSecure = false;
+
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy =
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
bool isProtected = false;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
Fps requestedRefreshRate;
int32_t maxLayerPictureProfiles = 0;
bool hasPictureProcessing = false;
+ hardware::graphics::composer::hal::PowerMode initialPowerMode{
+ hardware::graphics::composer::hal::PowerMode::OFF};
private:
static std::atomic<int32_t> sNextSequenceId;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 25f6513..8ead09c 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -26,12 +26,15 @@
#include <android/binder_manager.h>
#include <common/FlagManager.h>
#include <common/trace.h>
+#include <fmt/core.h>
#include <log/log.h>
#include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h>
#include <algorithm>
#include <cinttypes>
+#include <string>
+#include <string_view>
#include "HWC2.h"
@@ -229,25 +232,32 @@
HWC2::ComposerCallback& mCallback;
};
-std::string AidlComposer::instance(const std::string& serviceName) {
- return std::string(AidlIComposer::descriptor) + "/" + serviceName;
+std::string AidlComposer::ensureFullyQualifiedName(std::string_view serviceName) {
+ if (!serviceName.starts_with(AidlIComposer::descriptor)) {
+ return fmt::format("{}/{}", AidlIComposer::descriptor, serviceName);
+ } else {
+ return std::string{serviceName};
+ }
}
-bool AidlComposer::isDeclared(const std::string& serviceName) {
- return AServiceManager_isDeclared(instance(serviceName).c_str());
+bool AidlComposer::namesAnAidlComposerService(std::string_view serviceName) {
+ if (!serviceName.starts_with(AidlIComposer::descriptor)) {
+ return AServiceManager_isDeclared(ensureFullyQualifiedName(serviceName).c_str());
+ }
+ return true;
}
AidlComposer::AidlComposer(const std::string& serviceName) {
// This only waits if the service is actually declared
- mAidlComposer = AidlIComposer::fromBinder(
- ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str())));
+ mAidlComposer = AidlIComposer::fromBinder(ndk::SpAIBinder(
+ AServiceManager_waitForService(ensureFullyQualifiedName(serviceName).c_str())));
if (!mAidlComposer) {
LOG_ALWAYS_FATAL("Failed to get AIDL composer service");
return;
}
if (!mAidlComposer->createClient(&mAidlComposerClient).isOk()) {
- LOG_ALWAYS_FATAL("Can't create AidlComposerClient, fallback to HIDL");
+ LOG_ALWAYS_FATAL("Can't create AidlComposerClient");
return;
}
@@ -318,9 +328,7 @@
std::string str;
// Use other thread to read pipe to prevent
// pipe is full, making HWC be blocked in writing.
- std::thread t([&]() {
- base::ReadFdToString(pipefds[0], &str);
- });
+ std::thread t([&]() { base::ReadFdToString(pipefds[0], &str); });
const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0);
// Close the write-end of the pipe to make sure that when reading from the
// read-end we will get eof instead of blocking forever
@@ -347,7 +355,9 @@
mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);
ndk::SpAIBinder binder = mAidlComposerCallback->asBinder();
- AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2);
+ if (!FlagManager::getInstance().disable_sched_fifo_composer_callback()) {
+ AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2);
+ }
const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback);
if (!status.isOk()) {
@@ -686,6 +696,36 @@
return error;
}
+Error AidlComposer::getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) {
+ Error error = Error::NONE;
+ std::vector<PresentFence::LayerPresentFence> fences;
+ {
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ fences = reader->get().takeLayerPresentFences(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ }
+
+ outLayers->reserve(fences.size());
+ outFences->reserve(fences.size());
+ outLatenciesNanos->reserve(fences.size());
+
+ for (auto& fence : fences) {
+ outLayers->emplace_back(translate<Layer>(fence.layer));
+ // take ownership
+ const int fenceOwner = fence.bufferFence.get();
+ *fence.bufferFence.getR() = -1;
+ outFences->emplace_back(fenceOwner);
+ outLatenciesNanos->emplace_back(fence.bufferLatencyNanos);
+ }
+ return error;
+}
+
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
const auto displayId = translate<int64_t>(display);
SFTRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
@@ -1678,6 +1718,41 @@
return error;
}
+Error AidlComposer::startHdcpNegotiation(Display display,
+ const aidl::android::hardware::drm::HdcpLevels& levels) {
+ const auto status =
+ mAidlComposerClient->startHdcpNegotiation(translate<int64_t>(display), levels);
+ if (!status.isOk()) {
+ ALOGE("startHdcpNegotiation failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ return Error::NONE;
+}
+
+Error AidlComposer::getLuts(Display display, const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) {
+ std::vector<aidl::android::hardware::graphics::composer3::Buffer> aidlBuffers;
+ aidlBuffers.reserve(buffers.size());
+
+ for (auto& buffer : buffers) {
+ if (buffer.get()) {
+ aidl::android::hardware::graphics::composer3::Buffer aidlBuffer;
+ aidlBuffer.handle.emplace(::android::dupToAidl(buffer->getNativeBuffer()->handle));
+ aidlBuffers.emplace_back(std::move(aidlBuffer));
+ }
+ }
+
+ const auto status =
+ mAidlComposerClient->getLuts(translate<int64_t>(display), aidlBuffers, luts);
+ if (!status.isOk()) {
+ ALOGE("getLuts failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ return Error::NONE;
+}
+
ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
REQUIRES_SHARED(mMutex) {
return mWriters.get(display);
@@ -1708,7 +1783,6 @@
}
bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
- if (!FlagManager::getInstance().multithreaded_present()) return false;
const auto displayId = translate<int64_t>(display);
std::vector<AidlDisplayCapability> capabilities;
const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 6b5ebc5..b84d39a 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -24,7 +24,7 @@
#include <functional>
#include <optional>
#include <string>
-#include <utility>
+#include <string_view>
#include <vector>
#include <android/hardware/graphics/composer/2.4/IComposer.h>
@@ -53,7 +53,8 @@
// Composer is a wrapper to IComposer, a proxy to server-side composer.
class AidlComposer final : public Hwc2::Composer {
public:
- static bool isDeclared(const std::string& serviceName);
+ // Returns true if serviceName appears to be something that is meant to be used by AidlComposer.
+ static bool namesAnAidlComposerService(std::string_view serviceName);
explicit AidlComposer(const std::string& serviceName);
~AidlComposer() override;
@@ -106,6 +107,10 @@
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
+ Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) override;
+
Error presentDisplay(Display display, int* outPresentFence) override;
Error setActiveConfig(Display display, Config config) override;
@@ -245,6 +250,9 @@
Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
Error setDisplayPictureProfileId(Display, PictureProfileId id) override;
Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override;
+ Error startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) override;
+ Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
private:
// Many public functions above simply write a command into the command
@@ -252,8 +260,8 @@
// this function to execute the command queue.
Error execute(Display) REQUIRES_SHARED(mMutex);
- // returns the default instance name for the given service
- static std::string instance(const std::string& serviceName);
+ // Ensures serviceName is fully qualified.
+ static std::string ensureFullyQualifiedName(std::string_view serviceName);
ftl::Optional<std::reference_wrapper<ComposerClientWriter>> getWriter(Display)
REQUIRES_SHARED(mMutex);
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index d69a923..1e4132c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -26,7 +26,7 @@
Composer::~Composer() = default;
std::unique_ptr<Composer> Composer::create(const std::string& serviceName) {
- if (AidlComposer::isDeclared(serviceName)) {
+ if (AidlComposer::namesAnAidlComposerService(serviceName)) {
return std::make_unique<AidlComposer>(serviceName);
}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index ff292fa..c558931 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -46,6 +46,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <optional>
@@ -156,6 +157,10 @@
virtual Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) = 0;
+ virtual Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) = 0;
+
virtual Error presentDisplay(Display display, int* outPresentFence) = 0;
virtual Error setActiveConfig(Display display, Config config) = 0;
@@ -224,14 +229,13 @@
virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
Display display) = 0;
virtual Error getRenderIntents(Display display, ColorMode colorMode,
- std::vector<RenderIntent>* outRenderIntents) = 0;
+ std::vector<RenderIntent>* outRenderIntents) = 0;
virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0;
// Composer HAL 2.3
virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
std::vector<uint8_t>* outData) = 0;
- virtual Error setLayerColorTransform(Display display, Layer layer,
- const float* matrix) = 0;
+ virtual Error setLayerColorTransform(Display display, Layer layer, const float* matrix) = 0;
virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
Dataspace* outDataspace,
uint8_t* outComponentMask) = 0;
@@ -313,6 +317,10 @@
virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0;
virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0;
virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0;
+ virtual Error startHdcpNegotiation(Display display,
+ const aidl::android::hardware::drm::HdcpLevels& levels) = 0;
+ virtual Error getLuts(Display display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<V3_0::Luts>*) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 081f4aa..fd0bf73 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -439,11 +439,8 @@
// FIXME (b/319505580): At least the first config set on an external display must be
// `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints`
// for simplicity.
- const bool connected_display = FlagManager::getInstance().connected_display();
-
if (isVsyncPeriodSwitchSupported() &&
- (!connected_display ||
- getConnectionType().value_opt() != ui::DisplayConnectionType::External)) {
+ getConnectionType().value_opt() != ui::DisplayConnectionType::External) {
Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
@@ -629,7 +626,7 @@
auto layer = getLayerById(layerIds[i]);
if (layer) {
auto& layerLut = tmpLuts[i];
- if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) {
+ if (layerLut.luts.pfd.get() >= 0 && layerLut.luts.offsets.has_value()) {
const auto& offsets = layerLut.luts.offsets.value();
std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties;
lutOffsetsAndProperties.reserve(offsets.size());
@@ -638,9 +635,17 @@
[](int32_t i, LutProperties j) { return std::make_pair(i, j); });
outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties);
lutFileDescriptorMapper.emplace_or_replace(layer.get(),
- ndk::ScopedFileDescriptor(
+ ::android::base::unique_fd(
layerLut.luts.pfd.release()));
+ } else {
+ ALOGE("getRequestedLuts: invalid luts on layer %" PRIu64 " found"
+ " on display %" PRIu64 ". pfd.get()=%d, offsets.has_value()=%d",
+ layerIds[i], mId, layerLut.luts.pfd.get(), layerLut.luts.offsets.has_value());
}
+ } else {
+ ALOGE("getRequestedLuts: invalid layer %" PRIu64 " found"
+ " on display %" PRIu64,
+ layerIds[i], mId);
}
}
@@ -669,6 +674,17 @@
return static_cast<Error>(error);
}
+Error Display::startHdcpNegotiation(const aidl::android::hardware::drm::HdcpLevels& levels) {
+ const auto error = mComposer.startHdcpNegotiation(mId, levels);
+ return static_cast<Error>(error);
+}
+
+Error Display::getLuts(const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>* outLuts) {
+ const auto error = mComposer.getLuts(mId, buffers, outLuts);
+ return static_cast<Error>(error);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 6740d8a..3f51821 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -115,7 +115,7 @@
using LayerLuts =
ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>;
using LutFileDescriptorMapper =
- ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
+ ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, kLutFileDescriptorMapperSize>;
[[nodiscard]] virtual hal::Error acceptChanges() = 0;
[[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
@@ -203,6 +203,11 @@
[[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0;
[[nodiscard]] virtual hal::Error setPictureProfileHandle(
const PictureProfileHandle& handle) = 0;
+ [[nodiscard]] virtual hal::Error startHdcpNegotiation(
+ const aidl::android::hardware::drm::HdcpLevels& levels) = 0;
+ [[nodiscard]] virtual hal::Error getLuts(
+ const std::vector<android::sp<android::GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0;
};
namespace impl {
@@ -288,6 +293,10 @@
hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override;
hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override;
hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override;
+ hal::Error startHdcpNegotiation(
+ const aidl::android::hardware::drm::HdcpLevels& levels) override;
+ hal::Error getLuts(const std::vector<android::sp<android::GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 55ccdef..758d924 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -138,14 +138,14 @@
}
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:
- return {};
+ case HotplugEvent::LinkUnstable:
+ return onHotplugLinkTrainingFailure(hwcDisplayId);
}
}
@@ -225,7 +225,11 @@
}
void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId,
- std::optional<ui::Size> physicalSize) {
+ uint8_t port, std::optional<ui::Size> physicalSize) {
+ LOG_ALWAYS_FATAL_IF(!mActivePorts.try_emplace(port).second,
+ "Cannot attach display %" PRIu64 " to an already active port %" PRIu8 ".",
+ hwcDisplayId, port);
+
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
if (!mPrimaryHwcDisplayId) {
@@ -239,6 +243,7 @@
newDisplay->setConnected(true);
newDisplay->setPhysicalSizeInMm(physicalSize);
displayData.hwcDisplay = std::move(newDisplay);
+ displayData.port = port;
}
int32_t HWComposer::getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId configId,
@@ -555,7 +560,7 @@
if (!hasChangesError(error)) {
RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
}
- if (state == 1) { //Present Succeeded.
+ if (state == 1) { // Present Succeeded.
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
error = hwcDisplay->getReleaseFences(&releaseFences);
displayData.releaseFences = std::move(releaseFences);
@@ -758,6 +763,9 @@
const auto hwcDisplayId = displayData.hwcDisplay->getId();
mPhysicalDisplayIdMap.erase(hwcDisplayId);
+ if (const auto port = displayData.port) {
+ mActivePorts.erase(port.value());
+ }
mDisplayData.erase(displayId);
// Reset the primary display ID if we're disconnecting it.
@@ -816,8 +824,8 @@
RETURN_IF_INVALID_DISPLAY(displayId, {});
mat4 matrix;
- auto error = mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace,
- &matrix);
+ auto error =
+ mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, &matrix);
RETURN_IF_HWC_ERROR(error, displayId, {});
return matrix;
}
@@ -1046,11 +1054,30 @@
return NO_ERROR;
}
+status_t HWComposer::startHdcpNegotiation(PhysicalDisplayId displayId,
+ const aidl::android::hardware::drm::HdcpLevels& levels) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto error = hwcDisplay->startHdcpNegotiation(levels);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
+status_t HWComposer::getLuts(
+ PhysicalDisplayId displayId, const std::vector<sp<GraphicBuffer>>& buffers,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto error = hwcDisplay->getLuts(buffers, luts);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
return mSupportedLayerGenericMetadata;
}
-ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>&
+ftl::SmallMap<HWC2::Layer*, ::android::base::unique_fd, 20>&
HWComposer::getLutFileDescriptorMapper() {
return mLutFileDescriptorMapper;
}
@@ -1113,8 +1140,15 @@
return {};
}
-bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
+bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId, uint8_t port,
bool hasDisplayIdentificationData) const {
+ if (mActivePorts.contains(port)) {
+ ALOGE("Ignoring connection of display %" PRIu64 ". Port %" PRIu8
+ " is already in active use.",
+ hwcDisplayId, port);
+ return true;
+ }
+
if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
hwcDisplayId);
@@ -1160,7 +1194,7 @@
mHasMultiDisplaySupport ? "generalized" : "legacy");
}
- if (shouldIgnoreHotplugConnect(hwcDisplayId, hasDisplayIdentificationData)) {
+ if (shouldIgnoreHotplugConnect(hwcDisplayId, port, hasDisplayIdentificationData)) {
return {};
}
@@ -1180,6 +1214,7 @@
return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
.name = isPrimary ? "Primary display"
: "Secondary display",
+ .port = port,
.deviceProductInfo = std::nullopt};
}();
@@ -1191,7 +1226,7 @@
if (info->preferredDetailedTimingDescriptor) {
size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
}
- allocatePhysicalDisplay(hwcDisplayId, info->id, size);
+ allocatePhysicalDisplay(hwcDisplayId, info->id, info->port, size);
}
return info;
}
@@ -1219,6 +1254,16 @@
return DisplayIdentificationInfo{.id = *displayId};
}
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugLinkTrainingFailure(
+ hal::HWDisplayId hwcDisplayId) {
+ const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+ if (!displayId) {
+ LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
+ return {};
+ }
+ return DisplayIdentificationInfo{.id = *displayId};
+}
+
void HWComposer::loadCapabilities() {
static_assert(sizeof(hal::Capability) == sizeof(int32_t), "Capability size has changed");
auto capabilities = mComposer->getCapabilities();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 52662cf..fcecd23 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -28,6 +28,7 @@
#include <ftl/expected.h>
#include <ftl/future.h>
#include <ui/DisplayIdentification.h>
+#include <ui/DisplayMap.h>
#include <ui/FenceTime.h>
#include <ui/PictureProfileHandle.h>
@@ -55,6 +56,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
#include <aidl/android/hardware/graphics/composer3/OutputType.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
@@ -143,7 +145,7 @@
// supported by the HWC can be queried in advance, but allocation may fail for other reasons.
virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0;
- virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, uint8_t port,
std::optional<ui::Size> physicalSize) = 0;
// Attempts to create a new layer on this display
@@ -230,11 +232,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
@@ -324,6 +327,10 @@
virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0;
virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
const PictureProfileHandle& handle) = 0;
+ virtual status_t startHdcpNegotiation(PhysicalDisplayId,
+ const aidl::android::hardware::drm::HdcpLevels&) = 0;
+ virtual status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -358,7 +365,7 @@
bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
- void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, uint8_t port,
std::optional<ui::Size> physicalSize) override;
// Attempts to create a new layer on this display
@@ -432,9 +439,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;
@@ -491,6 +496,10 @@
int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override;
status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
const android::PictureProfileHandle& profile) override;
+ status_t startHdcpNegotiation(PhysicalDisplayId,
+ const aidl::android::hardware::drm::HdcpLevels&) override;
+ status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -521,6 +530,7 @@
struct DisplayData {
std::unique_ptr<HWC2::Display> hwcDisplay;
+ std::optional<uint8_t> port; // Set on hotplug for physical displays
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
nsecs_t lastPresentTimestamp = 0;
@@ -538,7 +548,9 @@
std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
- bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+ std::optional<DisplayIdentificationInfo> onHotplugLinkTrainingFailure(hal::HWDisplayId);
+ bool shouldIgnoreHotplugConnect(hal::HWDisplayId, uint8_t port,
+ bool hasDisplayIdentificationData) const;
aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi
getEstimatedDotsPerInchFromSize(uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const;
@@ -560,6 +572,7 @@
void loadHdrConversionCapabilities();
std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
+ ui::PhysicalDisplaySet<uint8_t> mActivePorts;
std::unique_ptr<android::Hwc2::Composer> mComposer;
std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 5703a2d..5e03f30 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -28,6 +28,7 @@
#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/composer/2.1/types.h>
+#include <common/FlagManager.h>
#include <common/trace.h>
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <hidl/HidlTransportSupport.h>
@@ -301,7 +302,9 @@
}
void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
- android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
+ if (!FlagManager::getInstance().disable_sched_fifo_composer_callback()) {
+ android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
+ }
auto ret = [&]() {
if (mClient_2_4) {
@@ -590,6 +593,11 @@
return Error::NONE;
}
+Error HidlComposer::getLayerPresentFences(Display, std::vector<Layer>*, std::vector<int>*,
+ std::vector<int64_t>*) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::presentDisplay(Display display, int* outPresentFence) {
SFTRACE_NAME("HwcPresentDisplay");
mWriter.selectDisplay(display);
@@ -1222,15 +1230,16 @@
translate<DisplayCapability>(tmpCaps);
});
} else {
- mClient_2_3
- ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
- error = static_cast<V2_4::Error>(tmpError);
- if (error != V2_4::Error::NONE) {
- return;
- }
+ mClient_2_3->getDisplayCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpCaps) {
+ error = static_cast<V2_4::Error>(tmpError);
+ if (error != V2_4::Error::NONE) {
+ return;
+ }
- *outCapabilities = translate<DisplayCapability>(tmpCaps);
- });
+ *outCapabilities =
+ translate<DisplayCapability>(tmpCaps);
+ });
}
return static_cast<Error>(error);
@@ -1452,6 +1461,15 @@
return Error::UNSUPPORTED;
}
+Error HidlComposer::startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) {
+ return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) {
+ return Error::UNSUPPORTED;
+}
+
Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) {
return Error::UNSUPPORTED;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 42ba9a9..d3874e4 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -214,6 +214,10 @@
Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) override;
+ Error getLayerPresentFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outFences,
+ std::vector<int64_t>* outLatenciesNanos) override;
+
Error presentDisplay(Display display, int* outPresentFence) override;
Error setActiveConfig(Display display, Config config) override;
@@ -359,6 +363,9 @@
Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
Error setDisplayPictureProfileId(Display, PictureProfileId) override;
Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override;
+ Error startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) override;
+ Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 7e29bff..5b7fe96 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -47,7 +47,8 @@
namespace android {
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
+ VirtualDisplayIdVariant virtualIdVariant,
const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -58,7 +59,7 @@
: ConsumerBase(bqConsumer),
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mHwc(hwc),
- mDisplayId(displayId),
+ mVirtualIdVariant(virtualIdVariant),
mDisplayName(name),
mSource{},
mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
@@ -123,7 +124,7 @@
}
status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ if (isBackedByGpu()) {
return NO_ERROR;
}
@@ -141,7 +142,7 @@
}
status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ if (isBackedByGpu()) {
return NO_ERROR;
}
@@ -189,7 +190,10 @@
}
status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ const auto halVirtualDisplayId = ftl::match(
+ mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); },
+ [](auto) { return ftl::Optional<HalVirtualDisplayId>(); });
+ if (!halVirtualDisplayId) {
return NO_ERROR;
}
@@ -220,11 +224,8 @@
VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
- const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
- LOG_FATAL_IF(!halDisplayId);
- // At this point we know the output buffer acquire fence,
- // so update HWC state with it.
- mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
+ // At this point we know the output buffer acquire fence, so update HWC state with it.
+ mHwc.setOutputBuffer(*halVirtualDisplayId, mOutputFence, outBuffer);
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
@@ -235,7 +236,7 @@
hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
}
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halVirtualDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN, hdrSdrRatio);
}
@@ -243,8 +244,8 @@
}
void VirtualDisplaySurface::onFrameCommitted() {
- const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
- if (!halDisplayId) {
+ const auto halDisplayId = asHalDisplayId(mVirtualIdVariant);
+ if (!halDisplayId.has_value()) {
return;
}
@@ -258,8 +259,7 @@
Mutex::Autolock lock(mMutex);
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot);
- addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
- retireFence);
+ addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], retireFence);
releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]);
}
@@ -307,7 +307,7 @@
status_t VirtualDisplaySurface::requestBuffer(int pslot,
sp<GraphicBuffer>* outBuf) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ if (isBackedByGpu()) {
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
@@ -329,7 +329,7 @@
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
- LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+ LOG_ALWAYS_FATAL_IF(isBackedByGpu());
// Exclude video encoder usage flag from scratch buffer usage flags.
if (source == SOURCE_SCRATCH) {
@@ -389,7 +389,7 @@
PixelFormat format, uint64_t usage,
uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ if (isBackedByGpu()) {
return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
outTimestamps);
}
@@ -475,7 +475,7 @@
status_t VirtualDisplaySurface::queueBuffer(int pslot,
const QueueBufferInput& input, QueueBufferOutput* output) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ if (isBackedByGpu()) {
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
@@ -533,7 +533,7 @@
status_t VirtualDisplaySurface::cancelBuffer(int pslot,
const sp<Fence>& fence) {
- if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+ if (isBackedByGpu()) {
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
@@ -637,7 +637,10 @@
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
- LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+ const auto halVirtualDisplayId = ftl::match(
+ mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); },
+ [](auto) { return ftl::Optional<HalVirtualDisplayId>(); });
+ LOG_ALWAYS_FATAL_IF(!halVirtualDisplayId);
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
@@ -656,14 +659,16 @@
// until after GPU calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
- const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
- LOG_FATAL_IF(!halDisplayId);
- result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
+ result = mHwc.setOutputBuffer(*halVirtualDisplayId, Fence::NO_FENCE,
mProducerBuffers[mOutputProducerSlot]);
return result;
}
+bool VirtualDisplaySurface::isBackedByGpu() const {
+ return std::holds_alternative<GpuVirtualDisplayId>(mVirtualIdVariant);
+}
+
// This slot mapping function is its own inverse, so two copies are unnecessary.
// Both are kept to make the intent clear where the function is called, and for
// the (unlikely) chance that we switch to a different mapping function.
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 805fce9..16a2ba8 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -75,7 +75,8 @@
public BnGraphicBufferProducer,
private ConsumerBase {
public:
- VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
+ VirtualDisplaySurface(HWComposer&, VirtualDisplayIdVariant,
+ const sp<IGraphicBufferProducer>& sink,
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
const std::string& name, bool secure);
@@ -147,6 +148,7 @@
void updateQueueBufferOutput(QueueBufferOutput&&);
void resetPerFrameState();
status_t refreshOutputBuffer();
+ bool isBackedByGpu() const;
// Both the sink and scratch buffer pools have their own set of slots
// ("source slots", or "sslot"). We have to merge these into the single
@@ -161,7 +163,7 @@
// Immutable after construction
//
HWComposer& mHwc;
- const VirtualDisplayId mDisplayId;
+ const VirtualDisplayIdVariant mVirtualIdVariant;
const std::string mDisplayName;
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
deleted file mode 100644
index c63c738..0000000
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DisplayRenderArea.h"
-#include "DisplayDevice.h"
-
-namespace android {
-
-std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
- const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace reqDataSpace,
- ftl::Flags<Options> options) {
- if (auto display = displayWeak.promote()) {
- // Using new to access a private constructor.
- return std::unique_ptr<DisplayRenderArea>(new DisplayRenderArea(std::move(display),
- sourceCrop, reqSize,
- reqDataSpace, options));
- }
- return nullptr;
-}
-
-DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
- ui::Size reqSize, ui::Dataspace reqDataSpace,
- ftl::Flags<Options> options)
- : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, options),
- mDisplay(std::move(display)),
- mSourceCrop(sourceCrop) {}
-
-const ui::Transform& DisplayRenderArea::getTransform() const {
- return mTransform;
-}
-
-bool DisplayRenderArea::isSecure() const {
- return mOptions.test(Options::CAPTURE_SECURE_LAYERS) && mDisplay->isSecure();
-}
-
-sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const {
- return mDisplay;
-}
-
-Rect DisplayRenderArea::getSourceCrop() const {
- // use the projected display viewport by default.
- if (mSourceCrop.isEmpty()) {
- return mDisplay->getLayerStackSpaceRect();
- }
- return mSourceCrop;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
deleted file mode 100644
index 677d019..0000000
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <ui/GraphicTypes.h>
-#include <ui/Transform.h>
-
-#include "RenderArea.h"
-
-namespace android {
-
-class DisplayDevice;
-
-class DisplayRenderArea : public RenderArea {
-public:
- static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
- ui::Size reqSize, ui::Dataspace,
- ftl::Flags<Options> options);
-
- const ui::Transform& getTransform() const override;
- bool isSecure() const override;
- sp<const DisplayDevice> getDisplayDevice() const override;
- Rect getSourceCrop() const override;
-
-private:
- DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace, ftl::Flags<Options> options);
-
- const sp<const DisplayDevice> mDisplay;
- const Rect mSourceCrop;
- const ui::Transform mTransform;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
deleted file mode 100644
index 8e28cc3..0000000
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
- default_team: "trendy_team_android_core_graphics_stack",
-}
-
-cc_library_static {
- name: "libframetimeline",
- defaults: ["surfaceflinger_defaults"],
- srcs: [
- "FrameTimeline.cpp",
- ],
- header_libs: [
- "libscheduler_headers",
- ],
- shared_libs: [
- "android.hardware.graphics.composer@2.4",
- "libbase",
- "libcutils",
- "liblog",
- "libgui",
- "libtimestats",
- "libui",
- "libutils",
- ],
- static_libs: [
- "libperfetto_client_experimental",
- "libsurfaceflinger_common",
- ],
- export_include_dirs: ["."],
-}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 86d7388..51d4078 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -29,7 +29,6 @@
#include <cinttypes>
#include <numeric>
#include <unordered_set>
-#include <vector>
#include "../Jank/JankTracker.h"
@@ -611,7 +610,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 +632,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 {
@@ -651,7 +653,7 @@
// We try to do this by moving the deadline. Since the queue could be stuffed by more
// than one buffer, we take the last latch time as reference and give one vsync
// worth of time for the frame to be ready.
- nsecs_t adjustedDeadline = mLastLatchTime + refreshRate.getPeriodNsecs();
+ nsecs_t adjustedDeadline = mLastLatchTime + displayFrameRenderRate.getPeriodNsecs();
if (adjustedDeadline > mActuals.endTime) {
mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
} else {
@@ -667,9 +669,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 {
@@ -1003,11 +1004,6 @@
finalizeCurrentDisplayFrame();
}
-const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames()
- const {
- return mPresentFrames;
-}
-
void FrameTimeline::onCommitNotComposited() {
SFTRACE_CALL();
std::scoped_lock lock(mMutex);
@@ -1091,7 +1087,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 +1122,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 +1141,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 +1150,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 +1163,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) {
@@ -1527,7 +1524,6 @@
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
- mPresentFrames.clear();
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
@@ -1541,12 +1537,6 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
- // Surface frames have been jank classified and can be provided to caller
- // to detect if buffer stuffing is occurring.
- for (const auto& frame : displayFrame->getSurfaceFrames()) {
- mPresentFrames.push_back(frame);
- }
-
mPreviousPredictionPresentTime =
displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a47bd57..fa83cd8 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();
};
@@ -328,11 +331,6 @@
virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence) = 0;
- // Provides surface frames that have already been jank classified in the most recent
- // flush of pending present fences. This allows buffer stuffing detection from SF.
- virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
- const = 0;
-
// Tells FrameTimeline that a frame was committed but not composited. This is used to flush
// all the associated surface frames.
virtual void onCommitNotComposited() = 0;
@@ -510,8 +508,6 @@
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
- const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
- const override;
void onCommitNotComposited() override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
@@ -559,9 +555,6 @@
// display frame, this is a good starting size for the vector so that we can avoid the
// internal vector resizing that happens with push_back.
static constexpr uint32_t kNumSurfaceFramesInitial = 10;
- // Presented surface frames that have been jank classified and can
- // indicate of potential buffer stuffing.
- std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames;
};
} // namespace impl
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index da536b6..00ec863 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -54,7 +54,8 @@
mChildren = hierarchy.mChildren;
}
-void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& traversalPath,
+void LayerHierarchy::traverse(const Visitor& visitor,
+ const LayerHierarchy::TraversalPath& traversalPath,
uint32_t depth) const {
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
"Cycle detected in LayerHierarchy::traverse. See "
@@ -70,14 +71,13 @@
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(traversalPath.hasRelZLoop(), "Found relative z loop layerId:%d",
traversalPath.invalidRelativeRootId);
for (auto& [child, childVariant] : mChildren) {
- ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
- childVariant);
- child->traverse(visitor, traversalPath, depth + 1);
+ child->traverse(visitor, traversalPath.makeChild(child->mLayer->id, childVariant),
+ depth + 1);
}
}
void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
- LayerHierarchy::TraversalPath& traversalPath) const {
+ const LayerHierarchy::TraversalPath& traversalPath) const {
bool traverseThisLayer = (mLayer != nullptr);
for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
auto& [child, childVariant] = *it;
@@ -91,9 +91,7 @@
if (childVariant == LayerHierarchy::Variant::Detached) {
continue;
}
- ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
- childVariant);
- child->traverseInZOrder(visitor, traversalPath);
+ child->traverseInZOrder(visitor, traversalPath.makeChild(child->mLayer->id, childVariant));
}
if (traverseThisLayer) {
@@ -568,42 +566,23 @@
return ss.str();
}
-// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
-// of scope the TraversalPath is reset to its original state.
-LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
- uint32_t layerId,
- LayerHierarchy::Variant variant)
- : mTraversalPath(traversalPath), mParentPath(traversalPath) {
- // Update the traversal id with the child layer id and variant. Parent id and variant are
- // stored to reset the id upon destruction.
- traversalPath.id = layerId;
- traversalPath.variant = variant;
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::makeChild(
+ uint32_t layerId, LayerHierarchy::Variant variant) const {
+ TraversalPath child{*this};
+ child.id = layerId;
+ child.variant = variant;
if (LayerHierarchy::isMirror(variant)) {
- traversalPath.mirrorRootIds.emplace_back(mParentPath.id);
+ child.mirrorRootIds.emplace_back(id);
} else if (variant == LayerHierarchy::Variant::Relative) {
- if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
- layerId) != traversalPath.relativeRootIds.end()) {
- traversalPath.invalidRelativeRootId = layerId;
+ if (std::find(relativeRootIds.begin(), relativeRootIds.end(), layerId) !=
+ relativeRootIds.end()) {
+ child.invalidRelativeRootId = layerId;
}
- traversalPath.relativeRootIds.emplace_back(layerId);
+ child.relativeRootIds.emplace_back(layerId);
} else if (variant == LayerHierarchy::Variant::Detached) {
- traversalPath.detached = true;
+ child.detached = true;
}
-}
-LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
- // Reset the traversal id to its original parent state using the state that was saved in
- // the constructor.
- if (LayerHierarchy::isMirror(mTraversalPath.variant)) {
- mTraversalPath.mirrorRootIds.pop_back();
- } else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
- mTraversalPath.relativeRootIds.pop_back();
- }
- if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
- mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
- }
- mTraversalPath.id = mParentPath.id;
- mTraversalPath.variant = mParentPath.variant;
- mTraversalPath.detached = mParentPath.detached;
+ return child;
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 47d0041..c8c6b4d 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -102,6 +102,10 @@
// Returns true if the node is a clone.
bool isClone() const { return !mirrorRootIds.empty(); }
+ TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; }
+
+ TraversalPath makeChild(uint32_t layerId, LayerHierarchy::Variant variant) const;
+
bool operator==(const TraversalPath& other) const {
return id == other.id && mirrorRootIds == other.mirrorRootIds;
}
@@ -120,18 +124,6 @@
}
};
- // Helper class to add nodes to an existing traversal id and removes the
- // node when it goes out of scope.
- class ScopedAddToTraversalPath {
- public:
- ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
- LayerHierarchy::Variant variantArg);
- ~ScopedAddToTraversalPath();
-
- private:
- TraversalPath& mTraversalPath;
- TraversalPath mParentPath;
- };
LayerHierarchy(RequestedLayerState* layer);
// Visitor function that provides the hierarchy node and a traversal id which uniquely
@@ -189,8 +181,9 @@
void removeChild(LayerHierarchy*);
void sortChildrenByZOrder();
void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
- void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
- void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent,
+ void traverseInZOrder(const Visitor& visitor,
+ const LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, const LayerHierarchy::TraversalPath& parent,
uint32_t depth = 0) const;
void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
bool isLastChild, bool includeMirroredHierarchy) const;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index f1091a6..d369403 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -182,8 +182,8 @@
}
}
-void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions,
- bool ignoreUnknownLayers) {
+void LayerLifecycleManager::applyTransactions(
+ const std::vector<QueuedTransactionState>& transactions, bool ignoreUnknownLayers) {
for (const auto& transaction : transactions) {
for (const auto& resolvedComposerState : transaction.states) {
const auto& clientState = resolvedComposerState.state;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 330da9a..072be35 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -16,8 +16,8 @@
#pragma once
+#include "QueuedTransactionState.h"
#include "RequestedLayerState.h"
-#include "TransactionState.h"
namespace android::surfaceflinger::frontend {
@@ -43,7 +43,8 @@
// the layers it is unreachable. When using the LayerLifecycleManager for layer trace
// generation we may encounter layers which are known because we don't have an explicit
// lifecycle. Ignore these errors while we have to interop with legacy.
- void applyTransactions(const std::vector<TransactionState>&, bool ignoreUnknownLayers = false);
+ void applyTransactions(const std::vector<QueuedTransactionState>&,
+ bool ignoreUnknownLayers = false);
// Ignore unknown handles when iteroping with legacy front end. In the old world, we
// would create child layers which are not necessary with the new front end. This means
// we will get notified for handle changes that don't exist in the new front end.
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 58f6b96..3aa2e98 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -18,12 +18,17 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
-#include "LayerSnapshot.h"
+#include <PowerAdvisor/Workload.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <gui/LayerState.h>
+
#include "Layer.h"
+#include "LayerSnapshot.h"
namespace android::surfaceflinger::frontend {
using namespace ftl::flag_operators;
+using namespace aidl::android::hardware::graphics::composer3;
namespace {
@@ -36,7 +41,7 @@
if (forceFullDamage) {
outSurfaceDamageRegion = Region::INVALID_REGION;
} else {
- outSurfaceDamageRegion = requested.surfaceDamageRegion;
+ outSurfaceDamageRegion = requested.getSurfaceDamageRegion();
}
}
@@ -174,8 +179,12 @@
return backgroundBlurRadius > 0 || blurRegions.size() > 0;
}
+bool LayerSnapshot::hasOutline() const {
+ return borderSettings.strokeWidth > 0;
+}
+
bool LayerSnapshot::hasEffect() const {
- return fillsColor() || drawShadows() || hasBlur();
+ return fillsColor() || drawShadows() || hasBlur() || hasOutline();
}
bool LayerSnapshot::hasSomethingToDraw() const {
@@ -248,6 +257,7 @@
reason << " buffer=" << externalTexture->getId() << " frame=" << frameNumber;
if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
+ if (hasOutline()) reason << "borderSettings=" << borderSettings.toString();
if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
if (contentDirty) reason << " contentDirty";
@@ -300,7 +310,11 @@
out << rootId << ",";
}
}
- out << "] " << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible")
+ out << "] ";
+ if (obj.isSecure) {
+ out << "(Secure) ";
+ }
+ out << obj.name << "\n " << (obj.isVisible ? "visible" : "invisible")
<< " reason=" << obj.getIsVisibleReason();
if (!obj.geomLayerBounds.isEmpty()) {
@@ -367,7 +381,7 @@
updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) {
- transparentRegionHint = requested.transparentRegion;
+ transparentRegionHint = requested.getTransparentRegion();
}
if (forceUpdate || requested.what & layer_state_t::eFlagsChanged) {
layerOpaqueFlagSet =
@@ -401,6 +415,9 @@
if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
shadowSettings.length = requested.shadowRadius;
}
+ if (forceUpdate || requested.what & layer_state_t::eBorderSettingsChanged) {
+ borderSettings = requested.borderSettings;
+ }
if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
frameRateSelectionPriority = requested.frameRateSelectionPriority;
}
@@ -418,7 +435,7 @@
}
if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) {
// TODO(b/337330263): Also consider the system-determined priority of the app
- pictureProfilePriority = requested.appContentPriority;
+ pictureProfilePriority = int64_t(requested.appContentPriority) + INT_MAX;
}
if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
@@ -438,15 +455,7 @@
}
if (forceUpdate || requested.what & layer_state_t::eInputInfoChanged) {
- if (requested.windowInfoHandle) {
- inputInfo = *requested.windowInfoHandle->getInfo();
- } else {
- inputInfo = {};
- // b/271132344 revisit this and see if we can always use the layers uid/pid
- inputInfo.name = requested.name;
- inputInfo.ownerUid = requested.ownerUid;
- inputInfo.ownerPid = requested.ownerPid;
- }
+ inputInfo = requested.getWindowInfo();
inputInfo.id = static_cast<int32_t>(uniqueSequence);
touchCropId = requested.touchCropId;
}
@@ -506,9 +515,9 @@
(layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged |
layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged |
layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged |
- layer_state_t::eEdgeExtensionChanged)) {
+ layer_state_t::eEdgeExtensionChanged | layer_state_t::eBorderSettingsChanged)) {
forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect() ||
- edgeExtensionEffect.hasEffect();
+ edgeExtensionEffect.hasEffect() || borderSettings.strokeWidth > 0;
}
if (forceUpdate ||
@@ -528,4 +537,50 @@
}
}
+char LayerSnapshot::classifyCompositionForDebug(
+ const compositionengine::LayerFE::HwcLayerDebugState& hwcState) const {
+ if (!isVisible) {
+ return '.';
+ }
+
+ switch (hwcState.lastCompositionType) {
+ case Composition::INVALID:
+ return 'i';
+ case Composition::SOLID_COLOR:
+ return 'c';
+ case Composition::CURSOR:
+ return 'u';
+ case Composition::SIDEBAND:
+ return 'd';
+ case Composition::DISPLAY_DECORATION:
+ return 'a';
+ case Composition::REFRESH_RATE_INDICATOR:
+ return 'r';
+ case Composition::CLIENT:
+ case Composition::DEVICE:
+ break;
+ }
+
+ char code = '.'; // Default to invisible
+ if (hasBlur()) {
+ code = 'l'; // Blur
+ } else if (hasProtectedContent) {
+ code = 'p'; // Protected content
+ } else if (roundedCorner.hasRoundedCorners()) {
+ code = 'r'; // Rounded corners
+ } else if (drawShadows()) {
+ code = 's'; // Shadow
+ } else if (fillsColor()) {
+ code = 'c'; // Solid color
+ } else if (hasBufferOrSidebandStream()) {
+ code = 'b';
+ }
+
+ if (hwcState.lastCompositionType == Composition::CLIENT) {
+ return static_cast<char>(std::toupper(code));
+ } else {
+ return code;
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index b8df3ed..eca9718 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -16,6 +16,7 @@
#pragma once
+#include <PowerAdvisor/Workload.h>
#include <compositionengine/LayerFECompositionState.h>
#include <renderengine/LayerSettings.h>
#include "DisplayHardware/ComposerHal.h"
@@ -23,21 +24,29 @@
#include "RequestedLayerState.h"
#include "Scheduler/LayerInfo.h"
#include "android-base/stringprintf.h"
+#include "compositionengine/LayerFE.h"
namespace android::surfaceflinger::frontend {
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
- : cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
- // Radius of the rounded rectangle.
+ // Radius of the rounded rectangle for composition
vec2 radius;
+ // Requested radius of the rounded rectangle
+ vec2 requestedRadius;
+ // Radius drawn by client for the rounded rectangle
+ vec2 clientDrawnRadius;
+ bool hasClientDrawnRadius() const {
+ return clientDrawnRadius.x > 0.0f && clientDrawnRadius.y > 0.0f;
+ }
+ bool hasRequestedRadius() const { return requestedRadius.x > 0.0f && requestedRadius.y > 0.0f; }
bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
bool operator==(RoundedCornerState const& rhs) const {
- return cropRect == rhs.cropRect && radius == rhs.radius;
+ return cropRect == rhs.cropRect && radius == rhs.radius &&
+ clientDrawnRadius == rhs.clientDrawnRadius;
}
};
@@ -140,6 +149,7 @@
bool hasBlur() const;
bool hasBufferOrSidebandStream() const;
bool hasEffect() const;
+ bool hasOutline() const;
bool hasSomethingToDraw() const;
bool isContentOpaque() const;
bool isHiddenByPolicy() const;
@@ -152,6 +162,10 @@
friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj);
void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges,
bool forceFullDamage, uint32_t displayRotationFlags);
+ // Returns a char summarizing the composition request
+ // This function tries to maintain parity with planner::Plan chars.
+ char classifyCompositionForDebug(
+ const compositionengine::LayerFE::HwcLayerDebugState& hwcState) const;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 34b1307..e3526d9 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -16,6 +16,8 @@
// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include "FrontEnd/LayerSnapshot.h"
+#include "ui/Transform.h"
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
@@ -25,6 +27,7 @@
#include <common/FlagManager.h>
#include <common/trace.h>
#include <ftl/small_map.h>
+#include <math/vec2.h>
#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
@@ -261,25 +264,21 @@
}
snapshot.isVisible = visible;
- if (FlagManager::getInstance().skip_invisible_windows_in_input()) {
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
- } 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());
}
@@ -448,15 +447,14 @@
if (args.root.getLayer()) {
// The hierarchy can have a root layer when used for screenshots otherwise, it will have
// multiple children.
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
- LayerHierarchy::Variant::Attached);
- updateSnapshotsInHierarchy(args, args.root, root, rootSnapshot, /*depth=*/0);
+ LayerHierarchy::TraversalPath childPath =
+ root.makeChild(args.root.getLayer()->id, LayerHierarchy::Variant::Attached);
+ updateSnapshotsInHierarchy(args, args.root, childPath, rootSnapshot, /*depth=*/0);
} else {
for (auto& [childHierarchy, variant] : args.root.mChildren) {
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
- childHierarchy->getLayer()->id,
- variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, root, rootSnapshot, /*depth=*/0);
+ LayerHierarchy::TraversalPath childPath =
+ root.makeChild(childHierarchy->getLayer()->id, variant);
+ updateSnapshotsInHierarchy(args, *childHierarchy, childPath, rootSnapshot, /*depth=*/0);
}
}
@@ -521,7 +519,7 @@
const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
const Args& args, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
+ const LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
int depth) {
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
"Cycle detected in LayerSnapshotBuilder. See "
@@ -550,12 +548,10 @@
bool childHasValidFrameRate = false;
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
- LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
- childHierarchy->getLayer()->id,
- variant);
+ LayerHierarchy::TraversalPath childPath =
+ traversalPath.makeChild(childHierarchy->getLayer()->id, variant);
const LayerSnapshot& childSnapshot =
- updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
- depth + 1);
+ updateSnapshotsInHierarchy(args, *childHierarchy, childPath, *snapshot, depth + 1);
updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(),
args, &childHasValidFrameRate);
}
@@ -929,7 +925,8 @@
if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
- RequestedLayerState::Changes::BufferUsageFlags)) {
+ RequestedLayerState::Changes::BufferUsageFlags) ||
+ snapshot.clientChanges & layer_state_t::eClientDrawnCornerRadiusChanged) {
updateRoundedCorner(snapshot, requested, parentSnapshot, args);
}
@@ -939,6 +936,18 @@
}
if (forceUpdate ||
+ snapshot.clientChanges &
+ (layer_state_t::eBorderSettingsChanged | layer_state_t::eAlphaChanged)) {
+ snapshot.borderSettings = requested.borderSettings;
+
+ // Multiply outline alpha by snapshot alpha.
+ uint32_t c = static_cast<uint32_t>(snapshot.borderSettings.color);
+ float alpha = snapshot.alpha * (c >> 24) / 255.0f;
+ uint32_t a = static_cast<uint32_t>(alpha * 255 + 0.5f);
+ snapshot.borderSettings.color = static_cast<int32_t>((c & ~0xff000000) | (a << 24));
+ }
+
+ if (forceUpdate ||
snapshot.changes.any(RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Input)) {
updateInput(snapshot, requested, parentSnapshot, path, args);
@@ -946,7 +955,9 @@
// computed snapshot properties
snapshot.forceClientComposition = snapshot.shadowSettings.length > 0 ||
- snapshot.stretchEffect.hasEffect() || snapshot.edgeExtensionEffect.hasEffect();
+ snapshot.stretchEffect.hasEffect() || snapshot.edgeExtensionEffect.hasEffect() ||
+ snapshot.borderSettings.strokeWidth > 0;
+
snapshot.contentOpaque = snapshot.isContentOpaque();
snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
@@ -969,19 +980,27 @@
}
snapshot.roundedCorner = RoundedCornerState();
RoundedCornerState parentRoundedCorner;
- if (parentSnapshot.roundedCorner.hasRoundedCorners()) {
+ if (parentSnapshot.roundedCorner.hasRequestedRadius()) {
parentRoundedCorner = parentSnapshot.roundedCorner;
ui::Transform t = snapshot.localTransform.inverse();
parentRoundedCorner.cropRect = t.transform(parentRoundedCorner.cropRect);
parentRoundedCorner.radius.x *= t.getScaleX();
parentRoundedCorner.radius.y *= t.getScaleY();
+ parentRoundedCorner.requestedRadius.x *= t.getScaleX();
+ parentRoundedCorner.requestedRadius.y *= t.getScaleY();
}
FloatRect layerCropRect = snapshot.croppedBufferSize;
- const vec2 radius(requested.cornerRadius, requested.cornerRadius);
- RoundedCornerState layerSettings(layerCropRect, radius);
- const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
- const bool parentRoundedCornerValid = parentRoundedCorner.hasRoundedCorners();
+ const vec2 requestedRadius(requested.cornerRadius, requested.cornerRadius);
+ const vec2 clientDrawnRadius(requested.clientDrawnCornerRadius,
+ requested.clientDrawnCornerRadius);
+ RoundedCornerState layerSettings;
+ layerSettings.cropRect = layerCropRect;
+ layerSettings.requestedRadius = requestedRadius;
+ layerSettings.clientDrawnRadius = clientDrawnRadius;
+
+ const bool layerSettingsValid = layerSettings.hasRequestedRadius() && !layerCropRect.isEmpty();
+ const bool parentRoundedCornerValid = parentRoundedCorner.hasRequestedRadius();
if (layerSettingsValid && parentRoundedCornerValid) {
// If the parent and the layer have rounded corner settings, use the parent settings if
// the parent crop is entirely inside the layer crop. This has limitations and cause
@@ -999,6 +1018,14 @@
} else if (parentRoundedCornerValid) {
snapshot.roundedCorner = parentRoundedCorner;
}
+
+ if (snapshot.roundedCorner.requestedRadius.x == requested.clientDrawnCornerRadius) {
+ // If the client drawn radius matches the requested radius, then surfaceflinger
+ // does not need to draw rounded corners for this layer
+ snapshot.roundedCorner.radius = vec2(0.f, 0.f);
+ } else {
+ snapshot.roundedCorner.radius = snapshot.roundedCorner.requestedRadius;
+ }
}
/**
@@ -1074,7 +1101,7 @@
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
const Rect geomLayerBoundsWithoutTransparentRegion =
RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
- requested.transparentRegion);
+ requested.getTransparentRegion());
snapshot.transformedBoundsWithoutTransparentRegion =
snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
snapshot.parentTransform = parentSnapshot.geomLayerTransform;
@@ -1082,7 +1109,7 @@
if (requested.potentialCursor) {
// Subtract the transparent region and snap to the bounds
const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize),
- requested.transparentRegion);
+ requested.getTransparentRegion());
snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
}
}
@@ -1116,22 +1143,14 @@
const Args& args) {
using InputConfig = gui::WindowInfo::InputConfig;
- if (requested.windowInfoHandle) {
- snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
- } else {
- snapshot.inputInfo = {};
- // b/271132344 revisit this and see if we can always use the layers uid/pid
- snapshot.inputInfo.name = requested.name;
- snapshot.inputInfo.ownerUid = gui::Uid{requested.ownerUid};
- snapshot.inputInfo.ownerPid = gui::Pid{requested.ownerPid};
- }
+ snapshot.inputInfo = requested.getWindowInfo();
snapshot.touchCropId = requested.touchCropId;
snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
snapshot.inputInfo.displayId =
ui::LogicalDisplayId{static_cast<int32_t>(snapshot.outputFilter.layerStack.id)};
snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
- ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+ ? requested.getWindowInfo().touchOcclusionMode
: parentSnapshot.inputInfo.touchOcclusionMode;
snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation ||
(requested.flags & layer_state_t::eCanOccludePresentation);
@@ -1202,13 +1221,27 @@
snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
snapshot.croppedBufferSize.getWidth()};
- // If the layer is a clone, we need to crop the input region to cloned root to prevent
- // touches from going outside the cloned area.
+ snapshot.inputInfo.cloneLayerStackTransform.reset();
+
if (path.isClone()) {
snapshot.inputInfo.inputConfig |= InputConfig::CLONE;
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH);
+
+ // Compute the transform that maps the clone's display to the layer stack space of the
+ // cloned window.
+ const LayerSnapshot* clonedSnapshot = getSnapshot(path.getClonedFrom());
+ if (clonedSnapshot != nullptr) {
+ const auto& [clonedInputBounds, s] =
+ getInputBounds(*clonedSnapshot, /*fillParentBounds=*/false);
+ ui::Transform inputToLayer;
+ inputToLayer.set(clonedInputBounds.left, clonedInputBounds.top);
+ const ui::Transform& layerToLayerStack = getInputTransform(*clonedSnapshot);
+ const auto& displayToInput = snapshot.inputInfo.transform;
+ snapshot.inputInfo.cloneLayerStackTransform =
+ layerToLayerStack * inputToLayer * displayToInput;
+ }
}
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 486cb33..94b7e5f 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -106,9 +106,10 @@
void updateSnapshots(const Args& args);
- const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath,
- const LayerSnapshot& parentSnapshot, int depth);
+ const LayerSnapshot& updateSnapshotsInHierarchy(
+ const Args&, const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
+ int depth);
void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 8892419..621fd6c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -63,8 +63,11 @@
metadata.merge(args.metadata);
changes |= RequestedLayerState::Changes::Metadata;
handleAlive = true;
- // TODO: b/305254099 remove once we don't pass invisible windows to input
- windowInfoHandle = nullptr;
+ // b/271132344 revisit this and see if we can always use the layers uid/pid
+ auto* windowInfo = editWindowInfo();
+ windowInfo->name = name;
+ windowInfo->ownerPid = ownerPid;
+ windowInfo->ownerUid = ownerUid;
if (parentId != UNASSIGNED_LAYER_ID) {
canBeRoot = false;
}
@@ -105,8 +108,9 @@
currentHdrSdrRatio = 1.f;
dataspaceRequested = false;
hdrMetadata.validTypes = 0;
- surfaceDamageRegion = Region::INVALID_REGION;
+ mNotDefCmpState.surfaceDamageRegion = Region::INVALID_REGION;
cornerRadius = 0.0f;
+ clientDrawnCornerRadius = 0.0f;
backgroundBlurRadius = 0;
api = -1;
hasColorTransform = false;
@@ -146,6 +150,7 @@
}
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
+ bool transformWasValid = transformIsValid;
const uint32_t oldFlags = flags;
const half oldAlpha = color.a;
const bool hadBuffer = externalTexture != nullptr;
@@ -277,7 +282,7 @@
if (clientState.what & layer_state_t::eReparent) {
changes |= RequestedLayerState::Changes::Parent;
parentId = resolvedComposerState.parentId;
- parentSurfaceControlForChild = nullptr;
+ mNotDefCmpState.parentSurfaceControlForChild = nullptr;
// Once a layer has be reparented, it cannot be placed at the root. It sounds odd
// but thats the existing logic and until we make this behavior more explicit, we need
// to maintain this logic.
@@ -287,7 +292,7 @@
changes |= RequestedLayerState::Changes::RelativeParent;
relativeParentId = resolvedComposerState.relativeParentId;
isRelativeOf = true;
- relativeLayerSurfaceControl = nullptr;
+ mNotDefCmpState.relativeLayerSurfaceControl = nullptr;
}
if ((clientState.what & layer_state_t::eLayerChanged ||
(clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) &&
@@ -303,7 +308,7 @@
}
if (clientState.what & layer_state_t::eInputInfoChanged) {
touchCropId = resolvedComposerState.touchCropId;
- windowInfoHandle->editInfo()->touchableRegionCropHandle.clear();
+ editWindowInfo()->touchableRegionCropHandle.clear();
}
if (clientState.what & layer_state_t::eStretchChanged) {
stretchEffect.sanitize();
@@ -348,6 +353,19 @@
requestedFrameRate.category = category;
changes |= RequestedLayerState::Changes::FrameRate;
}
+
+ if (clientState.what & layer_state_t::eClientDrawnCornerRadiusChanged) {
+ clientDrawnCornerRadius = clientState.clientDrawnCornerRadius;
+ changes |= RequestedLayerState::Changes::Geometry;
+ }
+
+ // We can't just check requestedTransform here because LayerSnapshotBuilder uses
+ // getTransform which reads destinationFrame or buffer dimensions.
+ // Display rotation does not affect validity so just use ROT_0.
+ transformIsValid = LayerSnapshot::isTransformValid(getTransform(ui::Transform::ROT_0));
+ if (!transformWasValid && transformIsValid) {
+ changes |= RequestedLayerState::Changes::Visibility;
+ }
}
ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
@@ -548,12 +566,9 @@
}
bool RequestedLayerState::hasInputInfo() const {
- if (!windowInfoHandle) {
- return false;
- }
- const auto windowInfo = windowInfoHandle->getInfo();
- return windowInfo->token != nullptr ||
- windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+ const auto& windowInfo = getWindowInfo();
+ return windowInfo.token != nullptr ||
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
bool RequestedLayerState::needsInputInfo() const {
@@ -565,13 +580,9 @@
return true;
}
- if (!windowInfoHandle) {
- return false;
- }
-
- const auto windowInfo = windowInfoHandle->getInfo();
- return windowInfo->token != nullptr ||
- windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+ const auto& windowInfo = getWindowInfo();
+ return windowInfo.token != nullptr ||
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
bool RequestedLayerState::hasBufferOrSidebandStream() const {
@@ -633,6 +644,7 @@
const uint64_t deniedChanges = layer_state_t::ePositionChanged | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eBackgroundColorChanged |
layer_state_t::eMatrixChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eClientDrawnCornerRadiusChanged |
layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBufferTransformChanged |
layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index f974ed3..b8310be 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -23,7 +23,7 @@
#include "Scheduler/LayerInfo.h"
#include "LayerCreationArgs.h"
-#include "TransactionState.h"
+#include "QueuedTransactionState.h"
namespace android::surfaceflinger::frontend {
using namespace ftl::flag_operators;
@@ -115,6 +115,7 @@
const gui::Pid ownerPid;
bool dataspaceRequested;
bool hasColorTransform;
+ bool transformIsValid = true;
bool premultipliedAlpha{true};
// This layer can be a cursor on some displays.
bool potentialCursor{false};
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index a1e8213..5bf86e5 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -28,7 +28,7 @@
namespace android::surfaceflinger::frontend {
-void TransactionHandler::queueTransaction(TransactionState&& state) {
+void TransactionHandler::queueTransaction(QueuedTransactionState&& state) {
mLocklessTransactionQueue.push(std::move(state));
mPendingTransactionCount.fetch_add(1);
SFTRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
@@ -45,9 +45,9 @@
}
}
-std::vector<TransactionState> TransactionHandler::flushTransactions() {
+std::vector<QueuedTransactionState> TransactionHandler::flushTransactions() {
// Collect transaction that are ready to be applied.
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
TransactionFlushState flushState;
flushState.queueProcessTime = systemTime();
// Transactions with a buffer pending on a barrier may be on a different applyToken
@@ -76,7 +76,7 @@
}
void TransactionHandler::applyUnsignaledBufferTransaction(
- std::vector<TransactionState>& transactions, TransactionFlushState& flushState) {
+ std::vector<QueuedTransactionState>& transactions, TransactionFlushState& flushState) {
if (!flushState.queueWithUnsignaledBuffer) {
return;
}
@@ -98,9 +98,9 @@
}
}
-void TransactionHandler::popTransactionFromPending(std::vector<TransactionState>& transactions,
- TransactionFlushState& flushState,
- std::queue<TransactionState>& queue) {
+void TransactionHandler::popTransactionFromPending(
+ std::vector<QueuedTransactionState>& transactions, TransactionFlushState& flushState,
+ std::queue<QueuedTransactionState>& queue) {
auto& transaction = queue.front();
// Transaction is ready move it from the pending queue.
flushState.firstTransaction = false;
@@ -146,8 +146,8 @@
return ready;
}
-int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
- TransactionFlushState& flushState) {
+int TransactionHandler::flushPendingTransactionQueues(
+ std::vector<QueuedTransactionState>& transactions, TransactionFlushState& flushState) {
int transactionsPendingBarrier = 0;
auto it = mPendingTransactionQueues.begin();
while (it != mPendingTransactionQueues.end()) {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 00f6bce..e78dd88 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -22,7 +22,7 @@
#include <vector>
#include <LocklessQueue.h>
-#include <TransactionState.h>
+#include <QueuedTransactionState.h>
#include <android-base/thread_annotations.h>
#include <ftl/small_map.h>
#include <ftl/small_vector.h>
@@ -35,7 +35,7 @@
class TransactionHandler {
public:
struct TransactionFlushState {
- TransactionState* transaction;
+ QueuedTransactionState* transaction;
bool firstTransaction = true;
nsecs_t queueProcessTime = 0;
// Layer handles that have transactions with buffers that are ready to be applied.
@@ -61,9 +61,9 @@
bool hasPendingTransactions();
// Moves transactions from the lockless queue.
void collectTransactions();
- std::vector<TransactionState> flushTransactions();
+ std::vector<QueuedTransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
- void queueTransaction(TransactionState&&);
+ void queueTransaction(QueuedTransactionState&&);
struct StalledTransactionInfo {
pid_t pid;
@@ -81,14 +81,15 @@
// For unit tests
friend class ::android::TestableSurfaceFlinger;
- int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
- void applyUnsignaledBufferTransaction(std::vector<TransactionState>&, TransactionFlushState&);
- void popTransactionFromPending(std::vector<TransactionState>&, TransactionFlushState&,
- std::queue<TransactionState>&);
+ int flushPendingTransactionQueues(std::vector<QueuedTransactionState>&, TransactionFlushState&);
+ void applyUnsignaledBufferTransaction(std::vector<QueuedTransactionState>&,
+ TransactionFlushState&);
+ void popTransactionFromPending(std::vector<QueuedTransactionState>&, TransactionFlushState&,
+ std::queue<QueuedTransactionState>&);
TransactionReadiness applyFilters(TransactionFlushState&);
- std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+ std::unordered_map<sp<IBinder>, std::queue<QueuedTransactionState>, IListenerHash>
mPendingTransactionQueues;
- LocklessQueue<TransactionState> mLocklessTransactionQueue;
+ LocklessQueue<QueuedTransactionState> mLocklessTransactionQueue;
std::atomic<size_t> mPendingTransactionCount = 0;
ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
index 4af27ab..f7dfeb8 100644
--- a/services/surfaceflinger/FrontEnd/Update.h
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -19,15 +19,15 @@
#include <gui/DisplayInfo.h>
#include "FrontEnd/LayerCreationArgs.h"
+#include "QueuedTransactionState.h"
#include "RequestedLayerState.h"
-#include "TransactionState.h"
namespace android::surfaceflinger::frontend {
// Atomic set of changes affecting layer state. These changes are queued in binder threads and
// applied every vsync.
struct Update {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
std::vector<sp<Layer>> legacyLayers;
std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
std::vector<LayerCreationArgs> layerCreationArgs;
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 614f33f..758b111 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -19,11 +19,11 @@
#include <android-base/thread_annotations.h>
#include <android/gui/IHdrLayerInfoListener.h>
#include <binder/IBinder.h>
+#include <ui/RingBuffer.h>
#include <utils/Timers.h>
#include <unordered_map>
-#include "Utils/RingBuffer.h"
#include "WpHash.h"
namespace android {
@@ -102,7 +102,7 @@
EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); }
};
- utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
+ ui::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Jank/JankTracker.cpp b/services/surfaceflinger/Jank/JankTracker.cpp
index 8e0e084..5e6267d 100644
--- a/services/surfaceflinger/Jank/JankTracker.cpp
+++ b/services/surfaceflinger/Jank/JankTracker.cpp
@@ -88,7 +88,8 @@
}
void JankTracker::addJankListenerLocked(int32_t layerId, sp<IBinder> listener) {
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second; it++) {
if (it->second.mListener == listener) {
// Undo the duplicate increment in addJankListener.
sListenerCount--;
@@ -106,7 +107,8 @@
std::vector<sp<IBinder>> toSend;
mLock.lock();
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second;) {
if (!jankData.empty()) {
toSend.emplace_back(it->second.mListener);
}
@@ -133,7 +135,8 @@
void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener,
int64_t afterVysnc) {
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second; it++) {
if (it->second.mListener == listener) {
it->second.mRemoveAfter = std::max(static_cast<int64_t>(0), afterVysnc);
return;
@@ -156,7 +159,8 @@
void JankTracker::dropJankListener(int32_t layerId, sp<IBinder> listener) {
const std::lock_guard<std::mutex> _l(mLock);
- for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) {
+ auto range = mJankListeners.equal_range(layerId);
+ for (auto it = range.first; it != range.second; it++) {
if (it->second.mListener == listener) {
mJankListeners.erase(it);
sListenerCount--;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 195461f..2e31282 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -64,7 +64,7 @@
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
@@ -362,7 +362,7 @@
// transaction
// ----------------------------------------------------------------------------
-void Layer::commitTransaction() {
+void Layer::commitTransaction() REQUIRES(mFlinger->mStateLock) {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -394,7 +394,8 @@
};
void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime, gui::GameMode gameMode) {
+ nsecs_t postTime, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
mDrawingState.postTime = postTime;
// Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if
@@ -458,7 +459,7 @@
void Layer::addSurfaceFramePresentedForBuffer(
std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame, nsecs_t acquireFenceTime,
- nsecs_t currentLatchTime) {
+ nsecs_t currentLatchTime) REQUIRES(mFlinger->mStateLock) {
surfaceFrame->setAcquireFenceTime(acquireFenceTime);
surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -466,7 +467,8 @@
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
- const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) {
+ const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
@@ -488,7 +490,7 @@
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
- gui::GameMode gameMode) {
+ gui::GameMode gameMode) REQUIRES(mFlinger->mStateLock) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
@@ -506,7 +508,8 @@
}
void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
- std::string debugName, gui::GameMode gameMode) {
+ std::string debugName, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return;
}
@@ -719,6 +722,10 @@
uint32_t currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
+ if (FlagManager::getInstance().monitor_buffer_fences()) {
+ buffer->getDependencyMonitor().addEgress(FenceTime::makeValid(fence), "Layer release");
+ }
+
if (listener) {
listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
}
@@ -842,7 +849,7 @@
return true;
}
-void Layer::releasePreviousBuffer() {
+void Layer::releasePreviousBuffer() REQUIRES(mFlinger->mStateLock) {
mReleasePreviousBuffer = true;
if (!mBufferInfo.mBuffer ||
(!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
@@ -884,7 +891,8 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) {
+ bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
const bool frameNumberChanged =
@@ -936,6 +944,7 @@
std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
+ mDrawingState.previousBuffer = std::move(mDrawingState.buffer);
mDrawingState.buffer = std::move(buffer);
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
? bufferData.acquireFence
@@ -1074,7 +1083,8 @@
}
bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info,
- nsecs_t postTime, gui::GameMode gameMode) {
+ nsecs_t postTime, gui::GameMode gameMode)
+ REQUIRES(mFlinger->mStateLock) {
if (mDrawingState.sidebandStream == sidebandStream) return false;
if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
@@ -1117,6 +1127,7 @@
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
handle->previousFrameNumber = mDrawingState.previousFrameNumber;
+ handle->previousBuffer = mDrawingState.previousBuffer;
if (mPreviousReleaseBufferEndpoint == handle->listener) {
// Add fence from previous screenshot now so that it can be dispatched to the
// client.
@@ -1207,7 +1218,7 @@
return false;
}
-void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
+void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) REQUIRES(mFlinger->mStateLock) {
const State& s(getDrawingState());
if (!s.buffer) {
@@ -1428,8 +1439,8 @@
presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE);
mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
- } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
- displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+ } else if (const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant());
+ displayId.has_value() && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the present timestamp instead.
const nsecs_t presentTimestamp =
mFlinger->getHwComposer().getPresentTimestamp(*displayId);
@@ -1457,7 +1468,8 @@
mBufferInfo.mFrameLatencyNeeded = false;
}
-bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
+bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly)
+ REQUIRES(mFlinger->mStateLock) {
SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
getDrawingState().frameNumber);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c234a75..88754f9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -117,6 +117,7 @@
uint32_t bufferTransform;
bool transformToDisplayInverse;
Region transparentRegionHint;
+ std::shared_ptr<renderengine::ExternalTexture> previousBuffer;
std::shared_ptr<renderengine::ExternalTexture> buffer;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
@@ -288,7 +289,7 @@
bool leaveState);
inline bool hasTrustedPresentationListener() {
- return mTrustedPresentationListener.callbackInterface != nullptr;
+ return mTrustedPresentationListener.getCallback() != nullptr;
}
// Sets the masked bits.
@@ -516,11 +517,6 @@
bool mGetHandleCalled = false;
- // The inherited shadow radius after taking into account the layer hierarchy. This is the
- // final shadow radius for this layer. If a shadow is specified for a layer, then effective
- // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
- float mEffectiveShadowRadius = 0.f;
-
// Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
gui::GameMode mGameMode = gui::GameMode::Unsupported;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index fea7671..3cd432c 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -113,6 +113,8 @@
// set the shadow for the layer if needed
prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+ layerSettings->borderSettings = mSnapshot->borderSettings;
+
return layerSettings;
}
@@ -120,6 +122,7 @@
compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
SFTRACE_CALL();
compositionengine::LayerFE::LayerSettings layerSettings;
+ layerSettings.geometry.originalBounds = mSnapshot->geomLayerBounds;
layerSettings.geometry.boundaries =
reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint);
layerSettings.geometry.positionTransform = mSnapshot->geomLayerTransform.asMatrix4();
@@ -173,7 +176,7 @@
layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect;
// Record the name of the layer for debugging further down the stack.
layerSettings.name = mSnapshot->name;
- layerSettings.luts = mSnapshot->luts;
+ layerSettings.luts = mSnapshot->luts ? mSnapshot->luts : targetSettings.luts;
if (hasEffect() && !hasBufferOrSidebandStream()) {
prepareEffectsClientComposition(layerSettings, targetSettings);
@@ -191,6 +194,7 @@
layerSettings.disableBlending = true;
layerSettings.bufferId = 0;
layerSettings.frameNumber = 0;
+ layerSettings.sequence = -1;
// If layer is blacked out, force alpha to 1 so that we draw a black color layer.
layerSettings.alpha = blackout ? 1.0f : 0.0f;
@@ -204,7 +208,7 @@
if (targetSettings.realContentIsVisible && fillsColor()) {
// Set color for color fill settings.
layerSettings.source.solidColor = mSnapshot->color.rgb;
- } else if (hasBlur() || drawShadows()) {
+ } else if (hasBlur() || drawShadows() || hasOutline()) {
layerSettings.skipContentDraw = true;
}
}
@@ -262,6 +266,7 @@
layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
layerSettings.frameNumber = mSnapshot->frameNumber;
layerSettings.bufferId = mSnapshot->externalTexture->getId();
+ layerSettings.sequence = mSnapshot->sequence;
const bool useFiltering = targetSettings.needsFiltering ||
mSnapshot->geomLayerTransform.needsBilinearFiltering();
@@ -390,6 +395,10 @@
return mSnapshot->backgroundBlurRadius > 0 || mSnapshot->blurRegions.size() > 0;
}
+bool LayerFE::hasOutline() const {
+ return mSnapshot->borderSettings.strokeWidth > 0;
+}
+
bool LayerFE::drawShadows() const {
return mSnapshot->shadowSettings.length > 0.f &&
(mSnapshot->shadowSettings.ambientColor.a > 0 ||
@@ -408,6 +417,15 @@
if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) {
return;
}
+
+ if (releaseFence.has_value()) {
+ if (FlagManager::getInstance().monitor_buffer_fences()) {
+ if (auto strongBuffer = mReleasedBuffer.promote()) {
+ strongBuffer->getDependencyMonitor()
+ .addAccessCompletion(FenceTime::makeValid(releaseFence.value()), "HWC");
+ }
+ }
+ }
mReleaseFence.set_value(releaseFence);
mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED;
}
@@ -425,4 +443,17 @@
LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() {
return mReleaseFencePromiseStatus;
}
+
+void LayerFE::setReleasedBuffer(sp<GraphicBuffer> buffer) {
+ mReleasedBuffer = std::move(buffer);
+}
+
+void LayerFE::setLastHwcState(const LayerFE::HwcLayerDebugState &state) {
+ mLastHwcState = state;
+}
+
+const LayerFE::HwcLayerDebugState& LayerFE::getLastHwcState() const {
+ return mLastHwcState;
+};
+
} // namespace android
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 9483aeb..b897a90 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,6 +18,7 @@
#include <android/gui/CachingHint.h>
#include <gui/LayerMetadata.h>
+#include <ui/GraphicBuffer.h>
#include <ui/LayerStack.h>
#include <ui/PictureProfileHandle.h>
@@ -58,8 +59,13 @@
ftl::Future<FenceResult> createReleaseFenceFuture() override;
void setReleaseFence(const FenceResult& releaseFence) override;
LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+ void setReleasedBuffer(sp<GraphicBuffer> buffer) override;
void onPictureProfileCommitted() override;
+ // Used for debugging purposes, e.g. perfetto tracing, dumpsys.
+ void setLastHwcState(const HwcLayerDebugState &state) override;
+ const HwcLayerDebugState &getLastHwcState() const override;
+
std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
private:
@@ -77,12 +83,13 @@
compositionengine::LayerFE::LayerSettings&,
compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
- bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
+ bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur() || hasOutline(); }
bool hasBufferOrSidebandStream() const;
bool fillsColor() const;
bool hasBlur() const;
bool drawShadows() const;
+ bool hasOutline() const;
const sp<GraphicBuffer> getBuffer() const;
@@ -90,6 +97,8 @@
std::string mName;
std::promise<FenceResult> mReleaseFence;
ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED;
+ HwcLayerDebugState mLastHwcState;
+ wp<GraphicBuffer> mReleasedBuffer;
};
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 44cd319..280d66e 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -278,10 +278,9 @@
stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
continue;
}
- frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
- child->getLayer()->id,
- variant);
- LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child, path);
+ LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child,
+ path.makeChild(child->getLayer()->id,
+ variant));
}
// fill in relative and parent info
@@ -338,7 +337,8 @@
}
frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot(
- frontend::LayerHierarchy::TraversalPath& path, const frontend::RequestedLayerState& layer) {
+ const frontend::LayerHierarchy::TraversalPath& path,
+ const frontend::RequestedLayerState& layer) {
frontend::LayerSnapshot* snapshot = mSnapshotBuilder.getSnapshot(path);
if (snapshot) {
return snapshot;
@@ -349,7 +349,7 @@
}
void LayerProtoFromSnapshotGenerator::writeHierarchyToProto(
- const frontend::LayerHierarchy& root, frontend::LayerHierarchy::TraversalPath& path) {
+ const frontend::LayerHierarchy& root, const frontend::LayerHierarchy::TraversalPath& path) {
using Variant = frontend::LayerHierarchy::Variant;
perfetto::protos::LayerProto* layerProto = mLayersProto.add_layers();
const frontend::RequestedLayerState& layer = *root.getLayer();
@@ -362,10 +362,8 @@
LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
for (const auto& [child, variant] : root.mChildren) {
- frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
- child->getLayer()->id,
- variant);
- frontend::LayerSnapshot* childSnapshot = getSnapshot(path, layer);
+ frontend::LayerSnapshot* childSnapshot =
+ getSnapshot(path.makeChild(child->getLayer()->id, variant), layer);
if (variant == Variant::Attached || variant == Variant::Detached ||
frontend::LayerHierarchy::isMirror(variant)) {
mChildToParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
@@ -388,10 +386,7 @@
if (variant == Variant::Detached) {
continue;
}
- frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
- child->getLayer()->id,
- variant);
- writeHierarchyToProto(*child, path);
+ writeHierarchyToProto(*child, path.makeChild(child->getLayer()->id, variant));
}
}
@@ -447,7 +442,7 @@
}
layerInfo->set_type("Layer");
- LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+ LayerProtoHelper::writeToProto(requestedState.getTransparentRegion(),
[&]() { return layerInfo->mutable_transparent_region(); });
layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 3ca553a..28924e4 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -98,8 +98,8 @@
private:
void writeHierarchyToProto(const frontend::LayerHierarchy& root,
- frontend::LayerHierarchy::TraversalPath& path);
- frontend::LayerSnapshot* getSnapshot(frontend::LayerHierarchy::TraversalPath& path,
+ const frontend::LayerHierarchy::TraversalPath& path);
+ frontend::LayerSnapshot* getSnapshot(const frontend::LayerHierarchy::TraversalPath& path,
const frontend::RequestedLayerState& layer);
const frontend::LayerSnapshotBuilder& mSnapshotBuilder;
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
deleted file mode 100644
index bfe6d2a..0000000
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ui/GraphicTypes.h>
-#include <ui/Transform.h>
-
-#include "DisplayDevice.h"
-#include "FrontEnd/LayerCreationArgs.h"
-#include "Layer.h"
-#include "LayerRenderArea.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-
-LayerRenderArea::LayerRenderArea(sp<Layer> layer, frontend::LayerSnapshot layerSnapshot,
- const Rect& crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
- const ui::Transform& layerTransform, const Rect& layerBufferSize,
- ftl::Flags<RenderArea::Options> options)
- : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, options),
- mLayer(std::move(layer)),
- mLayerSnapshot(std::move(layerSnapshot)),
- mLayerBufferSize(layerBufferSize),
- mCrop(crop),
- mTransform(layerTransform) {}
-
-const ui::Transform& LayerRenderArea::getTransform() const {
- return mTransform;
-}
-
-bool LayerRenderArea::isSecure() const {
- return mOptions.test(Options::CAPTURE_SECURE_LAYERS);
-}
-
-sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
- return nullptr;
-}
-
-Rect LayerRenderArea::getSourceCrop() const {
- if (mCrop.isEmpty()) {
- // TODO this should probably be mBounds instead of just buffer bounds
- return mLayerBufferSize;
- } else {
- return mCrop;
- }
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
deleted file mode 100644
index f72c7c7..0000000
--- a/services/surfaceflinger/LayerRenderArea.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include <ui/GraphicTypes.h>
-#include <ui/Transform.h>
-#include <utils/StrongPointer.h>
-
-#include "RenderArea.h"
-
-namespace android {
-
-class DisplayDevice;
-class Layer;
-class SurfaceFlinger;
-
-class LayerRenderArea : public RenderArea {
-public:
- LayerRenderArea(sp<Layer> layer, frontend::LayerSnapshot layerSnapshot, const Rect& crop,
- ui::Size reqSize, ui::Dataspace reqDataSpace,
- const ui::Transform& layerTransform, const Rect& layerBufferSize,
- ftl::Flags<RenderArea::Options> options);
-
- const ui::Transform& getTransform() const override;
- bool isSecure() const override;
- sp<const DisplayDevice> getDisplayDevice() const override;
- Rect getSourceCrop() const override;
-
- sp<Layer> getParentLayer() const override { return mLayer; }
- const frontend::LayerSnapshot* getLayerSnapshot() const override { return &mLayerSnapshot; }
-
-private:
- const sp<Layer> mLayer;
- const frontend::LayerSnapshot mLayerSnapshot;
- const Rect mLayerBufferSize;
- const Rect mCrop;
-
- ui::Transform mTransform;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 38dc11d..81155fd 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -49,7 +49,8 @@
using Visitor = std::function<void(Layer*)>;
private:
- const StateSet mStateSet;
+ // FIXME: This is set but not used anywhere.
+ [[maybe_unused]] const StateSet mStateSet;
};
}
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index fa0ecee..13edd16 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -12,6 +12,5 @@
ramindani@google.com
rnlee@google.com
sallyqi@google.com
-scroggo@google.com
vishnun@google.com
xwxw@google.com
diff --git a/services/surfaceflinger/PowerAdvisor/Common.h b/services/surfaceflinger/PowerAdvisor/Common.h
new file mode 100644
index 0000000..b4a87dd
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/Common.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <aidl/android/adpf/ISessionManager.h>
+#include <aidl/android/hardware/power/CompositionData.h>
+#pragma clang diagnostic pop
+
+namespace android::adpf {
+using namespace ::aidl::android::adpf;
+namespace hal = ::aidl::android::hardware::power;
+} // namespace android::adpf
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index c7d0b2c..788448d 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -28,25 +28,25 @@
#include <optional>
#include <android-base/properties.h>
+#include <android/binder_libbinder.h>
+#include <common/WorkloadTracer.h>
#include <common/trace.h>
+#include <ftl/concat.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <binder/IServiceManager.h>
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#include <powermanager/PowerHalController.h>
#include <powermanager/PowerHintSessionWrapper.h>
-#pragma clang diagnostic pop
#include <common/FlagManager.h>
#include "PowerAdvisor.h"
-
-namespace hal = aidl::android::hardware::power;
+#include "SessionManager.h"
namespace android::adpf::impl {
+using namespace android::ftl::flag_operators;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using android::hardware::EventFlag;
@@ -65,6 +65,8 @@
}
}
+static constexpr ftl::Flags<Workload> TRIGGER_LOAD_CHANGE_HINTS = Workload::EFFECTS |
+ Workload::VISIBLE_REGION | Workload::DISPLAY_CHANGES | Workload::SCREENSHOT;
} // namespace
PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn,
@@ -513,7 +515,7 @@
}
void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) {
- mExpectedPresentTimes.append(expectedPresentTime);
+ mExpectedPresentTimes.next() = expectedPresentTime;
}
void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
@@ -530,7 +532,7 @@
}
void PowerAdvisor::setCommitStart(TimePoint commitStartTime) {
- mCommitStartTimes.append(commitStartTime);
+ mCommitStartTimes.next() = commitStartTime;
}
void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) {
@@ -545,6 +547,18 @@
mTotalFrameTargetDuration = targetDuration;
}
+std::shared_ptr<SessionManager> PowerAdvisor::getSessionManager() {
+ return mSessionManager;
+}
+
+sp<IBinder> PowerAdvisor::getOrCreateSessionManagerForBinder(uid_t uid) {
+ // Flag guards the creation of SessionManager
+ if (mSessionManager == nullptr && FlagManager::getInstance().adpf_native_session_manager()) {
+ mSessionManager = ndk::SharedRefBase::make<SessionManager>(uid);
+ }
+ return AIBinder_toPlatformBinder(mSessionManager->asBinder().get());
+}
+
std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds(
std::optional<TimePoint> DisplayTimingData::*sortBy) {
std::vector<DisplayId> sortedDisplays;
@@ -565,7 +579,7 @@
}
// Tracks when we finish presenting to hwc
- TimePoint estimatedHwcEndTime = mCommitStartTimes[0];
+ TimePoint estimatedHwcEndTime = mCommitStartTimes.back();
// How long we spent this frame not doing anything, waiting for fences or vsync
Duration idleDuration = 0ns;
@@ -629,13 +643,13 @@
// Also add the frame delay duration since the target did not move while we were delayed
Duration totalDuration = mFrameDelayDuration +
std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) -
- mCommitStartTimes[0];
+ mCommitStartTimes.back();
Duration totalDurationWithoutGpu =
- mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes[0];
+ mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes.back();
// We finish SurfaceFlinger when post-composition finishes, so add that in here
Duration flingerDuration =
- estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+ estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes.back();
Duration estimatedGpuDuration = firstGpuTimeline.has_value()
? estimatedGpuEndTime.value_or(TimePoint{0ns}) - firstGpuTimeline->startTime
: Duration::fromNs(0);
@@ -647,7 +661,7 @@
hal::WorkDuration duration{
.timeStampNanos = TimePoint::now().ns(),
.durationNanos = combinedDuration.ns(),
- .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+ .workPeriodStartTimestampNanos = mCommitStartTimes.back().ns(),
.cpuDurationNanos = supportsGpuReporting() ? cpuDuration.ns() : 0,
.gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0,
};
@@ -747,4 +761,58 @@
return *mPowerHal;
}
+void PowerAdvisor::setQueuedWorkload(ftl::Flags<Workload> queued) {
+ queued &= TRIGGER_LOAD_CHANGE_HINTS;
+ if (!(queued).get()) return;
+ uint32_t previousQueuedWorkload = mQueuedWorkload.fetch_or(queued.get());
+
+ uint32_t newHints = (previousQueuedWorkload ^ queued.get()) & queued.get();
+ if (newHints) {
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("QueuedWorkload: ",
+ ftl::truncated<20>(ftl::Flags<Workload>(newHints)
+ .string()
+ .c_str()))
+ .c_str());
+ }
+ if (!previousQueuedWorkload) {
+ // TODO(b/385028458) maybe load up hint if close to wake up
+ }
+}
+
+void PowerAdvisor::setScreenshotWorkload() {
+ mCommittedWorkload |= Workload::SCREENSHOT;
+}
+
+void PowerAdvisor::setCommittedWorkload(ftl::Flags<Workload> workload) {
+ workload &= TRIGGER_LOAD_CHANGE_HINTS;
+ uint32_t queued = mQueuedWorkload.exchange(0);
+ mCommittedWorkload |= workload;
+
+ bool cancelLoadupHint = queued && !mCommittedWorkload.get();
+ if (cancelLoadupHint) {
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("UncommittedQueuedWorkload: ",
+ ftl::truncated<20>(ftl::Flags<Workload>(queued)
+ .string()
+ .c_str()))
+ .c_str());
+ // TODO(b/385028458) cancel load up hint
+ }
+
+ bool increasedWorkload = queued == 0 && mCommittedWorkload.get() != 0;
+ if (increasedWorkload) {
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("CommittedWorkload: ",
+ ftl::truncated<20>(mCommittedWorkload.string()))
+ .c_str());
+
+ // TODO(b/385028458) load up hint
+ }
+}
+
+void PowerAdvisor::setCompositedWorkload(ftl::Flags<Workload> composited) {
+ composited &= TRIGGER_LOAD_CHANGE_HINTS;
+ mCommittedWorkload = composited;
+}
} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 458b46d..b97160a 100644
--- a/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -23,6 +23,7 @@
#include <ui/DisplayId.h>
#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
#include <utils/Mutex.h>
// FMQ library in IPower does questionable conversions
@@ -32,9 +33,14 @@
#include <fmq/AidlMessageQueue.h>
#pragma clang diagnostic pop
+#include <common/trace.h>
+#include <ftl/flags.h>
#include <scheduler/Time.h>
#include <ui/DisplayIdentification.h>
#include "../Scheduler/OneShotTimer.h"
+#include "Workload.h"
+
+#include "SessionManager.h"
using namespace std::chrono_literals;
@@ -47,6 +53,8 @@
namespace adpf {
+namespace hal = aidl::android::hardware::power;
+
class PowerAdvisor {
public:
virtual ~PowerAdvisor() = default;
@@ -102,12 +110,38 @@
virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
// Sets the target duration for the entire pipeline including the gpu
virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0;
+ // Get the session manager, if it exists
+ virtual std::shared_ptr<SessionManager> getSessionManager() = 0;
+
+ // --- Track per frame workloads to use for load up hint heuristics
+ // Track queued workload from transactions as they are queued from the binder thread.
+ // The workload is accumulated and reset on frame commit. The queued workload may be
+ // relevant for the next frame so can be used as an early load up hint. Note this is
+ // only a hint because the transaction can remain in the queue and not be applied on
+ // the next frame.
+ virtual void setQueuedWorkload(ftl::Flags<Workload> workload) = 0;
+ // Track additional workload dur to a screenshot request for load up hint heuristics. This
+ // would indicate an immediate increase in GPU workload.
+ virtual void setScreenshotWorkload() = 0;
+ // Track committed workload from transactions that are applied on the main thread.
+ // This workload is determined from the applied transactions. This can provide a high
+ // confidence that the CPU and or GPU workload will increase immediately.
+ virtual void setCommittedWorkload(ftl::Flags<Workload> workload) = 0;
+ // Update committed workload with the actual workload from post composition. This is
+ // used to update the baseline workload so we can detect increases in workloads on the
+ // next commit. We use composite instead of commit to update the baseline to account
+ // for optimizations like caching which may reduce the workload.
+ virtual void setCompositedWorkload(ftl::Flags<Workload> workload) = 0;
// --- The following methods may run on threads besides SF main ---
// Send a hint about an upcoming increase in the CPU workload
virtual void notifyCpuLoadUp() = 0;
// Send a hint about the imminent start of a new CPU workload
virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
+
+ // --- The following methods specifically run on binder threads ---
+ // Retrieve a SessionManager for HintManagerService to call
+ virtual sp<IBinder> getOrCreateSessionManagerForBinder(uid_t uid) = 0;
};
namespace impl {
@@ -146,11 +180,20 @@
void setCompositeEnd(TimePoint compositeEndTime) override;
void setDisplays(std::vector<DisplayId>& displayIds) override;
void setTotalFrameTargetWorkDuration(Duration targetDuration) override;
+ std::shared_ptr<SessionManager> getSessionManager() override;
+
+ void setQueuedWorkload(ftl::Flags<Workload> workload) override;
+ void setScreenshotWorkload() override;
+ void setCommittedWorkload(ftl::Flags<Workload> workload) override;
+ void setCompositedWorkload(ftl::Flags<Workload> workload) override;
// --- The following methods may run on threads besides SF main ---
void notifyCpuLoadUp() override;
void notifyDisplayUpdateImminentAndCpuReset() override;
+ // --- The following methods specifically run on binder threads ---
+ sp<IBinder> getOrCreateSessionManagerForBinder(uid_t uid) override;
+
private:
friend class PowerAdvisorTest;
@@ -205,27 +248,6 @@
std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime);
};
- template <class T, size_t N>
- class RingBuffer {
- std::array<T, N> elements = {};
- size_t mIndex = 0;
- size_t numElements = 0;
-
- public:
- void append(T item) {
- mIndex = (mIndex + 1) % N;
- numElements = std::min(N, numElements + 1);
- elements[mIndex] = item;
- }
- bool isFull() const { return numElements == N; }
- // Allows access like [0] == current, [-1] = previous, etc..
- T& operator[](int offset) {
- size_t positiveOffset =
- static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N));
- return elements[(mIndex + positiveOffset) % N];
- }
- };
-
// Filter and sort the display ids by a given property
std::vector<DisplayId> getOrderedDisplayIds(
std::optional<TimePoint> DisplayTimingData::*sortBy);
@@ -245,9 +267,9 @@
// Last frame's post-composition duration
Duration mLastPostcompDuration{0ns};
// Buffer of recent commit start times
- RingBuffer<TimePoint, 2> mCommitStartTimes;
+ ui::RingBuffer<TimePoint, 2> mCommitStartTimes;
// Buffer of recent expected present times
- RingBuffer<TimePoint, 2> mExpectedPresentTimes;
+ ui::RingBuffer<TimePoint, 2> mExpectedPresentTimes;
// Most recent present fence time, provided by SF after composition engine finishes presenting
TimePoint mLastPresentFenceTime;
// Most recent composition engine present end time, returned with the present fence from SF
@@ -318,11 +340,18 @@
static constexpr const Duration kFenceWaitStartDelayValidated{150us};
static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
+ // Track queued and committed workloads per frame. Queued workload is atomic because it's
+ // updated on both binder and the main thread.
+ std::atomic<uint32_t> mQueuedWorkload;
+ ftl::Flags<Workload> mCommittedWorkload;
+
void sendHintSessionHint(aidl::android::hardware::power::SessionHint hint);
template <aidl::android::hardware::power::ChannelMessage::ChannelMessageContents::Tag T,
class In>
bool writeHintSessionMessage(In* elements, size_t count) REQUIRES(mHintSessionMutex);
+
+ std::shared_ptr<SessionManager> mSessionManager;
};
} // namespace impl
diff --git a/services/surfaceflinger/PowerAdvisor/SessionLayerMap.cpp b/services/surfaceflinger/PowerAdvisor/SessionLayerMap.cpp
new file mode 100644
index 0000000..9f95163
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/SessionLayerMap.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include "SessionLayerMap.h"
+#include <android/binder_libbinder.h>
+
+namespace android::adpf {
+
+void SessionLayerMap::notifySessionsDied(std::vector<int32_t>& sessionIds) {
+ for (int id : sessionIds) {
+ auto&& iter = mSessions.find(id);
+ if (iter != mSessions.end()) {
+ mSessions.erase(iter);
+ }
+ }
+}
+
+void SessionLayerMap::notifyLayersDied(std::vector<int32_t>& layers) {
+ for (auto&& layer : layers) {
+ auto&& iter = mLayers.find(layer);
+ if (iter != mLayers.end()) {
+ mLayers.erase(iter);
+ }
+ }
+}
+
+bool SessionLayerMap::bindSessionIDToLayers(int sessionId, const std::vector<int32_t>& layerIds) {
+ // If there is no association, just drop from map
+ if (layerIds.empty()) {
+ mSessions.erase(sessionId);
+ return false;
+ }
+
+ // Ensure session exists
+ if (!mSessions.contains(sessionId)) {
+ mSessions.emplace(sessionId, MappedType(sessionId, mLayers));
+ }
+
+ MappedType& session = mSessions.at(sessionId);
+ std::set<int32_t> newLinks;
+
+ // For each incoming link
+ for (auto&& layerId : layerIds) {
+ auto&& iter = mLayers.find(layerId);
+
+ // If it's not in the map, add it
+ if (iter == mLayers.end()) {
+ mLayers.emplace(layerId, MappedType(layerId, mSessions));
+ }
+
+ // Make a ref to it in the session's new association map
+ newLinks.insert(layerId);
+ }
+
+ session.swapLinks(std::move(newLinks));
+ return true;
+}
+
+void SessionLayerMap::getAssociatedSessions(int32_t layerId, std::vector<int32_t>& sessionIdsOut) {
+ sessionIdsOut.clear();
+ auto&& iter = mLayers.find(layerId);
+
+ if (iter == mLayers.end()) {
+ return;
+ }
+
+ // Dump the internal association set into this vector
+ sessionIdsOut.insert(sessionIdsOut.begin(), iter->second.mLinks.begin(),
+ iter->second.mLinks.end());
+}
+
+void SessionLayerMap::getCurrentlyRelevantLayers(
+ std::unordered_set<int32_t>& currentlyRelevantLayers) {
+ currentlyRelevantLayers.clear();
+ for (auto&& layer : mLayers) {
+ currentlyRelevantLayers.insert(layer.first);
+ }
+}
+
+} // namespace android::adpf
\ No newline at end of file
diff --git a/services/surfaceflinger/PowerAdvisor/SessionLayerMap.h b/services/surfaceflinger/PowerAdvisor/SessionLayerMap.h
new file mode 100644
index 0000000..51808a6
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/SessionLayerMap.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <log/log.h>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android::adpf {
+
+class SessionLayerMap {
+public:
+ // Inform the SessionLayerMap about dead sessions
+ void notifySessionsDied(std::vector<int32_t>& sessionIds);
+ // Inform the SessionLayerMap about dead layers
+ void notifyLayersDied(std::vector<int32_t>& layers);
+ // Associate a session with a specific set of layer ids
+ bool bindSessionIDToLayers(int sessionId, const std::vector<int32_t>& layerIds);
+ // Get the set of sessions that are mapped to a specific layer id
+ void getAssociatedSessions(int32_t layerId, std::vector<int32_t>& sessionIdsOut);
+ // Get the set of layers that are currently being tracked
+ void getCurrentlyRelevantLayers(std::unordered_set<int32_t>& currentlyRelevantLayers);
+
+private:
+ struct MappedType {
+ MappedType(int32_t id, std::unordered_map<int32_t, MappedType>& otherList)
+ : mId(id), mOtherList(otherList) {};
+ MappedType() = delete;
+ ~MappedType() { swapLinks({}); }
+
+ // Replace the set of associated IDs for this mapped type with a different set of IDs,
+ // updating only associations which have changed between the two sets
+ void swapLinks(std::set<int32_t>&& incoming) {
+ auto&& oldIter = mLinks.begin();
+ auto&& newIter = incoming.begin();
+
+ // Dump all outdated values and insert new ones
+ while (oldIter != mLinks.end() || newIter != incoming.end()) {
+ // If there is a value in the new set but not the old set
+ // We should have already ensured what we're linking to exists
+ if (oldIter == mLinks.end() || (newIter != incoming.end() && *newIter < *oldIter)) {
+ addRemoteAssociation(*newIter);
+ ++newIter;
+ continue;
+ }
+
+ // If there is a value in the old set but not the new set
+ if (newIter == incoming.end() || (oldIter != mLinks.end() && *oldIter < *newIter)) {
+ dropRemoteAssociation(*oldIter);
+ ++oldIter;
+ continue;
+ }
+
+ // If they're the same, skip
+ if (*oldIter == *newIter) {
+ ++oldIter;
+ ++newIter;
+ continue;
+ }
+ }
+
+ mLinks.swap(incoming);
+ }
+
+ void addRemoteAssociation(int32_t other) {
+ auto&& iter = mOtherList.find(other);
+ if (iter != mOtherList.end()) {
+ iter->second.mLinks.insert(mId);
+ } else {
+ ALOGE("Existing entry in SessionLayerMap, link failed");
+ }
+ }
+
+ void dropRemoteAssociation(int32_t other) {
+ auto&& iter = mOtherList.find(other);
+ if (iter != mOtherList.end()) {
+ iter->second.mLinks.erase(mId);
+ if (iter->second.mLinks.empty()) {
+ // This only erases them from the map, not from general tracking
+ mOtherList.erase(iter);
+ }
+ } else {
+ ALOGE("Missing entry in SessionLayerMap, unlinking failed");
+ }
+ }
+
+ int32_t mId;
+ std::set<int> mLinks;
+ std::unordered_map<int32_t, MappedType>& mOtherList;
+ };
+
+ std::unordered_map<int32_t, MappedType> mSessions;
+ std::unordered_map<int32_t, MappedType> mLayers;
+};
+
+} // namespace android::adpf
diff --git a/services/surfaceflinger/PowerAdvisor/SessionManager.cpp b/services/surfaceflinger/PowerAdvisor/SessionManager.cpp
new file mode 100644
index 0000000..a855f07
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/SessionManager.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include "PowerAdvisor/SessionManager.h"
+#include <android/binder_libbinder.h>
+#include <android/binder_status.h>
+#include <binder/IPCThreadState.h>
+#include "FrontEnd/LayerHandle.h"
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+
+namespace android::adpf {
+
+SessionManager::SessionManager(uid_t uid) : mUid(uid) {}
+
+ndk::ScopedAStatus SessionManager::associateSessionToLayers(
+ int32_t sessionId, int32_t ownerUid, const std::vector<::ndk::SpAIBinder>& layerTokens) {
+ std::scoped_lock lock{mSessionManagerMutex};
+
+ std::vector<int32_t> layerIds;
+
+ for (auto&& token : layerTokens) {
+ auto platformToken = AIBinder_toPlatformBinder(token.get());
+
+ // Get the layer id for it
+ int32_t layerId =
+ static_cast<int32_t>(surfaceflinger::LayerHandle::getLayerId(platformToken));
+ auto&& iter = mTrackedLayerData.find(layerId);
+
+ // Ensure it is being tracked
+ if (iter == mTrackedLayerData.end()) {
+ mTrackedLayerData.emplace(layerId, LayerData{.layerId = layerId});
+ }
+ layerIds.push_back(layerId);
+ }
+
+ // Register the session then track it
+ if (mMap.bindSessionIDToLayers(sessionId, layerIds) &&
+ !mTrackedSessionData.contains(sessionId)) {
+ mTrackedSessionData.emplace(sessionId,
+ SessionData{.sessionId = sessionId, .uid = ownerUid});
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus SessionManager::trackedSessionsDied(const std::vector<int32_t>& sessionIds) {
+ std::scoped_lock lock{mSessionManagerMutex};
+ for (int sessionId : sessionIds) {
+ mDeadSessions.push_back(sessionId);
+ mTrackedSessionData.erase(sessionId);
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+void SessionManager::updateTrackingState(
+ const std::vector<std::pair<uint32_t, std::string>>& handles) {
+ std::scoped_lock lock{mSessionManagerMutex};
+ std::vector<int32_t> deadLayers;
+ for (auto&& handle : handles) {
+ int32_t handleId = static_cast<int32_t>(handle.first);
+ auto it = mTrackedLayerData.find(handleId);
+ if (it != mTrackedLayerData.end()) {
+ // Track any dead layers to remove from the mapping
+ mTrackedLayerData.erase(it);
+ deadLayers.push_back(it->first);
+ }
+ }
+ mMap.notifyLayersDied(deadLayers);
+ mMap.notifySessionsDied(mDeadSessions);
+
+ mDeadSessions.clear();
+ mMap.getCurrentlyRelevantLayers(mCurrentlyRelevantLayers);
+}
+
+bool SessionManager::isLayerRelevant(int32_t layerId) {
+ return mCurrentlyRelevantLayers.contains(layerId);
+}
+
+} // namespace android::adpf
\ No newline at end of file
diff --git a/services/surfaceflinger/PowerAdvisor/SessionManager.h b/services/surfaceflinger/PowerAdvisor/SessionManager.h
new file mode 100644
index 0000000..afa52eb
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/SessionManager.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/adpf/BnSessionManager.h>
+#include <sys/types.h>
+
+#include <utils/Thread.h>
+#include "Common.h"
+#include "SessionLayerMap.h"
+
+#include <string>
+
+namespace android {
+
+class Layer;
+
+namespace adpf {
+namespace impl {
+
+class PowerAdvisor;
+
+}
+
+// Talks to HMS to manage sessions for PowerHAL
+class SessionManager : public BnSessionManager {
+public:
+ SessionManager(uid_t uid);
+
+ // ISessionManager binder methods
+ ndk::ScopedAStatus trackedSessionsDied(const std::vector<int32_t>& in_sessionId) override;
+ ndk::ScopedAStatus associateSessionToLayers(
+ int32_t sessionId, int32_t ownerUid,
+ const std::vector<::ndk::SpAIBinder>& layers) override;
+
+ // Update the lifecycles of any tracked sessions or layers. This is intended to accepts the
+ // "destroyedHandles" object from updateLayerSnapshots in SF, and should reflect that type
+ void updateTrackingState(const std::vector<std::pair<uint32_t, std::string>>& handles);
+
+private:
+ // Session metadata tracked by the mTrackedSessionData map
+ struct SessionData {
+ int32_t sessionId;
+ int uid;
+ };
+
+ // Layer metadata tracked by the mTrackedSessionData map
+ struct LayerData {
+ int32_t layerId;
+ };
+
+ // Checks if the layer is currently associated with a specific session in the SessionLayerMap
+ // This helps us know which layers might be included in an update for the HAL
+ bool isLayerRelevant(int32_t layerId);
+
+ // The UID of whoever created our ISessionManager connection
+ // FIXME: This is set but is not used anywhere.
+ [[maybe_unused]] const uid_t mUid;
+
+ // State owned by the main thread
+
+ // Set of layers that are currently being tracked in the SessionLayerMap. This is used to
+ // filter out which layers we actually care about during the latching process
+ std::unordered_set<int32_t> mCurrentlyRelevantLayers;
+
+ // Tracks active associations between sessions and layers. Items in this map can be thought of
+ // as "active" connections, and any session or layer not in this map will not receive updates or
+ // be collected in SurfaceFlinger
+ SessionLayerMap mMap;
+
+ // The list of currently-living layers which have ever been tracked, this is used to persist any
+ // data we want to track across potential mapping disconnects, and to determine when to send
+ // death updates
+ std::unordered_map<int32_t, LayerData> mTrackedLayerData;
+
+ // The list of currently-living sessions which have ever been tracked, this is used to persist
+ // any data we want to track across mapping disconnects
+ std::unordered_map<int32_t, SessionData> mTrackedSessionData;
+
+ // State owned by mSessionManagerMutex
+
+ std::mutex mSessionManagerMutex;
+
+ // The list of sessions that have died since we last called updateTrackingState
+ std::vector<int32_t> mDeadSessions GUARDED_BY(mSessionManagerMutex);
+};
+
+} // namespace adpf
+} // namespace android
diff --git a/services/surfaceflinger/PowerAdvisor/Workload.h b/services/surfaceflinger/PowerAdvisor/Workload.h
new file mode 100644
index 0000000..7002357
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/Workload.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <ftl/flags.h>
+#include <stdint.h>
+
+namespace android::adpf {
+// Additional composition workload that can increase cpu load.
+enum class Workload : uint32_t {
+ NONE = 0,
+ // Layer effects like blur and shadows which forces client composition
+ EFFECTS = 1 << 0,
+
+ // Geometry changes which requires HWC to validate and share composition strategy
+ VISIBLE_REGION = 1 << 1,
+
+ // Diplay changes which can cause geometry changes
+ DISPLAY_CHANGES = 1 << 2,
+
+ // Changes in sf duration which can shorten the deadline for sf to composite the frame
+ WAKEUP = 1 << 3,
+
+ // Increases in refresh rates can cause the deadline for sf to composite to be shorter
+ REFRESH_RATE_INCREASE = 1 << 4,
+
+ // Screenshot requests increase both the cpu and gpu workload
+ SCREENSHOT = 1 << 5
+};
+} // namespace android::adpf
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/QueuedTransactionState.h
similarity index 76%
rename from services/surfaceflinger/TransactionState.h
rename to services/surfaceflinger/QueuedTransactionState.h
index e5d6481..6a17a0d 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/QueuedTransactionState.h
@@ -16,15 +16,16 @@
#pragma once
-#include <condition_variable>
#include <memory>
-#include <mutex>
#include <vector>
#include "FrontEnd/LayerCreationArgs.h"
#include "renderengine/ExternalTexture.h"
+#include <PowerAdvisor/Workload.h>
#include <common/FlagManager.h>
+#include <ftl/flags.h>
#include <gui/LayerState.h>
+#include <gui/TransactionState.h>
#include <system/window.h>
namespace android {
@@ -47,34 +48,29 @@
uint32_t touchCropId = UNASSIGNED_LAYER_ID;
};
-struct TransactionState {
- TransactionState() = default;
+struct QueuedTransactionState {
+ QueuedTransactionState() = default;
- TransactionState(const FrameTimelineInfo& frameTimelineInfo,
- std::vector<ResolvedComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
- bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId,
- std::vector<uint64_t> mergedTransactionIds)
- : frameTimelineInfo(frameTimelineInfo),
- states(std::move(composerStates)),
- displays(displayStates),
- flags(transactionFlags),
- applyToken(applyToken),
- inputWindowCommands(inputWindowCommands),
- desiredPresentTime(desiredPresentTime),
- isAutoTimestamp(isAutoTimestamp),
+ QueuedTransactionState(TransactionState&& transactionState,
+ std::vector<ResolvedComposerState>&& composerStates,
+ std::vector<uint64_t>&& uncacheBufferIds, int64_t postTime,
+ int originPid, int originUid)
+ : frameTimelineInfo(std::move(transactionState.mFrameTimelineInfo)),
+ states(composerStates),
+ displays(std::move(transactionState.mDisplayStates)),
+ flags(transactionState.mFlags),
+ applyToken(transactionState.mApplyToken),
+ inputWindowCommands(std::move(transactionState.mInputWindowCommands)),
+ desiredPresentTime(transactionState.mDesiredPresentTime),
+ isAutoTimestamp(transactionState.mIsAutoTimestamp),
uncacheBufferIds(std::move(uncacheBufferIds)),
postTime(postTime),
- hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks),
+ hasListenerCallbacks(transactionState.mHasListenerCallbacks),
+ listenerCallbacks(std::move(transactionState.mListenerCallbacks)),
originPid(originPid),
originUid(originUid),
- id(transactionId),
- mergedTransactionIds(std::move(mergedTransactionIds)) {}
+ id(transactionState.getId()),
+ mergedTransactionIds(std::move(transactionState.mMergedTransactionIds)) {}
// Invokes `void(const layer_state_t&)` visitor for matching layers.
template <typename Visitor>
@@ -133,7 +129,7 @@
FrameTimelineInfo frameTimelineInfo;
std::vector<ResolvedComposerState> states;
- Vector<DisplayState> displays;
+ std::vector<DisplayState> displays;
uint32_t flags;
sp<IBinder> applyToken;
InputWindowCommands inputWindowCommands;
@@ -148,6 +144,7 @@
uint64_t id;
bool sentFenceTimeoutWarning = false;
std::vector<uint64_t> mergedTransactionIds;
+ ftl::Flags<adpf::Workload> workloadHint;
};
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 21d3396..615492a 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -39,11 +39,8 @@
#include <string>
#include "DisplayDevice.h"
-#include "DisplayRenderArea.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "Layer.h"
-#include "RenderAreaBuilder.h"
-#include "Scheduler/VsyncController.h"
#include "SurfaceFlinger.h"
namespace android {
@@ -259,6 +256,7 @@
ui::LayerStack layerStack;
ui::Transform::RotationFlags orientation;
ui::Size displaySize;
+ Rect layerStackSpaceRect;
{
// TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread
@@ -267,6 +265,7 @@
layerStack = display->getLayerStack();
orientation = ui::Transform::toRotationFlags(display->getOrientation());
displaySize = display->getSize();
+ layerStackSpaceRect = display->getLayerStackSpaceRect();
}
std::vector<RegionSamplingThread::Descriptor> descriptors;
@@ -346,20 +345,21 @@
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
constexpr bool kIsProtected = false;
- constexpr bool kAttachGainmap = false;
- SurfaceFlinger::RenderAreaBuilderVariant
- renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds,
- sampledBounds.getSize(), ui::Dataspace::V0_SRGB, displayWeak,
- RenderArea::Options::CAPTURE_SECURE_LAYERS);
+ SurfaceFlinger::ScreenshotArgs screenshotArgs;
+ screenshotArgs.captureTypeVariant = displayWeak;
+ screenshotArgs.displayIdVariant = std::nullopt;
+ screenshotArgs.sourceCrop = sampledBounds.isEmpty() ? layerStackSpaceRect : sampledBounds;
+ screenshotArgs.reqSize = sampledBounds.getSize();
+ screenshotArgs.dataspace = ui::Dataspace::V0_SRGB;
+ screenshotArgs.isSecure = true;
+ screenshotArgs.seamlessTransition = false;
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- auto displayState =
- mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
- FenceResult fenceResult =
- mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
- kIsProtected, kAttachGainmap, nullptr, displayState, layers)
- .get();
+ mFlinger.getSnapshotsFromMainThread(screenshotArgs, getLayerSnapshotsFn, layers);
+ FenceResult fenceResult = mFlinger.captureScreenshot(screenshotArgs, buffer, kRegionSampling,
+ kGrayscale, kIsProtected, nullptr, layers)
+ .get();
if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
}
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
deleted file mode 100644
index aa66ccf..0000000
--- a/services/surfaceflinger/RenderArea.h
+++ /dev/null
@@ -1,98 +0,0 @@
-#pragma once
-
-#include <ui/GraphicTypes.h>
-#include <ui/Transform.h>
-
-#include <functional>
-
-#include "FrontEnd/LayerSnapshot.h"
-#include "Layer.h"
-
-namespace android {
-
-class DisplayDevice;
-
-// RenderArea describes a rectangular area that layers can be rendered to.
-//
-// There is a logical render area and a physical render area. When a layer is
-// rendered to the render area, it is first transformed and clipped to the logical
-// render area. The transformed and clipped layer is then projected onto the
-// physical render area.
-class RenderArea {
-public:
- enum class CaptureFill {CLEAR, OPAQUE};
- enum class Options {
- // If not set, the secure layer would be blacked out or skipped
- // when rendered to an insecure render area
- CAPTURE_SECURE_LAYERS = 1 << 0,
-
- // If set, the render result may be used for system animations
- // that must preserve the exact colors of the display
- HINT_FOR_SEAMLESS_TRANSITION = 1 << 1,
- };
- static float getCaptureFillValue(CaptureFill captureFill);
-
- RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
- ftl::Flags<Options> options)
- : mOptions(options),
- mReqSize(reqSize),
- mReqDataSpace(reqDataSpace),
- mCaptureFill(captureFill) {}
-
- virtual ~RenderArea() = default;
-
- // Returns true if the render area is secure. A secure layer should be
- // blacked out / skipped when rendered to an insecure render area.
- virtual bool isSecure() const = 0;
-
- // Returns the transform to be applied on layers to transform them into
- // the logical render area.
- virtual const ui::Transform& getTransform() const = 0;
-
- // Returns the source crop of the render area. The source crop defines
- // how layers are projected from the logical render area onto the physical
- // render area. It can be larger than the logical render area. It can
- // also be optionally rotated.
- //
- // The source crop is specified in layer space (when rendering a layer and
- // its children), or in layer-stack space (when rendering all layers visible
- // on the display).
- virtual Rect getSourceCrop() const = 0;
-
- // Returns the size of the physical render area.
- int getReqWidth() const { return mReqSize.width; }
- int getReqHeight() const { return mReqSize.height; }
-
- // Returns the composition data space of the render area.
- ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
-
- // Returns the fill color of the physical render area. Regions not
- // covered by any rendered layer should be filled with this color.
- CaptureFill getCaptureFill() const { return mCaptureFill; }
-
- virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
-
- // If this is a LayerRenderArea, return the root layer of the
- // capture operation.
- virtual sp<Layer> getParentLayer() const { return nullptr; }
-
- // If this is a LayerRenderArea, return the layer snapshot
- // of the root layer of the capture operation
- virtual const frontend::LayerSnapshot* getLayerSnapshot() const { return nullptr; }
-
- // Returns whether the render result may be used for system animations that
- // must preserve the exact colors of the display.
- bool getHintForSeamlessTransition() const {
- return mOptions.test(Options::HINT_FOR_SEAMLESS_TRANSITION);
- }
-
-protected:
- ftl::Flags<Options> mOptions;
-
-private:
- const ui::Size mReqSize;
- const ui::Dataspace mReqDataSpace;
- const CaptureFill mCaptureFill;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/RenderAreaBuilder.h b/services/surfaceflinger/RenderAreaBuilder.h
deleted file mode 100644
index 599fa7e..0000000
--- a/services/surfaceflinger/RenderAreaBuilder.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "DisplayDevice.h"
-#include "DisplayRenderArea.h"
-#include "LayerRenderArea.h"
-#include "ui/Size.h"
-#include "ui/Transform.h"
-
-namespace android {
-/**
- * A parameter object for creating a render area
- */
-struct RenderAreaBuilder {
- // Source crop of the render area
- Rect crop;
-
- // Size of the physical render area
- ui::Size reqSize;
-
- // Composition data space of the render area
- ui::Dataspace reqDataSpace;
-
- ftl::Flags<RenderArea::Options> options;
- virtual std::unique_ptr<RenderArea> build() const = 0;
-
- RenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
- ftl::Flags<RenderArea::Options> options)
- : crop(crop), reqSize(reqSize), reqDataSpace(reqDataSpace), options(options) {}
-
- virtual ~RenderAreaBuilder() = default;
-};
-
-struct DisplayRenderAreaBuilder : RenderAreaBuilder {
- DisplayRenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
- wp<const DisplayDevice> displayWeak,
- ftl::Flags<RenderArea::Options> options)
- : RenderAreaBuilder(crop, reqSize, reqDataSpace, options), displayWeak(displayWeak) {}
-
- // Display that render area will be on
- wp<const DisplayDevice> displayWeak;
-
- std::unique_ptr<RenderArea> build() const override {
- return DisplayRenderArea::create(displayWeak, crop, reqSize, reqDataSpace, options);
- }
-};
-
-struct LayerRenderAreaBuilder : RenderAreaBuilder {
- LayerRenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace, sp<Layer> layer,
- bool childrenOnly, ftl::Flags<RenderArea::Options> options)
- : RenderAreaBuilder(crop, reqSize, reqDataSpace, options),
- layer(layer),
- childrenOnly(childrenOnly) {}
-
- // Root layer of the render area
- sp<Layer> layer;
-
- // Layer snapshot of the root layer
- frontend::LayerSnapshot layerSnapshot;
-
- // Transform to be applied on the layers to transform them
- // into the logical render area
- ui::Transform layerTransform{ui::Transform()};
-
- // Buffer bounds
- Rect layerBufferSize{Rect()};
-
- // If false, transform is inverted from the parent snapshot
- bool childrenOnly;
-
- // Uses parent snapshot to determine layer transform and buffer size
- void setLayerSnapshot(const frontend::LayerSnapshot& parentSnapshot) {
- layerSnapshot = parentSnapshot;
- if (!childrenOnly) {
- layerTransform = parentSnapshot.localTransform.inverse();
- }
- layerBufferSize = parentSnapshot.bufferSize;
- }
-
- std::unique_ptr<RenderArea> build() const override {
- return std::make_unique<LayerRenderArea>(layer, std::move(layerSnapshot), crop, reqSize,
- reqDataSpace, layerTransform, layerBufferSize,
- options);
- }
-};
-
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index c6d7160..5390295 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -45,7 +45,7 @@
#include <common/FlagManager.h>
#include <scheduler/FrameRateMode.h>
#include <scheduler/VsyncConfig.h>
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "VSyncDispatch.h"
#include "EventThread.h"
@@ -86,36 +86,43 @@
std::string toString(const DisplayEventReceiver::Event& event) {
switch (event.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG:
return StringPrintf("Hotplug{displayId=%s, %s}",
to_string(event.header.displayId).c_str(),
event.hotplug.connected ? "connected" : "disconnected");
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ case DisplayEventType::DISPLAY_EVENT_VSYNC:
return StringPrintf("VSync{displayId=%s, count=%u, expectedPresentationTime=%" PRId64
"}",
to_string(event.header.displayId).c_str(), event.vsync.count,
event.vsync.vsyncData.preferredExpectedPresentationTime());
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE:
return StringPrintf("ModeChanged{displayId=%s, modeId=%u}",
to_string(event.header.displayId).c_str(), event.modeChange.modeId);
- case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
return StringPrintf("HdcpLevelsChange{displayId=%s, connectedLevel=%d, maxLevel=%d}",
to_string(event.header.displayId).c_str(),
event.hdcpLevelsChange.connectedLevel,
event.hdcpLevelsChange.maxLevel);
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+ case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION:
return StringPrintf("ModeRejected{displayId=%s, modeId=%u}",
to_string(event.header.displayId).c_str(),
event.modeRejection.modeId);
- default:
- return "Event{}";
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ return StringPrintf("FrameRateOverride{displayId=%s, frameRateHz=%f}",
+ to_string(event.header.displayId).c_str(),
+ event.frameRateOverride.frameRateHz);
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ return StringPrintf("FrameRateOverrideFlush{displayId=%s}",
+ to_string(event.header.displayId).c_str());
+ case DisplayEventType::DISPLAY_EVENT_NULL:
+ return "NULL";
}
}
DisplayEventReceiver::Event makeHotplug(PhysicalDisplayId displayId, nsecs_t timestamp,
bool connected) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
+ event.header = {DisplayEventType::DISPLAY_EVENT_HOTPLUG, displayId, timestamp};
event.hotplug.connected = connected;
return event;
}
@@ -123,7 +130,7 @@
DisplayEventReceiver::Event makeHotplugError(nsecs_t timestamp, int32_t connectionError) {
DisplayEventReceiver::Event event;
PhysicalDisplayId unusedDisplayId;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, unusedDisplayId, timestamp};
+ event.header = {DisplayEventType::DISPLAY_EVENT_HOTPLUG, unusedDisplayId, timestamp};
event.hotplug.connected = false;
event.hotplug.connectionError = connectionError;
return event;
@@ -133,7 +140,7 @@
uint32_t count, nsecs_t expectedPresentationTime,
nsecs_t deadlineTimestamp) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
+ event.header = {DisplayEventType::DISPLAY_EVENT_VSYNC, displayId, timestamp};
event.vsync.count = count;
event.vsync.vsyncData.preferredFrameTimelineIndex = 0;
// Temporarily store the current vsync information in frameTimelines[0], marked as
@@ -148,7 +155,7 @@
DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ event.header = {DisplayEventType::DISPLAY_EVENT_MODE_CHANGE,
mode.modePtr->getPhysicalDisplayId(), systemTime()};
event.modeChange.modeId = ftl::to_underlying(mode.modePtr->getId());
event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
@@ -160,7 +167,7 @@
return DisplayEventReceiver::Event{
.header =
DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
+ .type = DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE,
.displayId = displayId,
.timestamp = systemTime(),
},
@@ -171,7 +178,7 @@
DisplayEventReceiver::Event makeFrameRateOverrideFlushEvent(PhysicalDisplayId displayId) {
return DisplayEventReceiver::Event{
.header = DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
+ .type = DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH,
.displayId = displayId,
.timestamp = systemTime(),
}};
@@ -182,7 +189,7 @@
return DisplayEventReceiver::Event{
.header =
DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE,
+ .type = DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE,
.displayId = displayId,
.timestamp = systemTime(),
},
@@ -195,7 +202,7 @@
return DisplayEventReceiver::Event{
.header =
DisplayEventReceiver::Event::Header{
- .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION,
+ .type = DisplayEventType::DISPLAY_EVENT_MODE_REJECTION,
.displayId = displayId,
.timestamp = systemTime(),
},
@@ -263,10 +270,10 @@
return size < 0 ? status_t(size) : status_t(NO_ERROR);
};
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
- event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE ||
+ event.header.type == DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH) {
mPendingEvents.emplace_back(event);
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE) {
return status_t(NO_ERROR);
}
@@ -344,7 +351,8 @@
auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
IPCThreadState::self()->getCallingUid(),
eventRegistration);
- if (FlagManager::getInstance().misc1()) {
+ if (FlagManager::getInstance().misc1() &&
+ !FlagManager::getInstance().disable_sched_fifo_sf_sched()) {
const int policy = SCHED_FIFO;
connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
}
@@ -504,14 +512,6 @@
mCondition.notify_all();
}
-// Merge lists of buffer stuffed Uids
-void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
- std::lock_guard<std::mutex> lock(mMutex);
- for (auto& [uid, count] : bufferStuffedUids) {
- mBufferStuffedUids.emplace_or_replace(uid, count);
- }
-}
-
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
DisplayEventConsumers consumers;
@@ -523,7 +523,7 @@
event = mPendingEvents.front();
mPendingEvents.pop_front();
- if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+ if (event->header.type == DisplayEventType::DISPLAY_EVENT_HOTPLUG) {
if (event->hotplug.connectionError == 0) {
if (event->hotplug.connected && !mVSyncState) {
mVSyncState.emplace();
@@ -635,18 +635,21 @@
};
switch (event.header.type) {
- case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ case DisplayEventType::DISPLAY_EVENT_HOTPLUG:
return true;
- case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
+ case DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE:
return true;
- case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
+ case DisplayEventType::DISPLAY_EVENT_MODE_CHANGE: {
return connection->mEventRegistration.test(
gui::ISurfaceComposer::EventRegistration::modeChanged);
}
- case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ case DisplayEventType::DISPLAY_EVENT_MODE_REJECTION:
+ return true;
+
+ case DisplayEventType::DISPLAY_EVENT_VSYNC:
switch (connection->vsyncRequest) {
case VSyncRequest::None:
return false;
@@ -672,13 +675,12 @@
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
- case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
[[fallthrough]];
- case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
+ case DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
return connection->mEventRegistration.test(
gui::ISurfaceComposer::EventRegistration::frameRateOverride);
-
- default:
+ case DisplayEventType::DISPLAY_EVENT_NULL:
return false;
}
}
@@ -751,26 +753,15 @@
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
- // List of Uids that have been sent vsync data with queued buffer count.
- // Used to keep track of which Uids can be removed from the map of
- // buffer stuffed clients.
- ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) {
const Period frameInterval = mCallback.getVsyncPeriod(consumer->mOwnerUid);
copy.vsync.vsyncData.frameInterval = frameInterval.ns();
generateFrameTimeline(copy.vsync.vsyncData, frameInterval.ns(), copy.header.timestamp,
event.vsync.vsyncData.preferredExpectedPresentationTime(),
event.vsync.vsyncData.preferredDeadlineTimestamp());
}
- auto it = mBufferStuffedUids.find(consumer->mOwnerUid);
- if (it != mBufferStuffedUids.end()) {
- copy.vsync.vsyncData.numberQueuedBuffers = it->second;
- uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid);
- } else {
- copy.vsync.vsyncData.numberQueuedBuffers = 0;
- }
switch (consumer->postEvent(copy)) {
case NO_ERROR:
break;
@@ -786,13 +777,7 @@
removeDisplayEventConnectionLocked(consumer);
}
}
- // The clients that have already received the queued buffer count
- // can be removed from the buffer stuffed Uid list to avoid
- // being sent duplicate messages.
- for (auto uid : uidsPostedQueuedBuffers) {
- mBufferStuffedUids.erase(uid);
- }
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
+ if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC &&
FlagManager::getInstance().vrr_config()) {
mLastCommittedVsyncTime =
TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime());
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 18bf416..612883a 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -56,7 +56,6 @@
// ---------------------------------------------------------------------------
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
-using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
enum class VSyncRequest {
None = -2,
@@ -141,10 +140,6 @@
virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) = 0;
-
- // An elevated number of queued buffers in the server is detected. This propagates a
- // flag to Choreographer indicating that buffer stuffing recovery should begin.
- virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
};
struct IEventThreadCallback {
@@ -199,8 +194,6 @@
void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
int32_t maxLevel) override;
- void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override;
-
private:
friend EventThreadTest;
@@ -241,10 +234,6 @@
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
- // All consumers that need to recover from buffer stuffing and the number
- // of their queued buffers.
- BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex);
-
IEventThreadCallback& mCallback;
std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 6e2b943..8c22de1 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -504,7 +504,7 @@
return FrameRateCompatibility::Exact;
case ANATIVEWINDOW_FRAME_RATE_MIN:
return FrameRateCompatibility::Min;
- case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE:
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST:
return FrameRateCompatibility::Gte;
case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
return FrameRateCompatibility::NoVote;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 2e1f938..91a798e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -24,7 +24,7 @@
#include <scheduler/interface/ICompositor.h>
#include "EventThread.h"
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "MessageQueue.h"
namespace android::impl {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4da76f6..16266c6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -124,7 +124,10 @@
// Cancel the pending refresh rate change, if any, before updating the phase configuration.
mVsyncModulator->cancelRefreshRateChange();
- mVsyncConfiguration->reset();
+ {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration->reset();
+ }
updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps);
}
@@ -211,7 +214,7 @@
.vsyncId = vsyncId,
.expectedVsyncTime = expectedVsyncTime,
.sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
- .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration,
+ .hwcMinWorkDuration = getCurrentVsyncConfigs().hwcMinWorkDuration,
.debugPresentTimeDelay = debugPresentDelay};
ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
@@ -516,12 +519,26 @@
if (!isPacesetter) return;
mRefreshRateStats->setRefreshRate(refreshRate);
- mVsyncConfiguration->setRefreshRateFps(refreshRate);
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
- refreshRate.getPeriod());
+ const auto currentConfigs = [=, this] {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration->setRefreshRateFps(refreshRate);
+ return mVsyncConfiguration->getCurrentConfigs();
+ }();
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(currentConfigs), refreshRate.getPeriod());
}
#pragma clang diagnostic pop
+void Scheduler::reloadPhaseConfiguration(Fps refreshRate, Duration minSfDuration,
+ Duration maxSfDuration, Duration appDuration) {
+ const auto currentConfigs = [=, this] {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration = std::make_unique<impl::WorkDuration>(refreshRate, minSfDuration,
+ maxSfDuration, appDuration);
+ return mVsyncConfiguration->getCurrentConfigs();
+ }();
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(currentConfigs), refreshRate.getPeriod());
+}
+
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
}
@@ -554,8 +571,7 @@
ftl::FakeGuard guard(kMainThreadContext);
for (const auto& [id, display] : mDisplays) {
- if (display.powerMode != hal::PowerMode::OFF ||
- !FlagManager::getInstance().multithreaded_present()) {
+ if (display.powerMode != hal::PowerMode::OFF) {
resyncToHardwareVsyncLocked(id, allowToEnable);
}
}
@@ -897,8 +913,11 @@
mFrameRateOverrideMappings.dump(dumper);
dumper.eol();
- mVsyncConfiguration->dump(dumper.out());
- dumper.eol();
+ {
+ std::scoped_lock lock{mVsyncConfigLock};
+ mVsyncConfiguration->dump(dumper.out());
+ dumper.eol();
+ }
mRefreshRateStats->dump(dumper.out());
dumper.eol();
@@ -961,11 +980,6 @@
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
-void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
- if (!mRenderEventThread) return;
- mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids));
-}
-
void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
@@ -986,7 +1000,7 @@
if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
const Display& pacesetter = *pacesetterOpt;
- if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ if (params.toggleIdleTimer) {
pacesetter.selectorPtr->setIdleTimerCallbacks(
{.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
.onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
@@ -1018,7 +1032,7 @@
}
void Scheduler::demotePacesetterDisplay(PromotionParams params) {
- if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ if (params.toggleIdleTimer) {
// No need to lock for reads on kMainThreadContext.
if (const auto pacesetterPtr =
FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a2cdd46..694856e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -53,6 +53,7 @@
#include "RefreshRateSelector.h"
#include "SmallAreaDetectionAllowMappings.h"
#include "Utils/Dumper.h"
+#include "VsyncConfiguration.h"
#include "VsyncModulator.h"
#include <FrontEnd/LayerHierarchy.h>
@@ -95,7 +96,7 @@
// TODO: b/241285191 - Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
void setPacesetterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext)
- EXCLUDES(mDisplayLock);
+ EXCLUDES(mDisplayLock, mVsyncConfigLock);
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -188,9 +189,19 @@
}
}
- void updatePhaseConfiguration(PhysicalDisplayId, Fps);
+ void updatePhaseConfiguration(PhysicalDisplayId, Fps) EXCLUDES(mVsyncConfigLock);
+ void reloadPhaseConfiguration(Fps, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration) EXCLUDES(mVsyncConfigLock);
- const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
+ VsyncConfigSet getCurrentVsyncConfigs() const EXCLUDES(mVsyncConfigLock) {
+ std::scoped_lock lock{mVsyncConfigLock};
+ return mVsyncConfiguration->getCurrentConfigs();
+ }
+
+ VsyncConfigSet getVsyncConfigsForRefreshRate(Fps refreshRate) const EXCLUDES(mVsyncConfigLock) {
+ std::scoped_lock lock{mVsyncConfigLock};
+ return mVsyncConfiguration->getConfigsForRefreshRate(refreshRate);
+ }
// Sets the render rate for the scheduler to run at.
void setRenderRate(PhysicalDisplayId, Fps, bool applyImmediately);
@@ -209,7 +220,6 @@
ftl::FakeGuard guard(kMainThreadContext);
resyncToHardwareVsyncLocked(id, allowToEnable, modePtr);
}
- void resync() override EXCLUDES(mDisplayLock);
void forceNextResync() { mLastResyncTime = 0; }
// Passes a vsync sample to VsyncController. Returns true if
@@ -267,7 +277,7 @@
bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
- void dump(utils::Dumper&) const;
+ void dump(utils::Dumper&) const EXCLUDES(mVsyncConfigLock);
void dump(Cycle, std::string&) const;
void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
@@ -338,10 +348,6 @@
mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
}
- // Propagates a flag to the EventThread indicating that buffer stuffing
- // recovery should begin.
- void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
-
void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; }
private:
@@ -353,7 +359,7 @@
// impl::MessageQueue overrides:
void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override
- REQUIRES(kMainThreadContext, mDisplayLock);
+ REQUIRES(kMainThreadContext, mDisplayLock) EXCLUDES(mVsyncConfigLock);
// Used to skip event dispatch before EventThread creation during boot.
// TODO: b/241285191 - Reorder Scheduler initialization to avoid this.
@@ -387,7 +393,7 @@
// a deadlock where the main thread joins with the timer thread as the timer thread waits to
// lock a mutex held by the main thread.
struct PromotionParams {
- // Whether to stop and start the idle timer. Ignored unless connected_display flag is set.
+ // Whether to stop and start the idle timer.
bool toggleIdleTimer;
};
@@ -471,6 +477,7 @@
bool throttleVsync(TimePoint, uid_t) override;
// Get frame interval
Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
+ void resync() override EXCLUDES(mDisplayLock);
void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
std::unique_ptr<EventThread> mRenderEventThread;
@@ -480,8 +487,9 @@
const FeatureFlags mFeatures;
+ mutable std::mutex mVsyncConfigLock;
// Stores phase offsets configured per refresh rate.
- const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration;
+ std::unique_ptr<VsyncConfiguration> mVsyncConfiguration GUARDED_BY(mVsyncConfigLock);
// Shifts the VSYNC phase during certain transactions and refresh rate changes.
const sp<VsyncModulator> mVsyncModulator;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ff360b7..bb04d12 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -206,7 +206,12 @@
// Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
auto it = mRateMap.find(idealPeriod());
- auto const currentPeriod = it->second.slope;
+ // Calculated slope over the period of time can become outdated as the new timestamps are
+ // stored. Using idealPeriod instead provides a rate which is valid at all the times.
+ auto const currentPeriod =
+ mDisplayModePtr->getVrrConfig() && FlagManager::getInstance().vsync_predictor_recovery()
+ ? idealPeriod()
+ : it->second.slope;
// The mean of the ordinals must be precise for the intercept calculation, so scale them up for
// fixed-point arithmetic.
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 6ae10f3..8cbb17c 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -362,6 +362,17 @@
validateSysprops();
}
+WorkDuration::WorkDuration(Fps currentRefreshRate, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration)
+ : WorkDuration(currentRefreshRate,
+ /*sfDuration*/ minSfDuration.ns(),
+ /*appDuration*/ appDuration.ns(),
+ /*sfEarlyDuration*/ maxSfDuration.ns(),
+ /*appEarlyDuration*/ appDuration.ns(),
+ /*sfEarlyGpuDuration*/ maxSfDuration.ns(),
+ /*appEarlyGpuDuration*/ appDuration.ns(),
+ /*hwcMinWorkDuration*/ 0) {}
+
WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration,
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index b6cb373..3d8ae3e 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -144,6 +144,8 @@
class WorkDuration : public VsyncConfiguration {
public:
explicit WorkDuration(Fps currentRefreshRate);
+ WorkDuration(Fps currentRefreshRate, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration);
protected:
// Used for unit tests
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
index f2be316..4dd3ab6 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -33,6 +33,10 @@
}
bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+
+ bool matchesResolution(const FrameRateMode& other) const {
+ return modePtr->getResolution() == other.modePtr->getResolution();
+ }
};
inline std::string to_string(const FrameRateMode& mode) {
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 813d4de..ff461d2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -24,6 +24,7 @@
#include <ui/DisplayId.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
#include <scheduler/Features.h>
#include <scheduler/FrameTime.h>
@@ -34,7 +35,6 @@
// TODO(b/185536303): Pull to FTL.
#include "../../../TracedOrdinal.h"
#include "../../../Utils/Dumper.h"
-#include "../../../Utils/RingBuffer.h"
namespace android::scheduler {
@@ -108,7 +108,7 @@
std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence(
Period vsyncPeriod, Period minFramePeriod) const;
std::array<PresentFence, 2> mPresentFencesLegacy;
- utils::RingBuffer<PresentFence, 5> mPresentFences;
+ ui::RingBuffer<PresentFence, 5> mPresentFences;
FrameTime mLastSignaledFrameTime;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
index 767462d..70ae940 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -36,7 +36,7 @@
using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
-using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>;
+using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayIdVariant, CompositionCoverageFlags>;
inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) {
CompositionCoverageFlags coverage;
diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
index 20c58eb..6a5eeba 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -24,6 +24,7 @@
#include <sys/timerfd.h>
#include <sys/unistd.h>
+#include <common/FlagManager.h>
#include <common/trace.h>
#include <ftl/concat.h>
#include <ftl/enum.h>
@@ -155,8 +156,10 @@
setDebugState(DebugState::Running);
struct sched_param param = {0};
param.sched_priority = 2;
- if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) {
- ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+ if (!FlagManager::getInstance().disable_sched_fifo_sf_sched()) {
+ if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) {
+ ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+ }
}
if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) {
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 41a9a1b..2906bbd 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -16,22 +16,29 @@
#include "ScreenCaptureOutput.h"
#include "ScreenCaptureRenderSurface.h"
+#include "common/include/common/FlagManager.h"
#include "ui/Rotation.h"
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/DisplayColorProfileCreationArgs.h>
#include <compositionengine/impl/DisplayColorProfile.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <ui/Rotation.h>
namespace android {
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
- ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
+ ScreenCaptureOutput, compositionengine::CompositionEngine,
+ /* sourceCrop */ const Rect, ftl::Optional<DisplayIdVariant>,
const compositionengine::Output::ColorProfile&,
- bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling,
- args.dimInGammaSpaceForEnhancedScreenshots, args.enableLocalTonemapping);
- output->editState().isSecure = args.renderArea.isSecure();
+ /* layerAlpha */ float,
+ /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop,
+ args.displayIdVariant, args.colorProfile, args.layerAlpha,
+ args.regionSampling,
+ args.dimInGammaSpaceForEnhancedScreenshots,
+ args.enableLocalTonemapping);
+ output->editState().isSecure = args.isSecure;
output->editState().isProtected = args.isProtected;
output->setCompositionEnabled(true);
output->setLayerFilter({args.layerStack});
@@ -45,16 +52,16 @@
.setHasWideColorGamut(true)
.Build()));
- const Rect& sourceCrop = args.renderArea.getSourceCrop();
+ const Rect& sourceCrop = args.sourceCrop;
const ui::Rotation orientation = ui::ROTATION_0;
output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
output->setProjection(orientation, sourceCrop,
- {args.renderArea.getReqWidth(), args.renderArea.getReqHeight()});
+ {args.reqBufferSize.width, args.reqBufferSize.height});
{
std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
- if (auto displayDevice = args.renderArea.getDisplayDevice()) {
- base::StringAppendF(&name, " for %" PRIu64, displayDevice->getId().value);
+ if (const auto id = args.displayIdVariant.and_then(asDisplayIdOfType<DisplayId>)) {
+ base::StringAppendF(&name, " for %" PRIu64, id->value);
}
output->setName(name);
}
@@ -62,11 +69,14 @@
}
ScreenCaptureOutput::ScreenCaptureOutput(
- const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile,
+ const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant,
+ const compositionengine::Output::ColorProfile& colorProfile, float layerAlpha,
bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
bool enableLocalTonemapping)
- : mRenderArea(renderArea),
+ : mSourceCrop(sourceCrop),
+ mDisplayIdVariant(displayIdVariant),
mColorProfile(colorProfile),
+ mLayerAlpha(layerAlpha),
mRegionSampling(regionSampling),
mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots),
mEnableLocalTonemapping(enableLocalTonemapping) {}
@@ -81,7 +91,7 @@
const std::shared_ptr<renderengine::ExternalTexture>& buffer) const {
auto clientCompositionDisplay =
compositionengine::impl::Output::generateClientCompositionDisplaySettings(buffer);
- clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
+ clientCompositionDisplay.clip = mSourceCrop;
auto renderIntent = static_cast<ui::RenderIntent>(clientCompositionDisplay.renderIntent);
if (mDimInGammaSpaceForEnhancedScreenshots && renderIntent != ui::RenderIntent::COLORIMETRIC &&
@@ -104,14 +114,81 @@
return clientCompositionDisplay;
}
+std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts>
+ScreenCaptureOutput::generateLuts() {
+ std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> lutsMapper;
+ if (FlagManager::getInstance().luts_api()) {
+ std::vector<sp<GraphicBuffer>> buffers;
+ std::vector<int32_t> layerIds;
+
+ for (const auto* layer : getOutputLayersOrderedByZ()) {
+ const auto& layerState = layer->getState();
+ const auto* layerFEState = layer->getLayerFE().getCompositionState();
+ auto pixelFormat = layerFEState->buffer
+ ? std::make_optional(
+ static_cast<ui::PixelFormat>(layerFEState->buffer->getPixelFormat()))
+ : std::nullopt;
+ const auto hdrType = getHdrRenderType(layerState.dataspace, pixelFormat,
+ layerFEState->desiredHdrSdrRatio);
+ if (layerFEState->buffer && !layerFEState->luts &&
+ hdrType == HdrRenderType::GENERIC_HDR) {
+ buffers.push_back(layerFEState->buffer);
+ layerIds.push_back(layer->getLayerFE().getSequence());
+ }
+ }
+
+ std::vector<aidl::android::hardware::graphics::composer3::Luts> luts;
+ if (const auto physicalDisplayId = mDisplayIdVariant.and_then(asPhysicalDisplayId)) {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ hwc.getLuts(*physicalDisplayId, buffers, &luts);
+ }
+
+ if (buffers.size() == luts.size()) {
+ for (size_t i = 0; i < luts.size(); i++) {
+ lutsMapper[layerIds[i]] = std::move(luts[i]);
+ }
+ }
+ }
+ return lutsMapper;
+}
+
std::vector<compositionengine::LayerFE::LayerSettings>
ScreenCaptureOutput::generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<compositionengine::LayerFE*>& outLayerFEs) {
+ // This map maps the layer unique id to a Lut
+ std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> lutsMapper =
+ generateLuts();
+
auto clientCompositionLayers = compositionengine::impl::Output::
generateClientCompositionRequests(supportsProtectedContent, outputDataspace,
outLayerFEs);
+ for (auto& layer : clientCompositionLayers) {
+ if (lutsMapper.find(layer.sequence) != lutsMapper.end()) {
+ auto& aidlLuts = lutsMapper[layer.sequence];
+ if (aidlLuts.pfd.get() >= 0 && aidlLuts.offsets) {
+ std::vector<int32_t> offsets = *aidlLuts.offsets;
+ std::vector<int32_t> dimensions;
+ dimensions.reserve(offsets.size());
+ std::vector<int32_t> sizes;
+ sizes.reserve(offsets.size());
+ std::vector<int32_t> keys;
+ keys.reserve(offsets.size());
+ for (size_t j = 0; j < offsets.size(); j++) {
+ dimensions.emplace_back(
+ static_cast<int32_t>(aidlLuts.lutProperties[j].dimension));
+ sizes.emplace_back(aidlLuts.lutProperties[j].size);
+ keys.emplace_back(
+ static_cast<int32_t>(aidlLuts.lutProperties[j].samplingKeys[0]));
+ }
+ layer.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(
+ aidlLuts.pfd.dup().get()),
+ offsets, dimensions, sizes, keys);
+ }
+ }
+ }
+
if (mRegionSampling) {
for (auto& layer : clientCompositionLayers) {
layer.backgroundBlurRadius = 0;
@@ -129,14 +206,16 @@
}
}
- Rect sourceCrop = mRenderArea.getSourceCrop();
compositionengine::LayerFE::LayerSettings fillLayer;
+ fillLayer.name = "ScreenCaptureFillLayer";
fillLayer.source.buffer.buffer = nullptr;
fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f);
fillLayer.geometry.boundaries =
- FloatRect(static_cast<float>(sourceCrop.left), static_cast<float>(sourceCrop.top),
- static_cast<float>(sourceCrop.right), static_cast<float>(sourceCrop.bottom));
- fillLayer.alpha = half(RenderArea::getCaptureFillValue(mRenderArea.getCaptureFill()));
+ FloatRect(static_cast<float>(mSourceCrop.left), static_cast<float>(mSourceCrop.top),
+ static_cast<float>(mSourceCrop.right),
+ static_cast<float>(mSourceCrop.bottom));
+
+ fillLayer.alpha = half(mLayerAlpha);
clientCompositionLayers.insert(clientCompositionLayers.begin(), fillLayer);
return clientCompositionLayers;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index c233ead..d4e20fc 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -20,24 +20,27 @@
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/Output.h>
#include <ui/Rect.h>
-
-#include "RenderArea.h"
+#include <unordered_map>
namespace android {
struct ScreenCaptureOutputArgs {
const compositionengine::CompositionEngine& compositionEngine;
const compositionengine::Output::ColorProfile& colorProfile;
- const RenderArea& renderArea;
ui::LayerStack layerStack;
+ Rect sourceCrop;
std::shared_ptr<renderengine::ExternalTexture> buffer;
+ ftl::Optional<DisplayIdVariant> displayIdVariant;
+ ui::Size reqBufferSize;
float sdrWhitePointNits;
float displayBrightnessNits;
// Counterintuitively, when targetBrightness > 1.0 then dim the scene.
float targetBrightness;
+ float layerAlpha;
bool regionSampling;
bool treat170mAsSrgb;
bool dimInGammaSpaceForEnhancedScreenshots;
+ bool isSecure = false;
bool isProtected = false;
bool enableLocalTonemapping = false;
};
@@ -48,10 +51,10 @@
// SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay.
class ScreenCaptureOutput : public compositionengine::impl::Output {
public:
- ScreenCaptureOutput(const RenderArea& renderArea,
+ ScreenCaptureOutput(const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant,
const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
- bool enableLocalTonemapping);
+ float layerAlpha, bool regionSampling,
+ bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping);
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -65,8 +68,11 @@
const std::shared_ptr<renderengine::ExternalTexture>& buffer) const override;
private:
- const RenderArea& mRenderArea;
+ std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> generateLuts();
+ const Rect mSourceCrop;
+ const ftl::Optional<DisplayIdVariant> mDisplayIdVariant;
const compositionengine::Output::ColorProfile& mColorProfile;
+ const float mLayerAlpha;
const bool mRegionSampling;
const bool mDimInGammaSpaceForEnhancedScreenshots;
const bool mEnableLocalTonemapping;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5947a43..9c79a87 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -19,7 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceFlinger.h"
@@ -37,12 +37,14 @@
#include <android/hardware/configstore/1.1/types.h>
#include <android/native_window.h>
#include <android/os/IInputFlinger.h>
+#include <android_os.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <common/FlagManager.h>
+#include <common/WorkloadTracer.h>
#include <common/trace.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
@@ -64,13 +66,11 @@
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
#include <ftl/future.h>
-#include <ftl/small_map.h>
#include <ftl/unit.h>
#include <gui/AidlUtil.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
-#include <gui/JankInfo.h>
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
@@ -126,7 +126,6 @@
#include <gui/SchedulingPolicy.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayIdentification.h>
-#include "ActivePictureUpdater.h"
#include "BackgroundExecutor.h"
#include "Client.h"
#include "ClientCache.h"
@@ -134,10 +133,8 @@
#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"
#include "Effects/Daltonizer.h"
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
@@ -151,13 +148,12 @@
#include "Jank/JankTracker.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
-#include "LayerRenderArea.h"
#include "LayerVector.h"
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "PowerAdvisor/PowerAdvisor.h"
+#include "PowerAdvisor/Workload.h"
#include "RegionSamplingThread.h"
-#include "RenderAreaBuilder.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
@@ -373,7 +369,7 @@
return std::abs(expectedPresentTime.ns() -
(lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns();
}
-} // namespace anonymous
+} // namespace
// ---------------------------------------------------------------------------
@@ -394,6 +390,7 @@
bool SurfaceFlinger::hasSyncFramework;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
int64_t SurfaceFlinger::minAcquiredBuffers = 1;
+std::optional<int64_t> SurfaceFlinger::maxAcquiredBuffersOpt;
uint32_t SurfaceFlinger::maxGraphicsWidth;
uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::useContextPriority;
@@ -404,7 +401,7 @@
LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
- switch(displayColorSetting) {
+ switch (displayColorSetting) {
case DisplayColorSetting::kManaged:
return std::string("Managed");
case DisplayColorSetting::kUnmanaged:
@@ -412,8 +409,7 @@
case DisplayColorSetting::kEnhanced:
return std::string("Enhanced");
default:
- return std::string("Unknown ") +
- std::to_string(static_cast<int>(displayColorSetting));
+ return std::string("Unknown ") + std::to_string(static_cast<int>(displayColorSetting));
}
}
@@ -462,6 +458,7 @@
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
minAcquiredBuffers =
SurfaceFlingerProperties::min_acquired_buffers().value_or(minAcquiredBuffers);
+ maxAcquiredBuffersOpt = SurfaceFlingerProperties::max_acquired_buffers();
maxGraphicsWidth = std::max(max_graphics_width(0), 0);
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
@@ -549,9 +546,6 @@
}
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
-
- // These are set by the HWC implementation to indicate that they will use the workarounds.
- mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -586,9 +580,10 @@
mScheduler->run();
}
-sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure,
- const std::string& uniqueId,
- float requestedRefreshRate) {
+sp<IBinder> SurfaceFlinger::createVirtualDisplay(
+ const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId,
+ float requestedRefreshRate) {
// SurfaceComposerAIDL checks for some permissions, but adding an additional check here.
// This is to ensure that only root, system, and graphics can request to create a secure
// display. Secure displays can show secure content so we add an additional restriction on it.
@@ -598,18 +593,19 @@
return nullptr;
}
+ ALOGD("Creating virtual display: %s", displayName.c_str());
+
class DisplayToken : public BBinder {
sp<SurfaceFlinger> flinger;
virtual ~DisplayToken() {
- // no more references, this display must be terminated
- Mutex::Autolock _l(flinger->mStateLock);
- flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
- flinger->setTransactionFlags(eDisplayTransactionNeeded);
- }
- public:
- explicit DisplayToken(const sp<SurfaceFlinger>& flinger)
- : flinger(flinger) {
+ // no more references, this display must be terminated
+ Mutex::Autolock _l(flinger->mStateLock);
+ flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
+ flinger->setTransactionFlags(eDisplayTransactionNeeded);
}
+
+ public:
+ explicit DisplayToken(const sp<SurfaceFlinger>& flinger) : flinger(flinger) {}
};
sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this));
@@ -621,6 +617,9 @@
// Set display as protected when marked as secure to ensure no behavior change
// TODO (b/314820005): separate as a different arg when creating the display.
state.isProtected = isSecure;
+ state.optimizationPolicy = optimizationPolicy;
+ // Virtual displays start in ON mode.
+ state.initialPowerMode = hal::PowerMode::ON;
state.displayName = displayName;
state.uniqueId = uniqueId;
state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
@@ -642,6 +641,9 @@
ALOGE("%s: Invalid operation on physical display", __func__);
return INVALID_OPERATION;
}
+
+ ALOGD("Destroying virtual display: %s", state.displayName.c_str());
+
mCurrentState.displays.removeItemsAt(index);
setTransactionFlags(eDisplayTransactionNeeded);
return NO_ERROR;
@@ -658,14 +660,16 @@
}
}
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
- const std::string& uniqueId,
- bool canAllocateHwcForVDS) {
+std::optional<VirtualDisplayIdVariant> SurfaceFlinger::acquireVirtualDisplay(
+ ui::Size resolution, ui::PixelFormat format, const std::string& uniqueId,
+ compositionengine::DisplayCreationArgsBuilder& builder,
+ bool canAllocateHwcForVDS) {
auto& generator = mVirtualDisplayIdGenerators.hal;
if (canAllocateHwcForVDS && generator) {
if (const auto id = generator->generateId()) {
if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
acquireVirtualDisplaySnapshot(*id, uniqueId);
+ builder.setId(*id);
return *id;
}
@@ -680,22 +684,23 @@
const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
acquireVirtualDisplaySnapshot(*id, uniqueId);
+ builder.setId(*id);
return *id;
}
-void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) {
- if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
- if (auto& generator = mVirtualDisplayIdGenerators.hal) {
- generator->releaseId(*id);
- releaseVirtualDisplaySnapshot(*id);
- }
- return;
- }
-
- const auto id = GpuVirtualDisplayId::tryCast(displayId);
- LOG_ALWAYS_FATAL_IF(!id);
- mVirtualDisplayIdGenerators.gpu.releaseId(*id);
- releaseVirtualDisplaySnapshot(*id);
+void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayIdVariant displayId) {
+ ftl::match(
+ displayId,
+ [this](HalVirtualDisplayId halVirtualDisplayId) {
+ if (auto& generator = mVirtualDisplayIdGenerators.hal) {
+ generator->releaseId(halVirtualDisplayId);
+ releaseVirtualDisplaySnapshot(halVirtualDisplayId);
+ }
+ },
+ [this](GpuVirtualDisplayId gpuVirtualDisplayId) {
+ mVirtualDisplayIdGenerators.gpu.releaseId(gpuVirtualDisplayId);
+ releaseVirtualDisplaySnapshot(gpuVirtualDisplayId);
+ });
}
void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) {
@@ -752,13 +757,16 @@
mBootFinished = true;
FlagManager::getMutableInstance().markBootCompleted();
- ::tracing_perfetto::registerWithPerfetto();
+ if (android::os::perfetto_sdk_tracing()) {
+ ::tracing_perfetto::registerWithPerfetto();
+ }
+
mInitBootPropsFuture.wait();
mRenderEnginePrimeCacheFuture.wait();
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
- ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
+ ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)));
mFrameTracer->initialize();
mFrameTimeline->onBootFinished();
@@ -777,8 +785,7 @@
property_set("service.bootanim.exit", "1");
const int LOGTAG_SF_STOP_BOOTANIM = 60110;
- LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
- ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
@@ -886,6 +893,8 @@
return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
} else if (algorithm == "kawase2") {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
+ } else if (algorithm == "kawase") {
+ return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
} else {
if (FlagManager::getInstance().window_blur_kawase2()) {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
@@ -896,8 +905,8 @@
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
SFTRACE_CALL();
- ALOGI( "SurfaceFlinger's main thread ready to run. "
- "Initializing graphics H/W...");
+ ALOGI("SurfaceFlinger's main thread ready to run. "
+ "Initializing graphics H/W...");
addTransactionReadyFilters();
Mutex::Autolock lock(mStateLock);
@@ -927,7 +936,8 @@
mCompositionEngine->setTimeStats(mTimeStats);
- mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
+ mHWComposer = getFactory().createHWComposer(mHwcServiceName);
+ mCompositionEngine->setHwComposer(mHWComposer.get());
auto& composer = mCompositionEngine->getHwComposer();
composer.setCallback(*this);
mDisplayModeController.setHwComposer(&composer);
@@ -1015,9 +1025,8 @@
mPowerAdvisor->init();
if (base::GetBoolProperty("service.sf.prime_shader_cache"s, true)) {
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Can't set SCHED_OTHER for primeCache");
- }
+ constexpr const char* kWhence = "primeCache";
+ setSchedFifo(false, kWhence);
mRenderEnginePrimeCacheFuture.callOnce([this] {
renderengine::PrimeCacheConfig config;
@@ -1053,9 +1062,7 @@
return getRenderEngine().primeCache(config);
});
- if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Can't set SCHED_FIFO after primeCache");
- }
+ setSchedFifo(true, kWhence);
}
// Avoid blocking the main thread on `init` to set properties.
@@ -1073,7 +1080,8 @@
void SurfaceFlinger::initBootProperties() {
property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0");
- if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) {
+ if (base::GetBoolProperty("debug.sf.boot_animation"s, true) &&
+ (base::GetIntProperty("debug.sf.nobootanimation"s, 0) == 0)) {
// Reset and (if needed) start BootAnimation.
property_set("service.bootanim.exit", "0");
property_set("service.bootanim.progress", "0");
@@ -1121,17 +1129,16 @@
static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
}
-status_t SurfaceFlinger::getSupportedFrameTimestamps(
- std::vector<FrameEvent>* outSupported) const {
+status_t SurfaceFlinger::getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const {
*outSupported = {
- FrameEvent::REQUESTED_PRESENT,
- FrameEvent::ACQUIRE,
- FrameEvent::LATCH,
- FrameEvent::FIRST_REFRESH_START,
- FrameEvent::LAST_REFRESH_START,
- FrameEvent::GPU_COMPOSITION_DONE,
- FrameEvent::DEQUEUE_READY,
- FrameEvent::RELEASE,
+ FrameEvent::REQUESTED_PRESENT,
+ FrameEvent::ACQUIRE,
+ FrameEvent::LATCH,
+ FrameEvent::FIRST_REFRESH_START,
+ FrameEvent::LAST_REFRESH_START,
+ FrameEvent::GPU_COMPOSITION_DONE,
+ FrameEvent::DEQUEUE_READY,
+ FrameEvent::RELEASE,
};
if (mHasReliablePresentFences) {
@@ -1168,8 +1175,8 @@
}
Mutex::Autolock lock(mStateLock);
- const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
- const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
+ const PhysicalDisplayId id = PhysicalDisplayId::fromValue(static_cast<uint64_t>(displayId));
+ const auto displayOpt = mPhysicalDisplays.get(id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
@@ -1179,6 +1186,7 @@
const auto& snapshot = snapshotRef.get();
info->connectionType = snapshot.connectionType();
+ info->port = snapshot.port();
info->deviceProductInfo = snapshot.deviceProductInfo();
if (mEmulatedDisplayDensity) {
@@ -1225,8 +1233,8 @@
outMode.peakRefreshRate = peakFps.getValue();
outMode.vsyncRate = mode->getVsyncRate().getValue();
- const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(
- Fps::fromValue(outMode.peakRefreshRate));
+ const auto vsyncConfigSet =
+ mScheduler->getVsyncConfigsForRefreshRate(Fps::fromValue(outMode.peakRefreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
outMode.group = mode->getGroup();
@@ -1262,7 +1270,17 @@
ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
info->frameRateCategoryRate = frameRateCategoryRate;
- info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
+ if (info->hasArrSupport) {
+ info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
+ } else {
+ // On non-ARR devices, list the refresh rates same as the supported display modes.
+ std::vector<float> supportedFrameRates;
+ supportedFrameRates.reserve(info->supportedDisplayModes.size());
+ std::transform(info->supportedDisplayModes.begin(), info->supportedDisplayModes.end(),
+ std::back_inserter(supportedFrameRates),
+ [](ui::DisplayMode mode) { return mode.peakRefreshRate; });
+ info->supportedRefreshRates = supportedFrameRates;
+ }
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
@@ -1291,9 +1309,9 @@
Mutex::Autolock lock(mStateLock);
- const auto id_ =
- DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
- const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+ const PhysicalDisplayId id =
+ PhysicalDisplayId::fromValue(static_cast<uint64_t>(physicalDisplayId));
+ const auto displayOpt = mPhysicalDisplays.get(id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
@@ -1368,7 +1386,8 @@
const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
if (!selectorPtr) break;
- const Fps renderRate = selectorPtr->getActiveMode().fps;
+ const auto activeMode = selectorPtr->getActiveMode();
+ const Fps renderRate = activeMode.fps;
// DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler.
mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */);
@@ -1387,6 +1406,15 @@
mScheduler->updatePhaseConfiguration(displayId, mode.fps);
mScheduler->setModeChangePending(true);
+
+ // The mode set to switch resolution is not initiated until the display transaction that
+ // resizes the display. DM sends this transaction in response to a mode change event, so
+ // emit the event now, not when finalizing the mode change as for a refresh rate switch.
+ if (FlagManager::getInstance().synced_resolution_switch() &&
+ !mode.matchesResolution(activeMode)) {
+ mScheduler->onDisplayModeChanged(displayId, mode,
+ /*clearContentRequirements*/ true);
+ }
break;
}
case DesiredModeAction::InitiateRenderRateSwitch:
@@ -1454,30 +1482,36 @@
return future.get();
}
-void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
+bool SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId);
if (!pendingModeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
- return;
+ return true;
}
const auto& activeMode = pendingModeOpt->mode;
+ const bool resolutionMatch = !FlagManager::getInstance().synced_resolution_switch() ||
+ activeMode.matchesResolution(mDisplayModeController.getActiveMode(displayId));
- if (const auto oldResolution =
- mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
- oldResolution != activeMode.modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
- // We need to generate new sequenceId in order to recreate the display (and this
- // way the framebuffer).
- state.sequenceId = DisplayDeviceState{}.sequenceId;
- state.physical->activeMode = activeMode.modePtr.get();
- processDisplayChangesLocked();
+ if (!FlagManager::getInstance().synced_resolution_switch()) {
+ if (const auto oldResolution =
+ mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
+ oldResolution != activeMode.modePtr->getResolution()) {
+ auto& state =
+ mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
+ // We need to generate new sequenceId in order to recreate the display (and this
+ // way the framebuffer).
+ state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.physical->activeMode = activeMode.modePtr.get();
+ processDisplayChangesLocked();
- // processDisplayChangesLocked will update all necessary components so we're done here.
- return;
+ // The DisplayDevice has been destroyed, so abort the commit for the now dead
+ // FrameTargeter.
+ return false;
+ }
}
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
@@ -1485,9 +1519,12 @@
mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
- if (pendingModeOpt->emitEvent) {
+ // Skip for resolution changes, since the event was already emitted on setting the desired mode.
+ if (resolutionMatch && pendingModeOpt->emitEvent) {
mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
}
+
+ return true;
}
void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) {
@@ -1535,8 +1572,9 @@
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
to_string(displayId).c_str());
- if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
- mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ const auto activeMode = mDisplayModeController.getActiveMode(displayId);
+
+ if (!desiredModeOpt->force && desiredModeOpt->mode == activeMode) {
applyActiveMode(displayId);
continue;
}
@@ -1557,6 +1595,15 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
+ // When initiating a resolution change, wait until the commit that resizes the display.
+ if (FlagManager::getInstance().synced_resolution_switch() &&
+ !activeMode.matchesResolution(desiredModeOpt->mode)) {
+ const auto display = getDisplayDeviceLocked(displayId);
+ if (display->getSize() != desiredModeOpt->mode.modePtr->getResolution()) {
+ continue;
+ }
+ }
+
const auto error =
mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
constraints, outTimeline);
@@ -2166,7 +2213,6 @@
}
hdrInfoReporter->addListener(listener);
-
mAddingHDRLayerInfoListener = true;
return OK;
}
@@ -2227,13 +2273,13 @@
const auto cycle = [&] {
if (FlagManager::getInstance().deprecate_vsync_sf()) {
ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger,
- "requested unsupported config eVsyncSourceSurfaceFlinger");
+ "requested unsupported config eVsyncSourceSurfaceFlinger");
return scheduler::Cycle::Render;
}
return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
- ? scheduler::Cycle::LastComposite
- : scheduler::Cycle::Render;
+ ? scheduler::Cycle::LastComposite
+ : scheduler::Cycle::Render;
}();
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
@@ -2262,20 +2308,6 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
- vsyncPeriod.has_value()) {
- if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
- const int32_t value = static_cast<int32_t>(-timestamp);
- // one byte is good enough to encode android.hardware.drm.HdcpLevel
- const int32_t maxLevel = (value >> 8) & 0xFF;
- const int32_t connectedLevel = value & 0xFF;
- ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64,
- __func__, connectedLevel, maxLevel, hwcDisplayId);
- updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
- return;
- }
- }
-
SFTRACE_NAME(vsyncPeriod
? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
: ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
@@ -2292,12 +2324,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) {
@@ -2315,9 +2347,19 @@
return;
}
- if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE &&
- !FlagManager::getInstance().display_config_error_hal()) {
- return;
+ if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE) {
+ if (!FlagManager::getInstance().display_config_error_hal()) {
+ return;
+ }
+ {
+ std::lock_guard<std::mutex> lock(mHotplugMutex);
+ mPendingHotplugEvents.push_back(
+ HotplugEvent{hwcDisplayId, HWComposer::HotplugEvent::LinkUnstable});
+ }
+ if (mScheduler) {
+ mScheduler->scheduleConfigure();
+ }
+ // do not return to also report the error.
}
// TODO(b/311403559): use enum type instead of int
@@ -2459,9 +2501,11 @@
}
bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs,
- bool flushTransactions, bool& outTransactionsAreEmpty) {
+ bool flushTransactions, bool& outTransactionsAreEmpty)
+ EXCLUDES(mStateLock) {
using Changes = frontend::RequestedLayerState::Changes;
SFTRACE_CALL();
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Transaction Handling");
frontend::Update update;
if (flushTransactions) {
SFTRACE_NAME("TransactionHandler:flushTransactions");
@@ -2488,8 +2532,20 @@
mDestroyedHandles.clear();
}
+ size_t addedLayers = update.newLayers.size();
mLayerLifecycleManager.addLayers(std::move(update.newLayers));
update.transactions = mTransactionHandler.flushTransactions();
+ ftl::Flags<adpf::Workload> committedWorkload;
+ for (auto& transaction : update.transactions) {
+ committedWorkload |= transaction.workloadHint;
+ }
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("Layers: +", addedLayers, " -",
+ update.destroyedHandles.size(),
+ " txns:", update.transactions.size())
+ .c_str());
+
+ mPowerAdvisor->setCommittedWorkload(committedWorkload);
if (mTransactionTracing) {
mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
update, mFrontEndDisplayInfos,
@@ -2643,7 +2699,7 @@
}
bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId,
- const scheduler::FrameTargets& frameTargets) {
+ const scheduler::FrameTargets& frameTargets) EXCLUDES(mStateLock) {
const scheduler::FrameTarget& pacesetterFrameTarget = *frameTargets.get(pacesetterId)->get();
const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
@@ -2669,7 +2725,10 @@
for (const auto [displayId, _] : frameTargets) {
if (mDisplayModeController.isModeSetPending(displayId)) {
- finalizeDisplayModeChange(displayId);
+ if (!finalizeDisplayModeChange(displayId)) {
+ mScheduler->scheduleFrame();
+ return false;
+ }
}
}
}
@@ -2687,7 +2746,7 @@
return false;
}
}
-
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Commit");
const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
// Save this once per commit + composite to ensure consistency
@@ -2737,9 +2796,10 @@
// setTransactionFlags which will schedule another SF frame. This was if the tracker
// needs to adjust the vsync timeline, it will be done before the next frame.
if (FlagManager::getInstance().vrr_config() && mustComposite) {
- mScheduler->getVsyncSchedule()->getTracker().onFrameBegin(
- pacesetterFrameTarget.expectedPresentTime(),
- pacesetterFrameTarget.lastSignaledFrameTime());
+ mScheduler->getVsyncSchedule()
+ ->getTracker()
+ .onFrameBegin(pacesetterFrameTarget.expectedPresentTime(),
+ pacesetterFrameTarget.lastSignaledFrameTime());
}
if (transactionFlushNeeded()) {
setTransactionFlags(eTransactionFlushNeeded);
@@ -2761,6 +2821,7 @@
// Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
// and may eventually call to ~Layer() if it holds the last reference
{
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Refresh Rate Selection");
bool updateAttachedChoreographer = mUpdateAttachedChoreographer;
mUpdateAttachedChoreographer = false;
@@ -2787,6 +2848,8 @@
CompositeResultsPerDisplay SurfaceFlinger::composite(
PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters& frameTargeters) {
+ SFTRACE_ASYNC_FOR_TRACK_BEGIN(WorkloadTracer::TRACK_NAME, "Composition",
+ WorkloadTracer::COMPOSITION_TRACE_COOKIE);
const scheduler::FrameTarget& pacesetterTarget =
frameTargeters.get(pacesetterId)->get()->target();
@@ -2798,12 +2861,45 @@
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+ // Track layer stacks of physical displays that might be added to CompositionEngine
+ // output. Layer stacks are not tracked in Display when we iterate through
+ // frameTargeters. Cross-referencing layer stacks allows us to filter out displays
+ // by ID with duplicate layer stacks before adding them to CompositionEngine output.
+ ui::DisplayMap<PhysicalDisplayId, ui::LayerStack> physicalDisplayLayerStacks;
+ for (auto& [_, display] : displays) {
+ const auto id = asPhysicalDisplayId(display->getDisplayIdVariant());
+ if (id && frameTargeters.contains(*id)) {
+ physicalDisplayLayerStacks.try_emplace(*id, display->getLayerStack());
+ }
+ }
+
+ // Tracks layer stacks of displays that are added to CompositionEngine output.
+ ui::DisplayMap<ui::LayerStack, ftl::Unit> outputLayerStacks;
+ auto isUniqueOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) {
+ if (FlagManager::getInstance().reject_dupe_layerstacks()) {
+ if (layerStack != ui::INVALID_LAYER_STACK && outputLayerStacks.contains(layerStack)) {
+ // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is
+ // removed
+ ALOGD("Existing layer stack ID %d output to another display %" PRIu64
+ ", dropping display from outputs",
+ layerStack.id, id.value);
+ return false;
+ }
+ }
+
+ outputLayerStacks.try_emplace(layerStack);
+ return true;
+ };
+
// Add outputs for physical displays.
for (const auto& [id, targeter] : frameTargeters) {
ftl::FakeGuard guard(mStateLock);
if (const auto display = getCompositionDisplayLocked(id)) {
- refreshArgs.outputs.push_back(display);
+ const auto layerStack = physicalDisplayLayerStacks.get(id)->get();
+ if (isUniqueOutputLayerStack(display->getId(), layerStack)) {
+ refreshArgs.outputs.push_back(display);
+ }
}
refreshArgs.frameTargets.try_emplace(id, &targeter->target());
@@ -2820,7 +2916,9 @@
if (!refreshRate.isValid() ||
mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) {
- refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ if (isUniqueOutputLayerStack(display->getId(), display->getLayerStack())) {
+ refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ }
}
}
}
@@ -2870,7 +2968,7 @@
for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
auto compositionDisplay = display->getCompositionDisplay();
if (!compositionDisplay->getState().isEnabled) continue;
- for (auto outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) {
+ for (const auto* outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) {
if (outputLayer->getLayerFE().getCompositionState() == nullptr) {
// This is unexpected but instead of crashing, capture traces to disk
// and recover gracefully by forcing CE to rebuild layer stack.
@@ -2916,19 +3014,103 @@
}
mCompositionEngine->present(refreshArgs);
- moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+ ftl::Flags<adpf::Workload> compositedWorkload;
+ if (refreshArgs.updatingGeometryThisFrame || refreshArgs.updatingOutputGeometryThisFrame) {
+ compositedWorkload |= adpf::Workload::VISIBLE_REGION;
+ }
+ if (mFrontEndDisplayInfosChanged) {
+ compositedWorkload |= adpf::Workload::DISPLAY_CHANGES;
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Display Changes");
+ }
+ int index = 0;
+ ftl::StaticVector<char, WorkloadTracer::COMPOSITION_SUMMARY_SIZE> compositionSummary;
+ auto lastLayerStack = ui::INVALID_LAYER_STACK;
+
+ uint64_t prevOverrideBufferId = 0;
for (auto& [layer, layerFE] : layers) {
CompositionResult compositionResult{layerFE->stealCompositionResult()};
+ if (lastLayerStack != layerFE->mSnapshot->outputFilter.layerStack) {
+ if (lastLayerStack != ui::INVALID_LAYER_STACK) {
+ // add a space to separate displays
+ compositionSummary.push_back(' ');
+ }
+ lastLayerStack = layerFE->mSnapshot->outputFilter.layerStack;
+ }
+
+ // If there are N layers in a cached set they should all share the same buffer id.
+ // The first layer in the cached set will be not skipped and layers 1..N-1 will be skipped.
+ // We expect all layers in the cached set to be marked as composited by HWC.
+ // Here is a made up example of how it is visualized
+ //
+ // [b:rrc][s:cc]
+ //
+ // This should be interpreted to mean that there are 2 cached sets.
+ // So there are only 2 non skipped layers -- b and s.
+ // The layers rrc and cc are flattened into layers b and s respectively.
+ const LayerFE::HwcLayerDebugState& hwcState = layerFE->getLastHwcState();
+ if (hwcState.overrideBufferId != prevOverrideBufferId) {
+ // End the existing run.
+ if (prevOverrideBufferId) {
+ compositionSummary.push_back(']');
+ }
+ // Start a new run.
+ if (hwcState.overrideBufferId) {
+ compositionSummary.push_back('[');
+ }
+ }
+
+ compositionSummary.push_back(layerFE->mSnapshot->classifyCompositionForDebug(hwcState));
+
+ if (hwcState.overrideBufferId && !hwcState.wasSkipped) {
+ compositionSummary.push_back(':');
+ }
+ prevOverrideBufferId = hwcState.overrideBufferId;
+
+ if (layerFE->mSnapshot->hasEffect()) {
+ compositedWorkload |= adpf::Workload::EFFECTS;
+ }
+
if (compositionResult.lastClientCompositionFence) {
layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
}
if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
- mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult);
+ mActivePictureTracker.onLayerComposed(*layer, *layerFE, compositionResult);
+ }
+ }
+ // End the last run.
+ if (prevOverrideBufferId) {
+ compositionSummary.push_back(']');
+ }
+
+ // Concisely describe the layers composited this frame using single chars. GPU composited layers
+ // are uppercase, DPU composited are lowercase. Special chars denote effects (blur, shadow,
+ // etc.). This provides a snapshot of the compositing workload.
+ SFTRACE_INSTANT_FOR_TRACK(WorkloadTracer::TRACK_NAME,
+ ftl::Concat("Layers: ", layers.size(), " ",
+ ftl::truncated<WorkloadTracer::COMPOSITION_SUMMARY_SIZE>(
+ std::string_view(compositionSummary.begin(),
+ compositionSummary.size())))
+ .c_str());
+
+ mPowerAdvisor->setCompositedWorkload(compositedWorkload);
+ SFTRACE_ASYNC_FOR_TRACK_END(WorkloadTracer::TRACK_NAME,
+ WorkloadTracer::COMPOSITION_TRACE_COOKIE);
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Post Composition");
+ SFTRACE_NAME("postComposition");
+
+ if (mDisplayModeController.supportsHdcp()) {
+ for (const auto& [id, _] : frameTargeters) {
+ ftl::FakeGuard guard(mStateLock);
+ if (const auto display = getCompositionDisplayLocked(id)) {
+ if (!display->isSecure() && display->hasSecureLayers()) {
+ mDisplayModeController.startHdcpNegotiation(id);
+ }
+ }
}
}
- SFTRACE_NAME("postComposition");
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime());
// Send a power hint after presentation is finished.
@@ -2962,7 +3144,7 @@
for (const auto& [_, display] : displays) {
const auto& state = display->getCompositionDisplay()->getState();
CompositionCoverageFlags& flags =
- mCompositionCoverage.try_emplace(display->getId()).first->second;
+ mCompositionCoverage.try_emplace(display->getDisplayIdVariant()).first->second;
if (state.usesDeviceComposition) {
flags |= CompositionCoverage::Hwc;
@@ -3016,8 +3198,8 @@
CompositeResultsPerDisplay resultsPerDisplay;
// Filter out virtual displays.
- for (const auto& [id, coverage] : mCompositionCoverage) {
- if (const auto idOpt = PhysicalDisplayId::tryCast(id)) {
+ for (const auto& [idVar, coverage] : mCompositionCoverage) {
+ if (const auto idOpt = asPhysicalDisplayId(idVar)) {
resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage});
}
}
@@ -3055,16 +3237,12 @@
return false;
}
-ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
+ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(PhysicalDisplayId displayId,
bool isPrimary) const {
- const auto id = PhysicalDisplayId::tryCast(displayId);
- if (!id) {
- return ui::ROTATION_0;
- }
if (!mIgnoreHwcPhysicalDisplayOrientation &&
getHwComposer().getComposer()->isSupported(
Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
- switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
+ switch (getHwComposer().getPhysicalDisplayOrientation(displayId)) {
case Hwc2::AidlTransform::ROT_90:
return ui::ROTATION_90;
case Hwc2::AidlTransform::ROT_180:
@@ -3136,40 +3314,12 @@
const TimePoint presentTime = TimePoint::now();
- // The Uids of layer owners that are in buffer stuffing mode, and their elevated
- // buffer counts. Messages to start recovery are sent exclusively to these Uids.
- BufferStuffingMap bufferStuffedUids;
-
// Set presentation information before calling Layer::releasePendingBuffer, such that jank
// information from previous' frame classification is already available when sending jank info
// to clients, so they get jank classification as early as possible.
mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
pacesetterGpuCompositionDoneFenceTime);
- // Find and register any layers that are in buffer stuffing mode
- const auto& presentFrames = mFrameTimeline->getPresentFrames();
-
- for (const auto& frame : presentFrames) {
- const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId());
- if (!layer) continue;
- uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0;
- int32_t jankType = frame->getJankType().value_or(JankType::None);
- if (jankType & JankType::BufferStuffing &&
- layer->flags & layer_state_t::eRecoverableFromBufferStuffing) {
- auto [it, wasEmplaced] =
- bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers);
- // Update with maximum number of queued buffers, allows clients drawing
- // multiple windows to account for the most severely stuffed window
- if (!wasEmplaced && it->second < numberQueuedBuffers) {
- it->second = numberQueuedBuffers;
- }
- }
- }
-
- if (!bufferStuffedUids.empty()) {
- mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids));
- }
-
// We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
// be sampled a little later than when we started doing work for this frame,
// but that should be okay since CompositorTiming has snapping logic.
@@ -3182,8 +3332,7 @@
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
const Fps renderRate = pacesetterDisplay->refreshRateSelector().getActiveMode().fps;
- const nsecs_t vsyncPhase =
- mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset;
+ const nsecs_t vsyncPhase = mScheduler->getCurrentVsyncConfigs().late.sfOffset;
const CompositorTiming compositorTiming(vsyncDeadline.ns(), renderRate.getPeriodNsecs(),
vsyncPhase, presentLatency.ns());
@@ -3236,8 +3385,8 @@
std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
hdrInfoListeners;
bool haveNewHdrInfoListeners = false;
- sp<gui::IActivePictureListener> activePictureListener;
- bool haveNewActivePictureListener = false;
+ ActivePictureTracker::Listeners activePictureListenersToAdd;
+ ActivePictureTracker::Listeners activePictureListenersToRemove;
{
Mutex::Autolock lock(mStateLock);
if (mFpsReporter) {
@@ -3259,9 +3408,8 @@
haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
mAddingHDRLayerInfoListener = false;
- activePictureListener = mActivePictureListener;
- haveNewActivePictureListener = mHaveNewActivePictureListener;
- mHaveNewActivePictureListener = false;
+ std::swap(activePictureListenersToAdd, mActivePictureListenersToAdd);
+ std::swap(activePictureListenersToRemove, mActivePictureListenersToRemove);
}
if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
@@ -3325,14 +3473,10 @@
mHdrLayerInfoChanged = false;
if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
- // Track, update and notify changes to active pictures - layers that are undergoing picture
- // processing
- if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) {
- if (activePictureListener) {
- activePictureListener->onActivePicturesChanged(
- mActivePictureUpdater.getActivePictures());
- }
- }
+ // Track, update and notify changes to active pictures - layers that are undergoing
+ // picture processing
+ mActivePictureTracker.updateAndNotifyListeners(activePictureListenersToAdd,
+ activePictureListenersToRemove);
}
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
@@ -3342,13 +3486,7 @@
mTimeStats->setPresentFenceGlobal(pacesetterPresentFenceTime);
for (auto&& [id, presentFence] : presentFences) {
- ftl::FakeGuard guard(mStateLock);
- const bool isInternalDisplay =
- mPhysicalDisplays.get(id).transform(&PhysicalDisplay::isInternal).value_or(false);
-
- if (isInternalDisplay) {
- mScheduler->addPresentFence(id, std::move(presentFence));
- }
+ mScheduler->addPresentFence(id, std::move(presentFence));
}
const bool hasPacesetterDisplay =
@@ -3413,9 +3551,8 @@
std::vector<HWComposer::HWCDisplayMode> hwcModes;
std::optional<hal::HWConfigId> activeModeHwcIdOpt;
- const bool isExternalDisplay = FlagManager::getInstance().connected_display() &&
- getHwComposer().getDisplayConnectionType(displayId) ==
- ui::DisplayConnectionType::External;
+ const bool isExternalDisplay = getHwComposer().getDisplayConnectionType(displayId) ==
+ ui::DisplayConnectionType::External;
int attempt = 0;
constexpr int kMaxAttempts = 3;
@@ -3573,16 +3710,17 @@
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) {
+ // TODO: b/393126541 - replace if with switch as all cases are handled.
+ if (event == HWComposer::HotplugEvent::Connected ||
+ event == HWComposer::HotplugEvent::LinkUnstable) {
const auto activeModeIdOpt =
processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
- displayString.c_str());
+ displayString.c_str(), event);
if (!activeModeIdOpt) {
mScheduler->dispatchHotplugError(
static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
@@ -3608,7 +3746,7 @@
LOG_ALWAYS_FATAL_IF(!snapshotOpt);
mDisplayModeController.registerDisplay(*snapshotOpt, *activeModeIdOpt, config);
- } else {
+ } else { // event == HWComposer::HotplugEvent::Disconnected
// Unregister before destroying the DisplaySnapshot below.
mDisplayModeController.unregisterDisplay(displayId);
@@ -3623,7 +3761,8 @@
std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId,
hal::HWDisplayId hwcDisplayId,
DisplayIdentificationInfo&& info,
- const char* displayString) {
+ const char* displayString,
+ HWComposer::HotplugEvent event) {
auto [displayModes, activeMode] = loadDisplayModes(displayId);
if (!activeMode) {
ALOGE("Failed to hotplug %s", displayString);
@@ -3636,6 +3775,7 @@
if (const auto displayOpt = mPhysicalDisplays.get(displayId)) {
const auto& display = displayOpt->get();
const auto& snapshot = display.snapshot();
+ const uint8_t port = snapshot.port();
std::optional<DeviceProductInfo> deviceProductInfo;
if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
@@ -3644,36 +3784,40 @@
deviceProductInfo = snapshot.deviceProductInfo();
}
+ // Use the cached port via snapshot because we are updating an existing
+ // display on reconnect.
const auto it =
- mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
+ mPhysicalDisplays.try_replace(displayId, display.token(), displayId, port,
snapshot.connectionType(), std::move(displayModes),
std::move(colorModes), std::move(deviceProductInfo));
auto& state = mCurrentState.displays.editValueFor(it->second.token());
state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
state.physical->activeMode = std::move(activeMode);
+ state.physical->port = port;
ALOGI("Reconnecting %s", displayString);
return activeModeId;
+ } else if (event == HWComposer::HotplugEvent::LinkUnstable) {
+ ALOGE("Failed to reconnect unknown %s", displayString);
+ return std::nullopt;
}
const sp<IBinder> token = sp<BBinder>::make();
const ui::DisplayConnectionType connectionType =
getHwComposer().getDisplayConnectionType(displayId);
- mPhysicalDisplays.try_emplace(displayId, token, displayId, connectionType,
+ mPhysicalDisplays.try_emplace(displayId, token, displayId, info.port, connectionType,
std::move(displayModes), std::move(colorModes),
std::move(info.deviceProductInfo));
DisplayDeviceState state;
state.physical = {.id = displayId,
.hwcDisplayId = hwcDisplayId,
+ .port = info.port,
.activeMode = std::move(activeMode)};
- if (mIsHdcpViaNegVsync) {
- state.isSecure = connectionType == ui::DisplayConnectionType::Internal;
- } else {
- // TODO(b/349703362): Remove this when HDCP aidl API becomes ready
- state.isSecure = true; // All physical displays are currently considered secure.
- }
+ // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced
+ state.isSecure = !mDisplayModeController.supportsHdcp() ||
+ connectionType == ui::DisplayConnectionType::Internal;
state.isProtected = true;
state.displayName = std::move(info.name);
state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId);
@@ -3714,13 +3858,17 @@
creationArgs.hasWideColorGamut = false;
creationArgs.supportedPerFrameMetadata = 0;
- if (const auto physicalIdOpt = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+ if (const auto physicalIdOpt =
+ compositionDisplay->getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
const auto physicalId = *physicalIdOpt;
creationArgs.isPrimary = physicalId == getPrimaryDisplayIdLocked();
creationArgs.refreshRateSelector =
FTL_FAKE_GUARD(kMainThreadContext,
mDisplayModeController.selectorPtrFor(physicalId));
+ creationArgs.physicalOrientation =
+ getPhysicalDisplayOrientation(physicalId, creationArgs.isPrimary);
+ ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
mPhysicalDisplays.get(physicalId)
.transform(&PhysicalDisplay::snapshotRef)
@@ -3733,7 +3881,8 @@
}));
}
- if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+ if (const auto id = compositionDisplay->getDisplayIdVariant().and_then(
+ asHalDisplayId<DisplayIdVariant>)) {
getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
}
@@ -3749,11 +3898,12 @@
nativeWindow->setSwapInterval(nativeWindow.get(), 0);
}
- creationArgs.physicalOrientation =
- getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
- ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
-
- creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ creationArgs.initialPowerMode = state.initialPowerMode;
+ } else {
+ creationArgs.initialPowerMode =
+ state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
+ }
creationArgs.requestedRefreshRate = state.requestedRefreshRate;
@@ -3777,10 +3927,12 @@
mode.getPeakFps());
}
- display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
+ display->setLayerFilter(
+ makeLayerFilterForDisplay(display->getDisplayIdVariant(), state.layerStack));
display->setProjection(state.orientation, state.layerStackSpaceRect,
state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
+ display->setOptimizationPolicy(state.optimizationPolicy);
display->setFlags(state.flags);
return display;
@@ -3845,15 +3997,19 @@
// Virtual displays without a surface are dormant:
// they have external state (layer stack, projection,
// etc.) but no internal state (i.e. a DisplayDevice).
+ ALOGD("Not adding dormant virtual display with token %p: %s", displayToken.unsafe_get(),
+ state.displayName.c_str());
return;
}
compositionengine::DisplayCreationArgsBuilder builder;
+ std::optional<VirtualDisplayIdVariant> virtualDisplayIdVariantOpt;
if (const auto& physical = state.physical) {
builder.setId(physical->id);
} else {
- builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId,
- canAllocateHwcForVDS));
+ virtualDisplayIdVariantOpt =
+ acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId, builder,
+ canAllocateHwcForVDS);
}
builder.setPixels(resolution);
@@ -3873,11 +4029,10 @@
getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
if (state.isVirtual()) {
- const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());
- LOG_FATAL_IF(!displayId);
- auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,
- bqProducer, bqConsumer, state.displayName,
- state.isSecure);
+ LOG_FATAL_IF(!virtualDisplayIdVariantOpt);
+ auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *virtualDisplayIdVariantOpt,
+ state.surface, bqProducer, bqConsumer,
+ state.displayName, state.isSecure);
displaySurface = surface;
producer = std::move(surface);
} else {
@@ -3885,18 +4040,17 @@
"adding a supported display, but rendering "
"surface is provided (%p), ignoring it",
state.surface.get());
- const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());
- LOG_FATAL_IF(!displayId);
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
const auto frameBufferSurface =
- sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer,
+ sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqProducer,
+ bqConsumer,
state.physical->activeMode->getResolution(),
ui::Size(maxGraphicsWidth, maxGraphicsHeight));
displaySurface = frameBufferSurface;
producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer();
#else
displaySurface =
- sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
+ sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqConsumer,
state.physical->activeMode->getResolution(),
ui::Size(maxGraphicsWidth, maxGraphicsHeight));
producer = bqProducer;
@@ -3908,9 +4062,6 @@
displaySurface, producer);
if (mScheduler && !display->isVirtual()) {
- // TODO(b/241285876): Annotate `processDisplayAdded` instead.
- ftl::FakeGuard guard(kMainThreadContext);
-
// For hotplug reconnect, renew the registration since display modes have been reloaded.
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(),
mActiveDisplayId);
@@ -3924,13 +4075,16 @@
incRefreshableDisplays();
}
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ applyOptimizationPolicy(__func__);
+ }
+
mDisplays.try_emplace(displayToken, std::move(display));
// For an external display, loadDisplayModes already attempted to select the same mode
// as DM, but SF still needs to be updated to match.
// TODO (b/318534874): Let DM decide the initial mode.
- if (const auto& physical = state.physical;
- mScheduler && physical && FlagManager::getInstance().connected_display()) {
+ if (const auto& physical = state.physical; mScheduler && physical) {
const bool isInternalDisplay = mPhysicalDisplays.get(physical->id)
.transform(&PhysicalDisplay::isInternal)
.value_or(false);
@@ -3953,8 +4107,8 @@
if (display) {
display->disconnect();
- if (display->isVirtual()) {
- releaseVirtualDisplay(display->getVirtualId());
+ if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) {
+ releaseVirtualDisplay(*virtualDisplayIdVariant);
} else {
mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
}
@@ -3981,6 +4135,10 @@
// not be accessible.
}));
}
+
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ applyOptimizationPolicy(__func__);
+ }
}
void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
@@ -3993,8 +4151,8 @@
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
- if (display->isVirtual()) {
- releaseVirtualDisplay(display->getVirtualId());
+ if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) {
+ releaseVirtualDisplay(*virtualDisplayIdVariant);
}
if (display->isRefreshable()) {
@@ -4006,7 +4164,7 @@
if (const auto& physical = currentState.physical) {
getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id,
- /*physicalSize=*/std::nullopt);
+ physical->port, /*physicalSize=*/std::nullopt);
}
processDisplayAdded(displayToken, currentState);
@@ -4014,7 +4172,7 @@
if (currentState.physical) {
const auto display = getDisplayDeviceLocked(displayToken);
if (!mSkipPowerOnForQuiescent) {
- setPowerModeInternal(display, hal::PowerMode::ON);
+ setPhysicalDisplayPowerMode(display, hal::PowerMode::ON);
}
if (display->getPhysicalId() == mActiveDisplayId) {
@@ -4026,12 +4184,41 @@
if (const auto display = getDisplayDeviceLocked(displayToken)) {
if (currentState.layerStack != drawingState.layerStack) {
- display->setLayerFilter(
- makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
+ display->setLayerFilter(makeLayerFilterForDisplay(display->getDisplayIdVariant(),
+ currentState.layerStack));
}
if (currentState.flags != drawingState.flags) {
display->setFlags(currentState.flags);
}
+
+ const auto updateDisplaySize = [&]() {
+ if (currentState.width != drawingState.width ||
+ currentState.height != drawingState.height) {
+ const ui::Size resolution = ui::Size(currentState.width, currentState.height);
+
+ // Resize the framebuffer. For a virtual display, always do so. For a physical
+ // display, only do so if it has a pending modeset for the matching resolution.
+ if (!currentState.physical ||
+ (FlagManager::getInstance().synced_resolution_switch() &&
+ mDisplayModeController.getDesiredMode(display->getPhysicalId())
+ .transform([resolution](const auto& request) {
+ return resolution == request.mode.modePtr->getResolution();
+ })
+ .value_or(false))) {
+ display->setDisplaySize(resolution);
+ }
+
+ if (display->getId() == mActiveDisplayId) {
+ onActiveDisplaySizeChanged(*display);
+ }
+ }
+ };
+
+ if (FlagManager::getInstance().synced_resolution_switch()) {
+ // Update display size first, as display projection below depends on it.
+ updateDisplaySize();
+ }
+
if ((currentState.orientation != drawingState.orientation) ||
(currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
(currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
@@ -4043,13 +4230,9 @@
ui::Transform::toRotationFlags(display->getOrientation());
}
}
- if (currentState.width != drawingState.width ||
- currentState.height != drawingState.height) {
- display->setDisplaySize(currentState.width, currentState.height);
- if (display->getId() == mActiveDisplayId) {
- onActiveDisplaySizeChanged(*display);
- }
+ if (!FlagManager::getInstance().synced_resolution_switch()) {
+ updateDisplaySize();
}
}
}
@@ -4158,38 +4341,35 @@
mVisibleWindowIds = std::move(visibleWindowIds);
}
- BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
- windowInfos = std::move(windowInfos),
- displayInfos = std::move(displayInfos),
- inputWindowCommands =
- std::move(mInputWindowCommands),
- inputFlinger = mInputFlinger, this,
- visibleWindowsChanged, vsyncId,
- frameTime]() mutable {
- SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
- if (updateWindowInfo) {
- mWindowInfosListenerInvoker
- ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
- std::move(displayInfos),
- ftl::to_underlying(vsyncId),
- frameTime.ns()},
- std::move(
- inputWindowCommands.windowInfosReportedListeners),
- /* forceImmediateCall= */ visibleWindowsChanged ||
- !inputWindowCommands.focusRequests.empty());
- } else {
- // If there are listeners but no changes to input windows, call the listeners
- // immediately.
- for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) {
- if (IInterface::asBinder(listener)->isBinderAlive()) {
- listener->onWindowInfosReported();
+ BackgroundExecutor::getInstance().sendCallbacks(
+ {[updateWindowInfo, windowInfos = std::move(windowInfos),
+ displayInfos = std::move(displayInfos),
+ inputWindowCommands = std::move(mInputWindowCommands), inputFlinger = mInputFlinger,
+ this, visibleWindowsChanged, vsyncId, frameTime]() mutable {
+ SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
+ if (updateWindowInfo) {
+ mWindowInfosListenerInvoker
+ ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
+ std::move(displayInfos),
+ ftl::to_underlying(vsyncId),
+ frameTime.ns()},
+ inputWindowCommands.releaseListeners(),
+ /* forceImmediateCall= */ visibleWindowsChanged ||
+ !inputWindowCommands.getFocusRequests()
+ .empty());
+ } else {
+ // If there are listeners but no changes to input windows, call the listeners
+ // immediately.
+ for (const auto& listener : inputWindowCommands.getListeners()) {
+ if (IInterface::asBinder(listener)->isBinderAlive()) {
+ listener->onWindowInfosReported();
+ }
+ }
}
- }
- }
- for (const auto& focusRequest : inputWindowCommands.focusRequests) {
- inputFlinger->setFocusedWindow(focusRequest);
- }
- }});
+ for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) {
+ inputFlinger->setFocusedWindow(focusRequest);
+ }
+ }});
mInputWindowCommands.clear();
}
@@ -4245,7 +4425,7 @@
void SurfaceFlinger::updateCursorAsync() {
compositionengine::CompositionRefreshArgs refreshArgs;
for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
- if (HalDisplayId::tryCast(display->getId())) {
+ if (asHalDisplayId(display->getDisplayIdVariant())) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
}
}
@@ -4499,7 +4679,7 @@
/*applyImmediately*/ true);
}
- const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
+ const auto configs = mScheduler->getCurrentVsyncConfigs();
mScheduler->createEventThread(scheduler::Cycle::Render, mFrameTimeline->getTokenManager(),
/* workDuration */ configs.late.appWorkDuration,
@@ -4541,27 +4721,6 @@
status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parent,
uint32_t* outTransformHint) {
- if (mNumLayers >= MAX_LAYERS) {
- static std::atomic<nsecs_t> lasttime{0};
- nsecs_t now = systemTime();
- if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) {
- ALOGE("AddClientLayer already dumped 10s before");
- return NO_MEMORY;
- } else {
- lasttime = now;
- }
-
- ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
- MAX_LAYERS);
- static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) {
- ALOGE("Dumping on-screen layers.");
- mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy());
- ALOGE("Dumping off-screen layers.");
- mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy());
- }));
- return NO_MEMORY;
- }
-
if (outTransformHint) {
*outTransformHint = mActiveDisplayTransformHint;
}
@@ -4597,7 +4756,6 @@
SFTRACE_INT("mTransactionFlags", transactionFlags);
if (const bool scheduled = transactionFlags & mask; !scheduled) {
- mScheduler->resync();
scheduleCommit(frameHint);
} else if (frameHint == FrameHint::kActive) {
// Even if the next frame is already scheduled, we should reset the idle timer
@@ -4767,16 +4925,18 @@
// For tests only
bool SurfaceFlinger::flushTransactionQueues() {
mTransactionHandler.collectTransactions();
- std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
+ std::vector<QueuedTransactionState> transactions = mTransactionHandler.flushTransactions();
return applyTransactions(transactions);
}
-bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions)
+ EXCLUDES(mStateLock) {
Mutex::Autolock lock(mStateLock);
return applyTransactionsLocked(transactions);
}
-bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions)
+ REQUIRES(mStateLock) {
bool needsTraversal = false;
// Now apply all transactions.
for (auto& transaction : transactions) {
@@ -4853,49 +5013,54 @@
return true;
}
-status_t SurfaceFlinger::setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
- const std::vector<uint64_t>& mergedTransactionIds) {
+status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState) {
SFTRACE_CALL();
IPCThreadState* ipc = IPCThreadState::self();
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
- for (auto& composerState : states) {
+ ftl::Flags<adpf::Workload> queuedWorkload;
+ for (auto& composerState : transactionState.mComposerStates) {
composerState.state.sanitize(permissions);
+ if (composerState.state.what & layer_state_t::COMPOSITION_EFFECTS) {
+ queuedWorkload |= adpf::Workload::EFFECTS;
+ }
+ if (composerState.state.what & layer_state_t::VISIBLE_REGION_CHANGES) {
+ queuedWorkload |= adpf::Workload::VISIBLE_REGION;
+ }
}
- for (DisplayState& display : displays) {
+ for (DisplayState& display : transactionState.mDisplayStates) {
display.sanitize(permissions);
}
- if (!inputWindowCommands.empty() &&
+ if (!transactionState.mInputWindowCommands.empty() &&
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
ALOGE("Only privileged callers are allowed to send input commands.");
- inputWindowCommands.clear();
+ transactionState.mInputWindowCommands.clear();
}
- if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+ if (transactionState.mFlags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
const bool hasPermission =
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
callingThreadHasPermission(sWakeupSurfaceFlinger);
if (!hasPermission) {
ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
"eEarlyWakeup[Start|End] flags");
- flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ transactionState.mFlags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
}
}
+ if (transactionState.mFlags & eEarlyWakeupStart) {
+ queuedWorkload |= adpf::Workload::WAKEUP;
+ }
+ mPowerAdvisor->setQueuedWorkload(queuedWorkload);
const int64_t postTime = systemTime();
std::vector<uint64_t> uncacheBufferIds;
- uncacheBufferIds.reserve(uncacheBuffers.size());
- for (const auto& uncacheBuffer : uncacheBuffers) {
+ uncacheBufferIds.reserve(transactionState.mUncacheBuffers.size());
+ for (const auto& uncacheBuffer : transactionState.mUncacheBuffers) {
sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
if (buffer != nullptr) {
uncacheBufferIds.push_back(buffer->getId());
@@ -4903,56 +5068,53 @@
}
std::vector<ResolvedComposerState> resolvedStates;
- resolvedStates.reserve(states.size());
- for (auto& state : states) {
+ resolvedStates.reserve(transactionState.mComposerStates.size());
+ for (auto& state : transactionState.mComposerStates) {
resolvedStates.emplace_back(std::move(state));
auto& resolvedState = resolvedStates.back();
resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
resolvedState.state.surface) {
sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
- std::string layerName = (layer) ?
- layer->getDebugName() : std::to_string(resolvedState.state.layerId);
+ std::string layerName =
+ (layer) ? layer->getDebugName() : std::to_string(resolvedState.state.layerId);
resolvedState.externalTexture =
getExternalTextureFromBufferData(*resolvedState.state.bufferData,
- layerName.c_str(), transactionId);
+ layerName.c_str(), transactionState.getId());
if (resolvedState.externalTexture) {
resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
+ if (FlagManager::getInstance().monitor_buffer_fences()) {
+ resolvedState.state.bufferData->buffer->getDependencyMonitor()
+ .addIngress(FenceTime::makeValid(
+ resolvedState.state.bufferData->acquireFence),
+ "Incoming txn");
+ }
}
mBufferCountTracker.increment(resolvedState.layerId);
}
if (resolvedState.state.what & layer_state_t::eReparent) {
- resolvedState.parentId =
- getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
+ resolvedState.parentId = getLayerIdFromSurfaceControl(
+ resolvedState.state.getParentSurfaceControlForChild());
}
if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) {
- resolvedState.relativeParentId =
- getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl);
+ resolvedState.relativeParentId = getLayerIdFromSurfaceControl(
+ resolvedState.state.getRelativeLayerSurfaceControl());
}
if (resolvedState.state.what & layer_state_t::eInputInfoChanged) {
wp<IBinder>& touchableRegionCropHandle =
- resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle;
+ resolvedState.state.editWindowInfo()->touchableRegionCropHandle;
resolvedState.touchCropId =
LayerHandle::getLayerId(touchableRegionCropHandle.promote());
}
}
- TransactionState state{frameTimelineInfo,
- resolvedStates,
- displays,
- flags,
- applyToken,
- std::move(inputWindowCommands),
- desiredPresentTime,
- isAutoTimestamp,
- std::move(uncacheBufferIds),
- postTime,
- hasListenerCallbacks,
- listenerCallbacks,
- originPid,
- originUid,
- transactionId,
- mergedTransactionIds};
+ QueuedTransactionState state{std::move(transactionState),
+ std::move(resolvedStates),
+ std::move(uncacheBufferIds),
+ postTime,
+ originPid,
+ originUid};
+ state.workloadHint = queuedWorkload;
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
@@ -4965,6 +5127,9 @@
}(state.flags);
const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
+ // Copy fields of |state| needed after it is moved into queueTransaction
+ VsyncId vsyncId{state.frameTimelineInfo.vsyncId};
+ auto applyToken = state.applyToken;
{
// Transactions are added via a lockless queue and does not need to be added from the main
// thread.
@@ -4974,22 +5139,20 @@
for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
- scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
+ scheduleNotifyExpectedPresentHint(displayId, vsyncId);
}
}
setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
return NO_ERROR;
}
-bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
- std::vector<ResolvedComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags,
- const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime, bool isAutoTimestamp,
- const std::vector<uint64_t>& uncacheBufferIds,
- const int64_t postTime, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId) {
+bool SurfaceFlinger::applyTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, std::vector<ResolvedComposerState>& states,
+ std::span<DisplayState> displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds, const int64_t postTime,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ int originPid, int originUid, uint64_t transactionId) REQUIRES(mStateLock) {
uint32_t transactionFlags = 0;
// start and end registration for listeners w/ no surface so they can get their callback. Note
@@ -5037,7 +5200,7 @@
}
bool SurfaceFlinger::applyAndCommitDisplayTransactionStatesLocked(
- std::vector<TransactionState>& transactions) {
+ std::vector<QueuedTransactionState>& transactions) {
bool needsTraversal = false;
uint32_t transactionFlags = 0;
for (auto& transaction : transactions) {
@@ -5141,7 +5304,7 @@
ResolvedComposerState& composerState,
int64_t desiredPresentTime,
bool isAutoTimestamp, int64_t postTime,
- uint64_t transactionId) {
+ uint64_t transactionId) REQUIRES(mStateLock) {
layer_state_t& s = composerState.state;
std::vector<ListenerCallbacks> filteredListeners;
@@ -5333,14 +5496,13 @@
mirrorArgs.addToRoot = true;
mirrorArgs.layerStackToMirror = layerStack;
result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
+ if (result != NO_ERROR) {
+ return result;
+ }
outResult.layerId = rootMirrorLayer->sequence;
outResult.layerName = String16(rootMirrorLayer->getDebugName());
- result |= addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
- nullptr /* parent */, nullptr /* outTransformHint */);
- }
-
- if (result != NO_ERROR) {
- return result;
+ addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
+ nullptr /* parent */, nullptr /* outTransformHint */);
}
setTransactionFlags(eTransactionFlushNeeded);
@@ -5360,6 +5522,9 @@
[[fallthrough]];
case ISurfaceComposerClient::eFXSurfaceEffect: {
result = createBufferStateLayer(args, &outResult.handle, &layer);
+ if (result != NO_ERROR) {
+ return result;
+ }
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
if (pendingBufferCounter) {
std::string counterName = layer->getPendingBufferCounterName();
@@ -5400,6 +5565,9 @@
status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
+ if (checkLayerLeaks() != NO_ERROR) {
+ return NO_MEMORY;
+ }
*outLayer = getFactory().createBufferStateLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
@@ -5407,11 +5575,38 @@
status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
+ if (checkLayerLeaks() != NO_ERROR) {
+ return NO_MEMORY;
+ }
*outLayer = getFactory().createEffectLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
+status_t SurfaceFlinger::checkLayerLeaks() {
+ if (mNumLayers >= MAX_LAYERS) {
+ static std::atomic<nsecs_t> lasttime{0};
+ nsecs_t now = systemTime();
+ if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) {
+ ALOGE("CreateLayer already dumped 10s before");
+ return NO_MEMORY;
+ } else {
+ lasttime = now;
+ }
+
+ ALOGE("CreateLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
+ MAX_LAYERS);
+ static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ ALOGE("Dumping on-screen layers.");
+ mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy());
+ ALOGE("Dumping off-screen layers.");
+ mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy());
+ }));
+ return NO_MEMORY;
+ }
+ return NO_ERROR;
+}
+
void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer, uint32_t layerId) {
{
// Used to remove stalled transactions which uses an internal lock.
@@ -5431,7 +5626,7 @@
}
void SurfaceFlinger::initializeDisplays() {
- TransactionState state;
+ QueuedTransactionState state;
state.inputWindowCommands = mInputWindowCommands;
const nsecs_t now = systemTime();
state.desiredPresentTime = now;
@@ -5443,10 +5638,11 @@
auto layerStack = ui::DEFAULT_LAYER_STACK.id;
for (const auto& [id, display] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) {
- state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
+ state.displays.emplace_back(
+ DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
}
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back(state);
{
@@ -5459,7 +5655,7 @@
// In case of a restart, ensure all displays are off.
for (const auto& [id, display] : mPhysicalDisplays) {
- setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
+ setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::OFF);
}
// Power on all displays. The primary display is first, so becomes the active display. Also,
@@ -5468,13 +5664,14 @@
// Additionally, do not turn on displays if the boot should be quiescent.
if (!mSkipPowerOnForQuiescent) {
for (const auto& [id, display] : mPhysicalDisplays) {
- setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON);
+ setPhysicalDisplayPowerMode(getDisplayDeviceLocked(id), hal::PowerMode::ON);
}
}
}
}
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
+void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display,
+ hal::PowerMode mode) {
if (display->isVirtual()) {
// TODO(b/241285876): This code path should not be reachable, so enforce this at compile
// time.
@@ -5483,7 +5680,7 @@
}
const auto displayId = display->getPhysicalId();
- ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
+ ALOGD("Setting power mode %d on physical display %s", mode, to_string(displayId).c_str());
const auto currentMode = display->getPowerMode();
if (currentMode == mode) {
@@ -5526,21 +5723,15 @@
}
if (displayId == mActiveDisplayId) {
- // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and
- // set it before SCHED_FIFO due to b/190237315.
- if (setSchedAttr(true) != NO_ERROR) {
- ALOGW("Failed to set uclamp.min after powering on active display: %s",
- strerror(errno));
- }
- if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Failed to set SCHED_FIFO after powering on active display: %s",
- strerror(errno));
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ applyOptimizationPolicy("setPhysicalDisplayPowerMode(ON)");
+ } else {
+ disablePowerOptimizations("setPhysicalDisplayPowerMode(ON)");
}
}
getHwComposer().setPowerMode(displayId, mode);
- if (mode != hal::PowerMode::DOZE_SUSPEND &&
- (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
+ if (mode != hal::PowerMode::DOZE_SUSPEND) {
const bool enable =
mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
requestHardwareVsync(displayId, enable);
@@ -5562,24 +5753,18 @@
if (const auto display = getActivatableDisplay()) {
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
} else {
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
- strerror(errno));
- }
- if (setSchedAttr(false) != NO_ERROR) {
- ALOGW("Failed set uclamp.min after powering off active display: %s",
- strerror(errno));
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ applyOptimizationPolicy("setPhysicalDisplayPowerMode(OFF)");
+ } else {
+ enablePowerOptimizations("setPhysicalDisplayPowerMode(OFF)");
}
if (currentModeNotDozeSuspend) {
- if (!FlagManager::getInstance().multithreaded_present()) {
- mScheduler->disableHardwareVsync(displayId, true);
- }
mScheduler->enableSyntheticVsync();
}
}
}
- if (currentModeNotDozeSuspend && FlagManager::getInstance().multithreaded_present()) {
+ if (currentModeNotDozeSuspend) {
constexpr bool kDisallow = true;
mScheduler->disableHardwareVsync(displayId, kDisallow);
}
@@ -5597,8 +5782,7 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (currentMode == hal::PowerMode::DOZE_SUSPEND &&
- (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
+ if (currentMode == hal::PowerMode::DOZE_SUSPEND) {
if (displayId == mActiveDisplayId) {
ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
mVisibleRegionsDirty = true;
@@ -5610,10 +5794,9 @@
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
- if (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present()) {
- constexpr bool kDisallow = true;
- mScheduler->disableHardwareVsync(displayId, kDisallow);
- }
+ constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(displayId, kDisallow);
+
if (displayId == mActiveDisplayId) {
mScheduler->enableSyntheticVsync();
}
@@ -5630,21 +5813,96 @@
mScheduler->setDisplayPowerMode(displayId, mode);
- ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
+ ALOGD("Finished setting power mode %d on physical display %s", mode,
+ to_string(displayId).c_str());
+}
+
+void SurfaceFlinger::setVirtualDisplayPowerMode(const sp<DisplayDevice>& display,
+ hal::PowerMode mode) {
+ if (!display->isVirtual()) {
+ ALOGE("%s: Invalid operation on physical display", __func__);
+ return;
+ }
+
+ const auto displayId = display->getVirtualId();
+ ALOGD("Setting power mode %d on virtual display %s %s", mode, to_string(displayId).c_str(),
+ display->getDisplayName().c_str());
+
+ display->setPowerMode(static_cast<hal::PowerMode>(mode));
+
+ applyOptimizationPolicy(__func__);
+
+ ALOGD("Finished setting power mode %d on virtual display %s", mode,
+ to_string(displayId).c_str());
+}
+
+bool SurfaceFlinger::shouldOptimizeForPerformance() {
+ for (const auto& [_, display] : mDisplays) {
+ // Displays that are optimized for power are always powered on and should not influence
+ // whether there is an active display for the purpose of power optimization, etc. If these
+ // displays are being shown somewhere, a different (physical or virtual) display that is
+ // optimized for performance will be powered on in addition. Displays optimized for
+ // performance will change power mode, so if they are off then they are not active.
+ if (display->isPoweredOn() &&
+ display->getOptimizationPolicy() ==
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SurfaceFlinger::enablePowerOptimizations(const char* whence) {
+ ALOGD("%s: Enabling power optimizations", whence);
+
+ setSchedAttr(false, whence);
+ setSchedFifo(false, whence);
+}
+
+void SurfaceFlinger::disablePowerOptimizations(const char* whence) {
+ ALOGD("%s: Disabling power optimizations", whence);
+
+ // TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall
+ // and set it before SCHED_FIFO due to b/190237315.
+ setSchedAttr(true, whence);
+ setSchedFifo(true, whence);
+}
+
+void SurfaceFlinger::applyOptimizationPolicy(const char* whence) {
+ if (shouldOptimizeForPerformance()) {
+ disablePowerOptimizations(whence);
+ } else {
+ enablePowerOptimizations(whence);
+ }
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
- kMainThreadContext) {
+ auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
mSkipPowerOnForQuiescent = false;
- const auto display = getDisplayDeviceLocked(displayToken);
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
if (!display) {
- ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
- displayToken.get());
+ Mutex::Autolock lock(mStateLock);
+ const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+ if (index >= 0) {
+ auto& state = mCurrentState.displays.editValueFor(displayToken);
+ if (state.isVirtual()) {
+ ALOGD("Setting power mode %d for a dormant virtual display with token %p", mode,
+ displayToken.get());
+ state.initialPowerMode = static_cast<hal::PowerMode>(mode);
+ return;
+ }
+ }
+ ALOGE("Failed to set power mode %d for display token %p", mode, displayToken.get());
} else if (display->isVirtual()) {
- ALOGW("Attempt to set power mode %d for virtual display", mode);
+ if (FlagManager::getInstance().correct_virtual_display_power_state()) {
+ ftl::FakeGuard guard(mStateLock);
+ setVirtualDisplayPowerMode(display, static_cast<hal::PowerMode>(mode));
+ } else {
+ ALOGW("Attempt to set power mode %d for virtual display", mode);
+ }
} else {
- setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
+ ftl::FakeGuard guard(mStateLock);
+ setPhysicalDisplayPowerMode(display, static_cast<hal::PowerMode>(mode));
}
});
@@ -5658,8 +5916,7 @@
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_SHELL) &&
- !PermissionCache::checkPermission(sDump, pid, uid)) {
+ if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
pid, uid);
write(fd, result.c_str(), result.size());
@@ -5838,17 +6095,14 @@
for (const auto& [token, display] : mDisplays) {
if (display->isVirtual()) {
- const auto displayId = display->getId();
+ const VirtualDisplayId virtualId = display->getVirtualId();
utils::Dumper::Section section(dumper,
- ftl::Concat("Virtual Display ", displayId.value).str());
+ ftl::Concat("Virtual Display ", virtualId.value).str());
display->dump(dumper);
- if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) {
- std::lock_guard lock(mVirtualDisplaysMutex);
- const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value());
- if (virtualSnapshotIt != mVirtualDisplays.end()) {
- virtualSnapshotIt->second.dump(dumper);
- }
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ if (const auto snapshotOpt = mVirtualDisplays.get(virtualId)) {
+ snapshotOpt->get().dump(dumper);
}
}
}
@@ -5856,10 +6110,11 @@
void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
- const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+ const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant());
if (!displayId) {
continue;
}
+
const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
if (!hwcDisplayId) {
continue;
@@ -5868,6 +6123,7 @@
StringAppendF(&result,
"Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(),
*hwcDisplayId);
+
uint8_t port;
DisplayIdentificationData data;
if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
@@ -5895,6 +6151,19 @@
result.append(edid->displayName.data(), edid->displayName.length());
result.append("\"\n");
}
+
+ for (const auto& [token, display] : mDisplays) {
+ const auto virtualDisplayId = asVirtualDisplayId(display->getDisplayIdVariant());
+ if (virtualDisplayId) {
+ StringAppendF(&result, "Display %s (Virtual display): displayName=\"%s\"",
+ to_string(*virtualDisplayId).c_str(), display->getDisplayName().c_str());
+ std::lock_guard lock(mVirtualDisplaysMutex);
+ if (const auto snapshotOpt = mVirtualDisplays.get(*virtualDisplayId)) {
+ StringAppendF(&result, " uniqueId=\"%s\"", snapshotOpt->get().uniqueId().c_str());
+ }
+ result.append("\n");
+ }
+ }
}
void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args,
@@ -6065,7 +6334,7 @@
void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
- const auto displayId = HalDisplayId::tryCast(display->getId());
+ const auto displayId = asHalDisplayId(display->getDisplayIdVariant());
if (!displayId) {
continue;
}
@@ -6105,7 +6374,7 @@
// figure out if we're stuck somewhere
const nsecs_t now = systemTime();
const nsecs_t inTransaction(mDebugInTransaction);
- nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
+ nsecs_t inTransactionDuration = (inTransaction) ? now - inTransaction : 0;
/*
* Dump library configuration.
@@ -6285,7 +6554,7 @@
if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
- ipc->getCallingPid(), ipc->getCallingUid());
+ ipc->getCallingPid(), ipc->getCallingUid());
return PERMISSION_DENIED;
}
return OK;
@@ -6379,9 +6648,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1045 are currently used for backdoors. The code
+ // Numbers from 1000 to 1047 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1046) {
+ if (code >= 1000 && code <= 1047) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -6401,11 +6670,12 @@
CHECK_INTERFACE(ISurfaceComposer, data, reply);
IPCThreadState* ipc = IPCThreadState::self();
const int uid = ipc->getCallingUid();
- if (CC_UNLIKELY(uid != AID_SYSTEM
- && !PermissionCache::checkCallingPermission(sHardwareTest))) {
+ if (CC_UNLIKELY(uid != AID_SYSTEM &&
+ !PermissionCache::checkCallingPermission(sHardwareTest))) {
const int pid = ipc->getCallingPid();
ALOGE("Permission Denial: "
- "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ "can't access SurfaceFlinger pid=%d, uid=%d",
+ pid, uid);
return PERMISSION_DENIED;
}
int n;
@@ -6476,7 +6746,7 @@
n = data.readInt32();
if (n) {
// color matrix is sent as a column-major mat4 matrix
- for (size_t i = 0 ; i < 4; i++) {
+ for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < 4; j++) {
mClientColorMatrix[i][j] = data.readFloat();
}
@@ -6648,8 +6918,9 @@
return getDefaultDisplayDevice()->getDisplayToken().promote();
}
- if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) {
- return getPhysicalDisplayToken(*id);
+ if (const auto token =
+ getPhysicalDisplayToken(PhysicalDisplayId::fromValue(value))) {
+ return token;
}
ALOGE("Invalid physical display ID");
@@ -6747,10 +7018,10 @@
case 1040: {
auto future = mScheduler->schedule([&] {
n = data.readInt32();
- std::optional<PhysicalDisplayId> inputId = std::nullopt;
+ PhysicalDisplayId inputId;
if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
- inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
- if (!inputId || getPhysicalDisplayToken(*inputId)) {
+ inputId = PhysicalDisplayId::fromValue(inputDisplayId);
+ if (!getPhysicalDisplayToken(inputId)) {
ALOGE("No display with id: %" PRIu64, inputDisplayId);
return NAME_NOT_FOUND;
}
@@ -6759,7 +7030,7 @@
Mutex::Autolock lock(mStateLock);
mLayerCachingEnabled = n != 0;
for (const auto& [_, display] : mDisplays) {
- if (!inputId || *inputId == display->getPhysicalId()) {
+ if (inputId == display->getPhysicalId()) {
display->enableLayerCaching(mLayerCachingEnabled);
}
}
@@ -6842,11 +7113,10 @@
int64_t arg1 = data.readInt64();
int64_t arg2 = data.readInt64();
// Enable mirroring for one display
- const auto display1id = DisplayId::fromValue(arg1);
auto mirrorRoot = SurfaceComposerClient::getDefault()->mirrorDisplay(
- display1id.value());
- auto id2 = DisplayId::fromValue<PhysicalDisplayId>(arg2);
- const auto token2 = getPhysicalDisplayToken(*id2);
+ DisplayId::fromValue(arg1));
+ const auto token2 =
+ getPhysicalDisplayToken(PhysicalDisplayId::fromValue(arg2));
ui::LayerStack layerStack;
{
Mutex::Autolock lock(mStateLock);
@@ -6923,6 +7193,34 @@
mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs)));
return NO_ERROR;
}
+ // Update WorkDuration
+ // parameters:
+ // - (required) i64 minSfNs, used as the late.sf WorkDuration.
+ // - (required) i64 maxSfNs, used as the early.sf and earlyGl.sf WorkDuration.
+ // - (required) i64 appDurationNs, used as the late.app, early.app and earlyGl.app
+ // WorkDuration.
+ // Usage:
+ // adb shell service call SurfaceFlinger 1047 i64 12333333 i64 16666666 i64 16666666
+ case 1047: {
+ if (!property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+ ALOGE("Not supported when work duration is not enabled");
+ return INVALID_OPERATION;
+ }
+ int64_t minSfNs = 0;
+ int64_t maxSfNs = 0;
+ int64_t appDurationNs = 0;
+ if (data.readInt64(&minSfNs) != NO_ERROR || data.readInt64(&maxSfNs) != NO_ERROR ||
+ data.readInt64(&appDurationNs) != NO_ERROR) {
+ return BAD_VALUE;
+ }
+ mScheduler->reloadPhaseConfiguration(mDisplayModeController
+ .getActiveMode(mActiveDisplayId)
+ .fps,
+ Duration::fromNs(minSfNs),
+ Duration::fromNs(maxSfNs),
+ Duration::fromNs(appDurationNs));
+ return NO_ERROR;
+ }
}
}
return err;
@@ -6996,9 +7294,7 @@
class WindowDisconnector {
public:
WindowDisconnector(ANativeWindow* window, int api) : mWindow(window), mApi(api) {}
- ~WindowDisconnector() {
- native_window_api_disconnect(mWindow, mApi);
- }
+ ~WindowDisconnector() { native_window_api_disconnect(mWindow, mApi); }
private:
ANativeWindow* mWindow;
@@ -7032,13 +7328,13 @@
return PERMISSION_DENIED;
}
-status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+void SurfaceFlinger::setSchedFifo(bool enabled, const char* whence) {
static constexpr int kFifoPriority = 2;
static constexpr int kOtherPriority = 0;
struct sched_param param = {0};
int sched_policy;
- if (enabled) {
+ if (enabled && !FlagManager::getInstance().disable_sched_fifo_sf()) {
sched_policy = SCHED_FIFO;
param.sched_priority = kFifoPriority;
} else {
@@ -7047,19 +7343,19 @@
}
if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
- return -errno;
+ const char* kPolicy[] = {"SCHED_OTHER", "SCHED_FIFO"};
+ ALOGW("%s: Failed to set %s: %s", whence, kPolicy[sched_policy == SCHED_FIFO],
+ strerror(errno));
}
-
- return NO_ERROR;
}
-status_t SurfaceFlinger::setSchedAttr(bool enabled) {
+void SurfaceFlinger::setSchedAttr(bool enabled, const char* whence) {
static const unsigned int kUclampMin =
base::GetUintProperty<unsigned int>("ro.surface_flinger.uclamp.min"s, 0U);
if (!kUclampMin) {
// uclamp.min set to 0 (default), skip setting
- return NO_ERROR;
+ return;
}
sched_attr attr = {};
@@ -7070,22 +7366,20 @@
attr.sched_util_max = 1024;
if (syscall(__NR_sched_setattr, 0, &attr, 0)) {
- return -errno;
+ const char* kAction[] = {"disable", "enable"};
+ ALOGW("%s: Failed to %s uclamp.min: %s", whence, kAction[enabled], strerror(errno));
}
-
- return NO_ERROR;
}
namespace {
-ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace,
- const compositionengine::impl::OutputCompositionState& state,
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, ui::ColorMode colorMode,
bool capturingHdrLayers, bool hintForSeamlessTransition) {
if (requestedDataspace != ui::Dataspace::UNKNOWN) {
return requestedDataspace;
}
- const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
+ const auto dataspaceForColorMode = ui::pickDataspaceFor(colorMode);
// TODO: Enable once HDR screenshots are ready.
if constexpr (/* DISABLES CODE */ (false)) {
@@ -7134,9 +7428,13 @@
}
wp<const DisplayDevice> displayWeak;
+ ftl::Optional<DisplayIdVariant> displayIdVariantOpt;
ui::LayerStack layerStack;
ui::Size reqSize(args.width, args.height);
std::unordered_set<uint32_t> excludeLayerIds;
+ Rect layerStackSpaceRect;
+ bool displayIsSecure;
+
{
Mutex::Autolock lock(mStateLock);
sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
@@ -7146,11 +7444,14 @@
return;
}
displayWeak = display;
+ displayIdVariantOpt = display->getDisplayIdVariant();
layerStack = display->getLayerStack();
+ displayIsSecure = display->isSecure();
+ layerStackSpaceRect = display->getLayerStackSpaceRect();
// set the requested width/height to the logical display layer stack rect size by default
if (args.width == 0 || args.height == 0) {
- reqSize = display->getLayerStackSpaceRect().getSize();
+ reqSize = layerStackSpaceRect.getSize();
}
for (const auto& handle : captureArgs.excludeHandles) {
@@ -7169,26 +7470,32 @@
getLayerSnapshotsForScreenshots(layerStack, captureArgs.uid,
std::move(excludeLayerIds));
- ftl::Flags<RenderArea::Options> options;
- if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
- if (captureArgs.hintForSeamlessTransition)
- options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
- captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
- gui::aidl_utils::fromARect(captureArgs.sourceCrop),
- reqSize,
- static_cast<ui::Dataspace>(captureArgs.dataspace),
- displayWeak, options),
- getLayerSnapshotsFn, reqSize,
+ ScreenshotArgs screenshotArgs;
+ screenshotArgs.captureTypeVariant = displayWeak;
+ screenshotArgs.displayIdVariant = displayIdVariantOpt;
+ screenshotArgs.sourceCrop = gui::aidl_utils::fromARect(captureArgs.sourceCrop);
+ if (screenshotArgs.sourceCrop.isEmpty()) {
+ screenshotArgs.sourceCrop = layerStackSpaceRect;
+ }
+ screenshotArgs.reqSize = reqSize;
+ screenshotArgs.dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace);
+ screenshotArgs.isSecure = captureArgs.captureSecureLayers && displayIsSecure;
+ screenshotArgs.seamlessTransition = captureArgs.hintForSeamlessTransition;
+
+ captureScreenCommon(screenshotArgs, getLayerSnapshotsFn, reqSize,
static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
- captureArgs.allowProtected, captureArgs.grayscale,
- captureArgs.attachGainmap, captureListener);
+ captureArgs.allowProtected, captureArgs.grayscale, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
+ ftl::Optional<DisplayIdVariant> displayIdVariantOpt;
ui::Size size;
+ Rect layerStackSpaceRect;
+ bool displayIsSecure;
+
{
Mutex::Autolock lock(mStateLock);
@@ -7200,8 +7507,11 @@
}
displayWeak = display;
+ displayIdVariantOpt = display->getDisplayIdVariant();
layerStack = display->getLayerStack();
+ layerStackSpaceRect = display->getLayerStackSpaceRect();
size = display->getLayerStackSpaceRect().getSize();
+ displayIsSecure = display->isSecure();
}
size.width *= args.frameScaleX;
@@ -7230,15 +7540,18 @@
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- ftl::Flags<RenderArea::Options> options;
- if (args.hintForSeamlessTransition)
- options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
- captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
- Rect(), size,
- static_cast<ui::Dataspace>(args.dataspace),
- displayWeak, options),
- getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat),
- kAllowProtected, kGrayscale, args.attachGainmap, captureListener);
+ ScreenshotArgs screenshotArgs;
+ screenshotArgs.captureTypeVariant = displayWeak;
+ screenshotArgs.displayIdVariant = displayIdVariantOpt;
+ screenshotArgs.sourceCrop = layerStackSpaceRect;
+ screenshotArgs.reqSize = size;
+ screenshotArgs.dataspace = static_cast<ui::Dataspace>(args.dataspace);
+ screenshotArgs.isSecure = args.captureSecureLayers && displayIsSecure;
+ screenshotArgs.seamlessTransition = args.hintForSeamlessTransition;
+
+ captureScreenCommon(screenshotArgs, getLayerSnapshotsFn, size,
+ static_cast<ui::PixelFormat>(args.pixelFormat), kAllowProtected, kGrayscale,
+ captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7340,17 +7653,18 @@
return;
}
- ftl::Flags<RenderArea::Options> options;
- if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
- if (captureArgs.hintForSeamlessTransition)
- options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
- captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop,
- reqSize, dataspace, parent, args.childrenOnly,
- options),
- getLayerSnapshotsFn, reqSize,
+ ScreenshotArgs screenshotArgs;
+ screenshotArgs.captureTypeVariant = parent->getSequence();
+ screenshotArgs.childrenOnly = args.childrenOnly;
+ screenshotArgs.sourceCrop = crop;
+ screenshotArgs.reqSize = reqSize;
+ screenshotArgs.dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace);
+ screenshotArgs.isSecure = captureArgs.captureSecureLayers;
+ screenshotArgs.seamlessTransition = captureArgs.hintForSeamlessTransition;
+
+ captureScreenCommon(screenshotArgs, getLayerSnapshotsFn, reqSize,
static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
- captureArgs.allowProtected, captureArgs.grayscale,
- captureArgs.attachGainmap, captureListener);
+ captureArgs.allowProtected, captureArgs.grayscale, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
@@ -7379,15 +7693,17 @@
return protectedLayerFound;
}
-// Getting layer snapshots and display should take place on main thread.
-// Accessing display requires mStateLock, and contention for this lock
-// is reduced when grabbed from the main thread, thus also reducing
-// risk of deadlocks.
-std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread(
- RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
+// Getting layer snapshots and accessing display state should take place on
+// main thread. Accessing display requires mStateLock, and contention for
+// this lock is reduced when grabbed from the main thread, thus also reducing
+// risk of deadlocks. Returns false if no display is found.
+bool SurfaceFlinger::getSnapshotsFromMainThread(
+ ScreenshotArgs& args, GetLayerSnapshotsFunction getLayerSnapshotsFn,
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
return mScheduler
- ->schedule([=, this, &renderAreaBuilder, &layers]() REQUIRES(kMainThreadContext) {
+ ->schedule([=, this, &args, &layers]() REQUIRES(kMainThreadContext) {
+ SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Screenshot");
+ mPowerAdvisor->setScreenshotWorkload();
SFTRACE_NAME("getSnapshotsFromMainThread");
layers = getLayerSnapshotsFn();
// Non-threaded RenderEngine eventually returns to the main thread a 2nd time
@@ -7400,15 +7716,15 @@
ui::INVALID_LAYER_STACK);
}
}
- return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
+ return getDisplayStateOnMainThread(args);
})
.get();
}
-void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
+void SurfaceFlinger::captureScreenCommon(ScreenshotArgs& args,
GetLayerSnapshotsFunction getLayerSnapshotsFn,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- bool allowProtected, bool grayscale, bool attachGainmap,
+ bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>& captureListener) {
SFTRACE_CALL();
@@ -7421,7 +7737,15 @@
}
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
+ bool hasDisplayState = getSnapshotsFromMainThread(args, getLayerSnapshotsFn, layers);
+ if (!hasDisplayState) {
+ ALOGD("Display state not found");
+ invokeScreenCaptureError(NO_MEMORY, captureListener);
+ }
+
+ const bool hasHdrLayer = std::any_of(layers.cbegin(), layers.cend(), [this](const auto& layer) {
+ return isHdrLayer(*(layer.second->mSnapshot.get()));
+ });
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
@@ -7451,35 +7775,80 @@
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
+
+ std::shared_ptr<renderengine::impl::ExternalTexture> hdrTexture;
+ std::shared_ptr<renderengine::impl::ExternalTexture> gainmapTexture;
+
+ if (hasHdrLayer && !args.seamlessTransition &&
+ FlagManager::getInstance().true_hdr_screenshots()) {
+ const auto hdrBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-hdr");
+ const auto gainmapBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ buffer->getPixelFormat(), 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-gainmap");
+
+ const status_t hdrBufferStatus = hdrBuffer->initCheck();
+ const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
+
+ if (hdrBufferStatus != OK || gainmapBufferStatus != -OK) {
+ if (hdrBufferStatus != OK) {
+ ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.",
+ __func__, hdrBufferStatus);
+ } else {
+ ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
+ __func__, gainmapBufferStatus);
+ }
+ } else {
+ hdrTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ gainmapTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ }
+ }
+
auto futureFence =
- captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
- isProtected, attachGainmap, captureListener, displayState, layers);
+ captureScreenshot(args, texture, false /* regionSampling */, grayscale, isProtected,
+ captureListener, layers, hdrTexture, gainmapTexture);
futureFence.get();
}
-std::optional<SurfaceFlinger::OutputCompositionState>
-SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& renderAreaBuilder) {
+// Returns true if display is found and args was populated with display state
+// data. Otherwise, returns false.
+bool SurfaceFlinger::getDisplayStateOnMainThread(ScreenshotArgs& args) {
sp<const DisplayDevice> display = nullptr;
{
Mutex::Autolock lock(mStateLock);
- if (auto* layerRenderAreaBuilder =
- std::get_if<LayerRenderAreaBuilder>(&renderAreaBuilder)) {
+ // Screenshot initiated through captureLayers
+ if (auto* layerSequence = std::get_if<int32_t>(&args.captureTypeVariant)) {
// LayerSnapshotBuilder should only be accessed from the main thread.
const frontend::LayerSnapshot* snapshot =
- mLayerSnapshotBuilder.getSnapshot(layerRenderAreaBuilder->layer->getSequence());
+ mLayerSnapshotBuilder.getSnapshot(*layerSequence);
if (!snapshot) {
- ALOGW("Couldn't find layer snapshot for %d",
- layerRenderAreaBuilder->layer->getSequence());
+ ALOGW("Couldn't find layer snapshot for %d", *layerSequence);
} else {
- layerRenderAreaBuilder->setLayerSnapshot(*snapshot);
+ if (!args.childrenOnly) {
+ args.transform = snapshot->localTransform.inverse();
+ }
+ if (args.sourceCrop.isEmpty()) {
+ args.sourceCrop = snapshot->bufferSize;
+ }
display = findDisplay(
[layerStack = snapshot->outputFilter.layerStack](const auto& display) {
return display.getLayerStack() == layerStack;
});
}
- } else if (auto* displayRenderAreaBuilder =
- std::get_if<DisplayRenderAreaBuilder>(&renderAreaBuilder)) {
- display = displayRenderAreaBuilder->displayWeak.promote();
+
+ // Screenshot initiated through captureDisplay
+ } else if (auto* displayWeak =
+ std::get_if<wp<const DisplayDevice>>(&args.captureTypeVariant)) {
+ display = displayWeak->promote();
}
if (display == nullptr) {
@@ -7487,112 +7856,66 @@
}
if (display != nullptr) {
- return std::optional{display->getCompositionDisplay()->getState()};
+ const auto& state = display->getCompositionDisplay()->getState();
+ args.displayBrightnessNits = state.displayBrightnessNits;
+ args.sdrWhitePointNits = state.sdrWhitePointNits;
+ args.renderIntent = state.renderIntent;
+ args.colorMode = state.colorMode;
+ return true;
}
}
- return std::nullopt;
+ return false;
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
- const RenderAreaBuilderVariant& renderAreaBuilder,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, bool attachGainmap,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool regionSampling, bool grayscale, bool isProtected,
const sp<IScreenCaptureListener>& captureListener,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer,
+ const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer) {
SFTRACE_CALL();
ScreenCaptureResults captureResults;
- std::unique_ptr<const RenderArea> renderArea =
- std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); },
- renderAreaBuilder);
+ ftl::SharedFuture<FenceResult> renderFuture;
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- if (captureListener) {
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
- captureListener->onScreenCaptureCompleted(captureResults);
- }
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
- float displayBrightnessNits = displayState.value().displayBrightnessNits;
- float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
+ float hdrSdrRatio = args.displayBrightnessNits / args.sdrWhitePointNits;
- ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
- captureResults, displayState, layers);
+ if (hdrBuffer && gainmapBuffer) {
+ ftl::SharedFuture<FenceResult> hdrRenderFuture =
+ renderScreenImpl(args, hdrBuffer, regionSampling, grayscale, isProtected,
+ captureResults, layers);
+ captureResults.buffer = buffer->getBuffer();
+ captureResults.optionalGainMap = gainmapBuffer->getBuffer();
- if (captureResults.capturedHdrLayers && attachGainmap &&
- FlagManager::getInstance().true_hdr_screenshots()) {
- sp<GraphicBuffer> hdrBuffer =
- getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
- HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
- buffer->getUsage(), "screenshot-hdr");
- sp<GraphicBuffer> gainmapBuffer =
- getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
- buffer->getPixelFormat(), 1 /* layerCount */,
- buffer->getUsage(), "screenshot-gainmap");
+ renderFuture =
+ ftl::Future(std::move(hdrRenderFuture))
+ .then([&, hdrSdrRatio, dataspace = captureResults.capturedDataspace, buffer,
+ hdrBuffer, gainmapBuffer](FenceResult fenceResult) -> FenceResult {
+ if (!fenceResult.ok()) {
+ return fenceResult;
+ }
- const status_t bufferStatus = hdrBuffer->initCheck();
- const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
-
- if (bufferStatus != OK) {
- ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__,
- bufferStatus);
- } else if (gainmapBufferStatus != OK) {
- ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
- __func__, gainmapBufferStatus);
- } else {
- captureResults.optionalGainMap = gainmapBuffer;
- const auto hdrTexture = std::make_shared<
- renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::
- Usage::WRITEABLE);
- const auto gainmapTexture = std::make_shared<
- renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::
- Usage::WRITEABLE);
- ScreenCaptureResults unusedResults;
- ftl::SharedFuture<FenceResult> hdrRenderFuture =
- renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
- isProtected, unusedResults, displayState, layers);
-
- renderFuture =
- ftl::Future(std::move(renderFuture))
- .then([&, hdrRenderFuture = std::move(hdrRenderFuture),
- displayBrightnessNits, sdrWhitePointNits,
- dataspace = captureResults.capturedDataspace, buffer, hdrTexture,
- gainmapTexture](FenceResult fenceResult) -> FenceResult {
- if (!fenceResult.ok()) {
- return fenceResult;
- }
-
- auto hdrFenceResult = hdrRenderFuture.get();
-
- if (!hdrFenceResult.ok()) {
- return hdrFenceResult;
- }
-
- return getRenderEngine()
- .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture,
- hdrFenceResult.value()->get(),
- displayBrightnessNits / sdrWhitePointNits,
- static_cast<ui::Dataspace>(dataspace),
- gainmapTexture)
- .get();
- })
- .share();
- };
+ return getRenderEngine()
+ .tonemapAndDrawGainmap(hdrBuffer, fenceResult.value()->get(),
+ hdrSdrRatio,
+ static_cast<ui::Dataspace>(dataspace),
+ buffer, gainmapBuffer)
+ .get();
+ })
+ .share();
+ } else {
+ renderFuture = renderScreenImpl(args, buffer, regionSampling, grayscale, isProtected,
+ captureResults, layers);
}
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
return ftl::Future(std::move(renderFuture))
.then([captureListener, captureResults = std::move(captureResults),
- displayBrightnessNits,
- sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult {
+ hdrSdrRatio](FenceResult fenceResult) mutable -> FenceResult {
captureResults.fenceResult = std::move(fenceResult);
- captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits;
+ captureResults.hdrSdrRatio = hdrSdrRatio;
captureListener->onScreenCaptureCompleted(captureResults);
return base::unexpected(NO_ERROR);
})
@@ -7602,10 +7925,9 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
SFTRACE_CALL();
for (auto& [_, layerFE] : layers) {
@@ -7613,57 +7935,43 @@
captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
layerFE->mSnapshot->geomLayerTransform =
- renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+ args.transform * layerFE->mSnapshot->geomLayerTransform;
layerFE->mSnapshot->geomInverseLayerTransform =
layerFE->mSnapshot->geomLayerTransform.inverse();
}
- auto capturedBuffer = buffer;
+ const bool enableLocalTonemapping =
+ FlagManager::getInstance().local_tonemap_screenshots() && !args.seamlessTransition;
- auto requestedDataspace = renderArea->getReqDataSpace();
- auto parent = renderArea->getParentLayer();
- auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
- auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
- auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
+ captureResults.capturedDataspace =
+ pickBestDataspace(args.dataspace, args.colorMode, captureResults.capturedHdrLayers,
+ args.seamlessTransition);
- captureResults.capturedDataspace = requestedDataspace;
-
- const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() &&
- !renderArea->getHintForSeamlessTransition();
-
- if (displayState) {
- const auto& state = displayState.value();
- captureResults.capturedDataspace =
- pickBestDataspace(requestedDataspace, state, captureResults.capturedHdrLayers,
- renderArea->getHintForSeamlessTransition());
- sdrWhitePointNits = state.sdrWhitePointNits;
-
- if (!captureResults.capturedHdrLayers) {
- displayBrightnessNits = sdrWhitePointNits;
- } else {
- displayBrightnessNits = state.displayBrightnessNits;
- if (!enableLocalTonemapping) {
- // Only clamp the display brightness if this is not a seamless transition.
- // Otherwise for seamless transitions it's important to match the current
- // display state as the buffer will be shown under these same conditions, and we
- // want to avoid any flickers
- if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
- // Restrict the amount of HDR "headroom" in the screenshot to avoid
- // over-dimming the SDR portion. 2.0 chosen by experimentation
- constexpr float kMaxScreenshotHeadroom = 2.0f;
- displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
- displayBrightnessNits);
- }
- }
+ // Only clamp the display brightness if this is not a seamless transition.
+ // Otherwise for seamless transitions it's important to match the current
+ // display state as the buffer will be shown under these same conditions, and we
+ // want to avoid any flickers.
+ if (captureResults.capturedHdrLayers) {
+ if (!enableLocalTonemapping && args.sdrWhitePointNits > 1.0f && !args.seamlessTransition) {
+ // Restrict the amount of HDR "headroom" in the screenshot to avoid
+ // over-dimming the SDR portion. 2.0 chosen by experimentation
+ constexpr float kMaxScreenshotHeadroom = 2.0f;
+ // TODO: Aim to update displayBrightnessNits earlier in screenshot
+ // path so ScreenshotArgs can be passed as const
+ args.displayBrightnessNits = std::min(args.sdrWhitePointNits * kMaxScreenshotHeadroom,
+ args.displayBrightnessNits);
}
-
- // Screenshots leaving the device should be colorimetric
- if (requestedDataspace == ui::Dataspace::UNKNOWN &&
- renderArea->getHintForSeamlessTransition()) {
- renderIntent = state.renderIntent;
- }
+ } else {
+ args.displayBrightnessNits = args.sdrWhitePointNits;
}
+ auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
+ // Screenshots leaving the device should be colorimetric
+ if (args.dataspace == ui::Dataspace::UNKNOWN && args.seamlessTransition) {
+ renderIntent = args.renderIntent;
+ }
+
+ auto capturedBuffer = buffer;
captureResults.buffer = capturedBuffer->getBuffer();
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
@@ -7673,13 +7981,12 @@
}
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
- sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
- layers = std::move(layers), layerStack, regionSampling,
- renderArea = std::move(renderArea), renderIntent,
+ grayscale, isProtected, layers, layerStack, regionSampling, args, renderIntent,
enableLocalTonemapping]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
compositionEngine->setRenderEngine(mRenderEngine.get());
+ compositionEngine->setHwComposer(mHWComposer.get());
std::vector<sp<compositionengine::LayerFE>> layerFEs;
layerFEs.reserve(layers.size());
@@ -7700,9 +8007,10 @@
if (enableLocalTonemapping) {
// Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr
// sdr ratio via display brightness / sdrWhite nits.
- targetBrightness = sdrWhitePointNits / displayBrightnessNits;
+ targetBrightness = args.sdrWhitePointNits / args.displayBrightnessNits;
} else if (dataspace == ui::Dataspace::BT2020_HLG) {
- const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203;
+ const float maxBrightnessNits =
+ args.displayBrightnessNits / args.sdrWhitePointNits * 203;
// With a low dimming ratio, don't fit the entire curve. Otherwise mixed content
// will appear way too bright.
if (maxBrightnessNits < 1000.f) {
@@ -7710,23 +8018,33 @@
}
}
+ // Capturing screenshots using layers have a clear capture fill (0 alpha).
+ // Capturing via display or displayId, which do not use args.layerSequence,
+ // has an opaque capture fill (1 alpha).
+ const float layerAlpha =
+ std::holds_alternative<int32_t>(args.captureTypeVariant) ? 0.0f : 1.0f;
+
// Screenshots leaving the device must not dim in gamma space.
- const bool dimInGammaSpaceForEnhancedScreenshots = mDimInGammaSpaceForEnhancedScreenshots &&
- renderArea->getHintForSeamlessTransition();
+ const bool dimInGammaSpaceForEnhancedScreenshots =
+ mDimInGammaSpaceForEnhancedScreenshots && args.seamlessTransition;
std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine,
.colorProfile = colorProfile,
- .renderArea = *renderArea,
.layerStack = layerStack,
+ .sourceCrop = args.sourceCrop,
.buffer = std::move(buffer),
- .sdrWhitePointNits = sdrWhitePointNits,
- .displayBrightnessNits = displayBrightnessNits,
+ .displayIdVariant = args.displayIdVariant,
+ .reqBufferSize = args.reqSize,
+ .sdrWhitePointNits = args.sdrWhitePointNits,
+ .displayBrightnessNits = args.displayBrightnessNits,
.targetBrightness = targetBrightness,
+ .layerAlpha = layerAlpha,
.regionSampling = regionSampling,
.treat170mAsSrgb = mTreat170mAsSrgb,
.dimInGammaSpaceForEnhancedScreenshots =
dimInGammaSpaceForEnhancedScreenshots,
+ .isSecure = args.isSecure,
.isProtected = isProtected,
.enableLocalTonemapping = enableLocalTonemapping});
@@ -7815,9 +8133,8 @@
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
- if (const bool isPacesetter =
- mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
- /*clearContentRequirements*/ true)) {
+ if (mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
+ /*clearContentRequirements*/ true)) {
mDisplayModeController.updateKernelIdleTimer(displayId);
}
@@ -8080,11 +8397,13 @@
int SurfaceFlinger::calculateMaxAcquiredBufferCount(Fps refreshRate,
std::chrono::nanoseconds presentLatency) {
- auto pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs();
+ int64_t pipelineDepth = presentLatency.count() / refreshRate.getPeriodNsecs();
if (presentLatency.count() % refreshRate.getPeriodNsecs()) {
pipelineDepth++;
}
- return std::max(minAcquiredBuffers, static_cast<int64_t>(pipelineDepth - 1));
+ const int64_t maxAcquiredBuffers =
+ std::min(pipelineDepth - 1, maxAcquiredBuffersOpt.value_or(pipelineDepth - 1));
+ return std::max(minAcquiredBuffers, maxAcquiredBuffers);
}
status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
@@ -8115,8 +8434,7 @@
}
int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const {
- const auto vsyncConfig =
- mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late;
+ const auto vsyncConfig = mScheduler->getVsyncConfigsForRefreshRate(refreshRate).late;
const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
@@ -8144,8 +8462,8 @@
// TODO(b/255635821): Choose the pacesetter display, considering both internal and external
// displays. For now, pick the other internal display, assuming a dual-display foldable.
return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) {
- const auto idOpt = PhysicalDisplayId::tryCast(display.getId());
- return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
+ const auto idOpt = asPhysicalDisplayId(display.getDisplayIdVariant());
+ return idOpt.has_value() && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
mPhysicalDisplays.get(*idOpt)
.transform(&PhysicalDisplay::isInternal)
.value_or(false);
@@ -8202,10 +8520,6 @@
void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel,
int32_t maxLevel) {
- if (!FlagManager::getInstance().connected_display()) {
- return;
- }
-
Mutex::Autolock lock(mStateLock);
const auto idOpt = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
@@ -8226,21 +8540,31 @@
}
static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() {
+ const bool secure = connectedLevel >= 2 /* HDCP_V1 */;
if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) {
Mutex::Autolock lock(mStateLock);
- display->setSecure(connectedLevel >= 2 /* HDCP_V1 */);
+ display->setSecure(secure);
}
+ FTL_FAKE_GUARD(kMainThreadContext, mDisplayModeController.setSecure(displayId, secure));
mScheduler->onHdcpLevelsChanged(scheduler::Cycle::Render, displayId, connectedLevel,
maxLevel);
}));
}
-void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
- if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
- Mutex::Autolock lock(mStateLock);
- mActivePictureListener = listener;
- mHaveNewActivePictureListener = listener != nullptr;
- }
+void SurfaceFlinger::addActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+ Mutex::Autolock lock(mStateLock);
+ std::erase_if(mActivePictureListenersToRemove, [listener](const auto& otherListener) {
+ return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+ });
+ mActivePictureListenersToAdd.push_back(listener);
+}
+
+void SurfaceFlinger::removeActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+ Mutex::Autolock lock(mStateLock);
+ std::erase_if(mActivePictureListenersToAdd, [listener](const auto& otherListener) {
+ return IInterface::asBinder(listener) == IInterface::asBinder(otherListener);
+ });
+ mActivePictureListenersToRemove.push_back(listener);
}
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
@@ -8581,16 +8905,16 @@
}
}
-binder::Status SurfaceComposerAIDL::createVirtualDisplay(const std::string& displayName,
- bool isSecure, const std::string& uniqueId,
- float requestedRefreshRate,
- sp<IBinder>* outDisplay) {
+binder::Status SurfaceComposerAIDL::createVirtualDisplay(
+ const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy, const std::string& uniqueId,
+ float requestedRefreshRate, sp<IBinder>* outDisplay) {
status_t status = checkAccessPermission();
if (status != OK) {
return binderStatusFromStatusT(status);
}
- *outDisplay =
- mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId, requestedRefreshRate);
+ *outDisplay = mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy,
+ uniqueId, requestedRefreshRate);
return binder::Status::ok();
}
@@ -8619,8 +8943,8 @@
if (status != OK) {
return binderStatusFromStatusT(status);
}
- const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
- *outDisplay = mFlinger->getPhysicalDisplayToken(*id);
+ const PhysicalDisplayId id = PhysicalDisplayId::fromValue(static_cast<uint64_t>(displayId));
+ *outDisplay = mFlinger->getPhysicalDisplayToken(id);
return binder::Status::ok();
}
@@ -8678,6 +9002,7 @@
if (status == NO_ERROR) {
// convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
+ outInfo->port = info.port;
outInfo->density = info.density;
outInfo->secure = info.secure;
outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation);
@@ -9193,11 +9518,20 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::setActivePictureListener(
+binder::Status SurfaceComposerAIDL::addActivePictureListener(
const sp<gui::IActivePictureListener>& listener) {
status_t status = checkObservePictureProfilesPermission();
if (status == OK) {
- mFlinger->setActivePictureListener(listener);
+ mFlinger->addActivePictureListener(listener);
+ }
+ return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeActivePictureListener(
+ const sp<gui::IActivePictureListener>& listener) {
+ status_t status = checkObservePictureProfilesPermission();
+ if (status == OK) {
+ mFlinger->removeActivePictureListener(listener);
}
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 44856ae..282922e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -69,13 +69,13 @@
#include <ui/FenceResult.h>
#include <common/FlagManager.h>
-#include "ActivePictureUpdater.h"
-#include "BackgroundExecutor.h"
+#include "ActivePictureTracker.h"
#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
#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"
@@ -87,6 +87,7 @@
#include "LayerVector.h"
#include "MutexUtils.h"
#include "PowerAdvisor/PowerAdvisor.h"
+#include "QueuedTransactionState.h"
#include "Scheduler/ISchedulerCallback.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/Scheduler.h"
@@ -95,19 +96,15 @@
#include "Tracing/LayerTracing.h"
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
-#include "TransactionState.h"
#include "Utils/OnceFuture.h"
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <functional>
-#include <map>
#include <memory>
#include <mutex>
#include <optional>
-#include <queue>
-#include <set>
#include <string>
#include <thread>
#include <type_traits>
@@ -130,13 +127,11 @@
class FpsReporter;
class TunnelModeEnabledReporter;
class HdrLayerInfoReporter;
-class HWComposer;
class IGraphicBufferProducer;
class Layer;
class MessageBase;
class RefreshRateOverlay;
class RegionSamplingThread;
-class RenderArea;
class TimeStats;
class FrameTracer;
class ScreenCapturer;
@@ -165,6 +160,7 @@
class OutputLayer;
struct CompositionRefreshArgs;
+class DisplayCreationArgsBuilder;
} // namespace compositionengine
namespace renderengine {
@@ -201,9 +197,6 @@
Always,
};
-struct DisplayRenderAreaBuilder;
-struct LayerRenderAreaBuilder;
-
using DisplayColorSetting = compositionengine::OutputColorSetting;
class SurfaceFlinger : public BnSurfaceComposer,
@@ -219,11 +212,9 @@
SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API;
explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API;
- // set main thread scheduling policy
- static status_t setSchedFifo(bool enabled) ANDROID_API;
-
- // set main thread scheduling attributes
- static status_t setSchedAttr(bool enabled);
+ // Set scheduling policy and attributes of main thread.
+ static void setSchedFifo(bool enabled, const char* whence);
+ static void setSchedAttr(bool enabled, const char* whence);
static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
@@ -248,6 +239,11 @@
// ISurfaceComposer.getMaxAcquiredBufferCount().
static int64_t minAcquiredBuffers;
+ // Controls the maximum acquired buffers SurfaceFlinger will suggest via
+ // ISurfaceComposer.getMaxAcquiredBufferCount().
+ // Value is set through ro.surface_flinger.max_acquired_buffers.
+ static std::optional<int64_t> maxAcquiredBuffersOpt;
+
// Controls the maximum width and height in pixels that the graphics pipeline can support for
// GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
static uint32_t maxGraphicsWidth;
@@ -354,9 +350,6 @@
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
- virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
- REQUIRES(mStateLock);
-
virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId);
@@ -378,9 +371,7 @@
friend class Layer;
friend class RefreshRateOverlay;
friend class RegionSamplingThread;
- friend class LayerRenderArea;
friend class SurfaceComposerAIDL;
- friend class DisplayRenderArea;
// For unit tests
friend class TestableSurfaceFlinger;
@@ -389,7 +380,6 @@
using TransactionSchedule = scheduler::TransactionSchedule;
using GetLayerSnapshotsFunction = std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>;
- using RenderAreaBuilderVariant = std::variant<DisplayRenderAreaBuilder, LayerRenderAreaBuilder>;
using DumpArgs = Vector<String16>;
using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
@@ -545,6 +535,7 @@
// ISurfaceComposer implementation:
sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
const std::string& uniqueId,
float requestedRefreshRate = 0.0f);
status_t destroyVirtualDisplay(const sp<IBinder>& displayToken);
@@ -554,13 +545,7 @@
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
- status_t setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override;
+ status_t setTransactionState(TransactionState&&) override;
void bootFinished();
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
sp<IDisplayEventConnection> createDisplayEventConnection(
@@ -667,7 +652,9 @@
void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
- void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+ void addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
+ void removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
// IBinder::DeathRecipient overrides:
void binderDied(const wp<IBinder>& who) override;
@@ -730,15 +717,35 @@
Fps maxFps);
void initiateDisplayModeChanges() REQUIRES(kMainThreadContext) REQUIRES(mStateLock);
- void finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+
+ // Returns whether the commit stage should proceed. The return value is ignored when finalizing
+ // immediate mode changes, which happen toward the end of the commit stage.
+ // TODO: b/355427258 - Remove the return value once the `synced_resolution_switch` flag is live.
+ bool finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext)
REQUIRES(mStateLock);
void dropModeRequest(PhysicalDisplayId) REQUIRES(kMainThreadContext);
void applyActiveMode(PhysicalDisplayId) REQUIRES(kMainThreadContext);
// Called on the main thread in response to setPowerMode()
- void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
+ void setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
REQUIRES(mStateLock, kMainThreadContext);
+ void setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
+ REQUIRES(mStateLock, kMainThreadContext);
+
+ // Returns whether to optimize globally for performance instead of power.
+ bool shouldOptimizeForPerformance() REQUIRES(mStateLock);
+
+ // Turns on power optimizations, for example when there are no displays to be optimized for
+ // performance.
+ static void enablePowerOptimizations(const char* whence);
+
+ // Turns off power optimizations.
+ static void disablePowerOptimizations(const char* whence);
+
+ // Enables or disables power optimizations depending on whether there are displays that should
+ // be optimized for performance.
+ void applyOptimizationPolicy(const char* whence) REQUIRES(mStateLock);
// Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
// display. Falls back to the display's defaultModeId otherwise.
@@ -785,7 +792,7 @@
*/
bool applyTransactionState(const FrameTimelineInfo& info,
std::vector<ResolvedComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags,
+ std::span<DisplayState> displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<uint64_t>& uncacheBufferIds,
@@ -797,8 +804,9 @@
// For test only
bool flushTransactionQueues() REQUIRES(kMainThreadContext);
- bool applyTransactions(std::vector<TransactionState>&) REQUIRES(kMainThreadContext);
- bool applyAndCommitDisplayTransactionStatesLocked(std::vector<TransactionState>& transactions)
+ bool applyTransactions(std::vector<QueuedTransactionState>&) REQUIRES(kMainThreadContext);
+ bool applyAndCommitDisplayTransactionStatesLocked(
+ std::vector<QueuedTransactionState>& transactions)
REQUIRES(kMainThreadContext, mStateLock);
// Returns true if there is at least one transaction that needs to be flushed
@@ -827,7 +835,7 @@
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
- bool applyTransactionsLocked(std::vector<TransactionState>& transactions)
+ bool applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions)
REQUIRES(mStateLock, kMainThreadContext);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -845,6 +853,9 @@
status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
sp<Layer>* outLayer);
+ // Checks if there are layer leaks before creating layer
+ status_t checkLayerLeaks();
+
status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
gui::CreateSurfaceResult& outResult);
@@ -865,31 +876,77 @@
using OutputCompositionState = compositionengine::impl::OutputCompositionState;
- std::optional<OutputCompositionState> getSnapshotsFromMainThread(
- RenderAreaBuilderVariant& renderAreaBuilder,
- GetLayerSnapshotsFunction getLayerSnapshotsFn,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+ /*
+ * Parameters used across screenshot methods.
+ */
+ struct ScreenshotArgs {
+ // Contains the sequence ID of the parent layer if the screenshot is
+ // initiated though captureLayers(), or the display that the render
+ // result will be on if initiated through captureDisplay()
+ std::variant<int32_t, wp<const DisplayDevice>> captureTypeVariant;
- void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
- ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
- bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&);
+ // Display ID of the display the result will be on
+ ftl::Optional<DisplayIdVariant> displayIdVariant{std::nullopt};
- std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
- RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
+ // If true, transform is inverted from the parent layer snapshot
+ bool childrenOnly{false};
+
+ // Source crop of the render area
+ Rect sourceCrop;
+
+ // Transform to be applied on the layers to transform them
+ // into the logical render area
+ ui::Transform transform;
+
+ // Size of the physical render area
+ ui::Size reqSize;
+
+ // Composition dataspace of the render area
+ ui::Dataspace dataspace;
+
+ // If false, the secure layer is blacked out or skipped
+ // when rendered to an insecure render area
+ bool isSecure{false};
+
+ // If true, the render result may be used for system animations
+ // that must preserve the exact colors of the display
+ bool seamlessTransition{false};
+
+ // Current display brightness of the output composition state
+ float displayBrightnessNits{-1.f};
+
+ // SDR white point of the output composition state
+ float sdrWhitePointNits{-1.f};
+
+ // Current active color mode of the output composition state
+ ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+ // Current active render intent of the output composition state
+ ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+ };
+
+ bool getSnapshotsFromMainThread(ScreenshotArgs& args,
+ GetLayerSnapshotsFunction getLayerSnapshotsFn,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+
+ void captureScreenCommon(ScreenshotArgs& args, GetLayerSnapshotsFunction, ui::Size bufferSize,
+ ui::PixelFormat, bool allowProtected, bool grayscale,
+ const sp<IScreenCaptureListener>&);
+
+ bool getDisplayStateOnMainThread(ScreenshotArgs& args) REQUIRES(kMainThreadContext);
ftl::SharedFuture<FenceResult> captureScreenshot(
- const RenderAreaBuilderVariant& renderAreaBuilder,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, bool attachGainmap,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool regionSampling, bool grayscale, bool isProtected,
const sp<IScreenCaptureListener>& captureListener,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer = nullptr,
+ const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer = nullptr);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
+ ScreenshotArgs& args, const std::shared_ptr<renderengine::ExternalTexture>&,
bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&,
- std::optional<OutputCompositionState>& displayState,
- std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
bool canAllocateHwcDisplayIdForVDS(uint64_t usage);
@@ -985,10 +1042,10 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty);
- ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
+ ui::LayerFilter makeLayerFilterForDisplay(DisplayIdVariant displayId, ui::LayerStack layerStack)
REQUIRES(mStateLock) {
return {layerStack,
- PhysicalDisplayId::tryCast(displayId)
+ asPhysicalDisplayId(displayId)
.and_then(display::getPhysicalDisplay(mPhysicalDisplays))
.transform(&display::PhysicalDisplay::isInternal)
.value_or(false)};
@@ -1027,7 +1084,8 @@
// Returns the active mode ID, or nullopt on hotplug failure.
std::optional<DisplayModeId> processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId,
DisplayIdentificationInfo&&,
- const char* displayString)
+ const char* displayString,
+ HWComposer::HotplugEvent event)
REQUIRES(mStateLock, kMainThreadContext);
void processHotplugDisconnect(PhysicalDisplayId, const char* displayString)
REQUIRES(mStateLock, kMainThreadContext);
@@ -1039,6 +1097,8 @@
const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
+ void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
+ REQUIRES(mStateLock, kMainThreadContext);
void processDisplayRemoved(const wp<IBinder>& displayToken)
REQUIRES(mStateLock, kMainThreadContext);
void processDisplayChanged(const wp<IBinder>& displayToken,
@@ -1078,9 +1138,11 @@
void enableHalVirtualDisplays(bool);
// Virtual display lifecycle for ID generation and HAL allocation.
- VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId,
- bool canAllocateHwcForVDS)
+ std::optional<VirtualDisplayIdVariant> acquireVirtualDisplay(
+ ui::Size, ui::PixelFormat, const std::string& uniqueId,
+ compositionengine::DisplayCreationArgsBuilder&, bool canAllocateHwcDisplayIdForVDS)
REQUIRES(mStateLock);
+
template <typename ID>
void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) {
std::lock_guard lock(mVirtualDisplaysMutex);
@@ -1091,7 +1153,7 @@
}
}
- void releaseVirtualDisplay(VirtualDisplayId);
+ void releaseVirtualDisplay(VirtualDisplayIdVariant displayId);
void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId);
// Returns a display other than `mActiveDisplayId` that can be activated, if any.
@@ -1176,7 +1238,7 @@
bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
- ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
+ ui::Rotation getPhysicalDisplayOrientation(PhysicalDisplayId, bool isPrimary) const
REQUIRES(mStateLock);
void traverseLegacyLayers(const LayerVector::Visitor& visitor) const
REQUIRES(kMainThreadContext);
@@ -1277,11 +1339,9 @@
struct HotplugEvent {
hal::HWDisplayId hwcDisplayId;
- hal::Connection connection = hal::Connection::INVALID;
+ HWComposer::HotplugEvent event;
};
- bool mIsHdcpViaNegVsync = false;
-
std::mutex mHotplugMutex;
std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex);
@@ -1363,6 +1423,7 @@
std::atomic<int> mNumTrustedPresentationListeners = 0;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+ std::unique_ptr<HWComposer> mHWComposer;
CompositionCoveragePerDisplay mCompositionCoverage;
@@ -1398,16 +1459,14 @@
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
- // Tracks the number of maximum queued buffers by layer owner Uid.
- using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
- sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
- bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
- ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext);
+ ActivePictureTracker mActivePictureTracker GUARDED_BY(kMainThreadContext);
+ ActivePictureTracker::Listeners mActivePictureListenersToAdd GUARDED_BY(mStateLock);
+ ActivePictureTracker::Listeners mActivePictureListenersToRemove GUARDED_BY(mStateLock);
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
@@ -1526,9 +1585,11 @@
const sp<IBinder>& layerHandle,
sp<gui::IDisplayEventConnection>* outConnection) override;
binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
- binder::Status createVirtualDisplay(const std::string& displayName, bool isSecure,
- const std::string& uniqueId, float requestedRefreshRate,
- sp<IBinder>* outDisplay) override;
+ binder::Status createVirtualDisplay(
+ const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
+ const std::string& uniqueId, float requestedRefreshRate,
+ sp<IBinder>* outDisplay) override;
binder::Status destroyVirtualDisplay(const sp<IBinder>& displayToken) override;
binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override;
binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override;
@@ -1648,8 +1709,8 @@
binder::Status flushJankData(int32_t layerId) override;
binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
int64_t afterVsync) override;
- binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
- binder::Status clearActivePictureListener();
+ binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+ binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>& listener);
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index f39a4d2..6bbc04c 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -20,8 +20,8 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "LayerProtoHelper.h"
+#include "QueuedTransactionState.h"
#include "TransactionProtoParser.h"
-#include "TransactionState.h"
#include "gui/LayerState.h"
namespace android::surfaceflinger {
@@ -51,7 +51,8 @@
~FakeExternalTexture() = default;
};
-perfetto::protos::TransactionState TransactionProtoParser::toProto(const TransactionState& t) {
+perfetto::protos::TransactionState TransactionProtoParser::toProto(
+ const QueuedTransactionState& t) {
perfetto::protos::TransactionState proto;
proto.set_pid(t.originPid);
proto.set_uid(t.originUid);
@@ -138,7 +139,8 @@
colorProto->set_b(layer.color.b);
}
if (layer.what & layer_state_t::eTransparentRegionChanged) {
- LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
+ LayerProtoHelper::writeToProto(layer.getTransparentRegion(),
+ proto.mutable_transparent_region());
}
if (layer.what & layer_state_t::eBufferTransformChanged) {
proto.set_transform(layer.bufferTransform);
@@ -190,33 +192,30 @@
}
if (layer.what & layer_state_t::eInputInfoChanged) {
- if (layer.windowInfoHandle) {
- const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
- perfetto::protos::LayerState_WindowInfo* windowInfoProto =
- proto.mutable_window_info_handle();
- windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
- windowInfoProto->set_layout_params_type(
- static_cast<int32_t>(inputInfo->layoutParamsType));
- windowInfoProto->set_input_config(inputInfo->inputConfig.get());
- LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
- windowInfoProto->mutable_touchable_region());
- windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
- windowInfoProto->set_focusable(
- !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
- windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
- gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
- windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
- perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform();
- transformProto->set_dsdx(inputInfo->transform.dsdx());
- transformProto->set_dtdx(inputInfo->transform.dtdx());
- transformProto->set_dtdy(inputInfo->transform.dtdy());
- transformProto->set_dsdy(inputInfo->transform.dsdy());
- transformProto->set_tx(inputInfo->transform.tx());
- transformProto->set_ty(inputInfo->transform.ty());
- windowInfoProto->set_replace_touchable_region_with_crop(
- inputInfo->replaceTouchableRegionWithCrop);
- windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
- }
+ const gui::WindowInfo* inputInfo = &layer.getWindowInfo();
+ perfetto::protos::LayerState_WindowInfo* windowInfoProto =
+ proto.mutable_window_info_handle();
+ windowInfoProto->set_layout_params_flags(inputInfo->layoutParamsFlags.get());
+ windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->layoutParamsType));
+ windowInfoProto->set_input_config(inputInfo->inputConfig.get());
+ LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
+ windowInfoProto->mutable_touchable_region());
+ windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
+ windowInfoProto->set_focusable(
+ !inputInfo->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE));
+ windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
+ windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
+ perfetto::protos::Transform* transformProto = windowInfoProto->mutable_transform();
+ transformProto->set_dsdx(inputInfo->transform.dsdx());
+ transformProto->set_dtdx(inputInfo->transform.dtdx());
+ transformProto->set_dtdy(inputInfo->transform.dtdy());
+ transformProto->set_dsdy(inputInfo->transform.dsdy());
+ transformProto->set_tx(inputInfo->transform.tx());
+ transformProto->set_ty(inputInfo->transform.ty());
+ windowInfoProto->set_replace_touchable_region_with_crop(
+ inputInfo->replaceTouchableRegionWithCrop);
+ windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
}
if (layer.what & layer_state_t::eBackgroundColorChanged) {
proto.set_bg_color_alpha(layer.bgColor.a);
@@ -300,9 +299,9 @@
return proto;
}
-TransactionState TransactionProtoParser::fromProto(
+QueuedTransactionState TransactionProtoParser::fromProto(
const perfetto::protos::TransactionState& proto) {
- TransactionState t;
+ QueuedTransactionState t;
t.originPid = proto.pid();
t.originUid = proto.uid();
t.frameTimelineInfo.vsyncId = proto.vsync_id();
@@ -322,7 +321,7 @@
int32_t displayCount = proto.display_changes_size();
t.displays.reserve(static_cast<size_t>(displayCount));
for (int i = 0; i < displayCount; i++) {
- t.displays.add(fromProto(proto.display_changes(i)));
+ t.displays.emplace_back(fromProto(proto.display_changes(i)));
}
return t;
}
@@ -409,7 +408,9 @@
layer.color.b = colorProto.b();
}
if (proto.what() & layer_state_t::eTransparentRegionChanged) {
- LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
+ Region transparentRegion;
+ LayerProtoHelper::readFromProto(proto.transparent_region(), transparentRegion);
+ layer.updateTransparentRegion(transparentRegion);
}
if (proto.what() & layer_state_t::eBufferTransformChanged) {
layer.bufferTransform = proto.transform();
@@ -485,7 +486,7 @@
windowInfoProto.replace_touchable_region_with_crop();
resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id();
- layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
+ *layer.editWindowInfo() = inputInfo;
}
if (proto.what() & layer_state_t::eBackgroundColorChanged) {
layer.bgColor.a = proto.bg_color_alpha();
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index b3ab71c..a02e231 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -21,7 +21,7 @@
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
-#include "TransactionState.h"
+#include "QueuedTransactionState.h"
namespace android::surfaceflinger {
@@ -44,14 +44,14 @@
TransactionProtoParser(std::unique_ptr<FlingerDataMapper> provider)
: mMapper(std::move(provider)) {}
- perfetto::protos::TransactionState toProto(const TransactionState&);
+ perfetto::protos::TransactionState toProto(const QueuedTransactionState&);
perfetto::protos::TransactionState toProto(
const std::map<uint32_t /* layerId */, TracingLayerState>&);
perfetto::protos::LayerCreationArgs toProto(const LayerCreationArgs& args);
perfetto::protos::LayerState toProto(const ResolvedComposerState&);
static perfetto::protos::DisplayInfo toProto(const frontend::DisplayInfo&, uint32_t layerStack);
- TransactionState fromProto(const perfetto::protos::TransactionState&);
+ QueuedTransactionState fromProto(const perfetto::protos::TransactionState&);
void mergeFromProto(const perfetto::protos::LayerState&, TracingLayerState& outState);
void fromProto(const perfetto::protos::LayerCreationArgs&, LayerCreationArgs& outArgs);
std::unique_ptr<FlingerDataMapper> mMapper;
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index bc9f809..1cd7517 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -166,7 +166,7 @@
mBuffer.dump(result);
}
-void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
+void TransactionTracing::addQueuedTransaction(const QueuedTransactionState& transaction) {
perfetto::protos::TransactionState* state =
new perfetto::protos::TransactionState(mProtoParser.toProto(transaction));
mTransactionQueue.push(state);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 7a0fb5e..d784168 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -134,7 +134,7 @@
// Flush event from perfetto data source
void onFlush(Mode mode);
- void addQueuedTransaction(const TransactionState&);
+ void addQueuedTransaction(const QueuedTransactionState&);
void addCommittedTransactions(int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
const frontend::DisplayInfos&, bool displayInfoChanged);
status_t writeToFile(const std::string& filename = FILE_PATH);
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index 63c1b37..d2ffcc0 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -27,6 +27,7 @@
defaults: [
"libsurfaceflinger_mocks_defaults",
"librenderengine_deps",
+ "poweradvisor_deps",
"surfaceflinger_defaults",
"libsurfaceflinger_common_deps",
],
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 1dba175..84d837c 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -31,8 +31,8 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/RequestedLayerState.h"
#include "LayerProtoHelper.h"
+#include "QueuedTransactionState.h"
#include "Tracing/LayerTracing.h"
-#include "TransactionState.h"
#include "cutils/properties.h"
#include "LayerTraceGenerator.h"
@@ -95,18 +95,17 @@
addedLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
}
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.reserve((size_t)entry.transactions_size());
for (int j = 0; j < entry.transactions_size(); j++) {
// apply transactions
- TransactionState transaction = parser.fromProto(entry.transactions(j));
+ QueuedTransactionState transaction = parser.fromProto(entry.transactions(j));
for (auto& resolvedComposerState : transaction.states) {
if (resolvedComposerState.state.what & layer_state_t::eInputInfoChanged) {
- if (!resolvedComposerState.state.windowInfoHandle->getInfo()->inputConfig.test(
+ if (!resolvedComposerState.state.getWindowInfo().inputConfig.test(
gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
// create a fake token since the FE expects a valid token
- resolvedComposerState.state.windowInfoHandle->editInfo()->token =
- sp<BBinder>::make();
+ resolvedComposerState.state.editWindowInfo()->token = sp<BBinder>::make();
}
}
}
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index b22ec66..2e8c8c1 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -18,7 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "TransactionCallbackInvoker"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +28,7 @@
#include "Utils/FenceUtils.h"
#include <binder/IInterface.h>
+#include <common/FlagManager.h>
#include <common/trace.h>
#include <utils/RefBase.h>
@@ -142,8 +143,17 @@
handle->transformHint,
handle->currentMaxAcquiredBufferCount,
eventStats, handle->previousReleaseCallbackId);
+
if (handle->bufferReleaseChannel &&
handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
+ if (FlagManager::getInstance().monitor_buffer_fences()) {
+ if (auto previousBuffer = handle->previousBuffer.lock()) {
+ previousBuffer->getBuffer()
+ ->getDependencyMonitor()
+ .addEgress(FenceTime::makeValid(handle->previousReleaseFence),
+ "Txn release");
+ }
+ }
mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel,
handle->previousReleaseCallbackId,
handle->previousReleaseFence,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 178ddbb..34f6ffc 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -25,6 +25,7 @@
#include <ftl/future.h>
#include <gui/BufferReleaseChannel.h>
#include <gui/ITransactionCompletedListener.h>
+#include <renderengine/ExternalTexture.h>
#include <ui/Fence.h>
#include <ui/FenceResult.h>
@@ -55,6 +56,7 @@
uint64_t previousFrameNumber = 0;
ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
+ std::weak_ptr<renderengine::ExternalTexture> previousBuffer;
};
class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h
deleted file mode 100644
index 215472b..0000000
--- a/services/surfaceflinger/Utils/RingBuffer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stddef.h>
-#include <array>
-
-namespace android::utils {
-
-template <class T, size_t SIZE>
-class RingBuffer {
- RingBuffer(const RingBuffer&) = delete;
- void operator=(const RingBuffer&) = delete;
-
-public:
- RingBuffer() = default;
- ~RingBuffer() = default;
-
- constexpr size_t capacity() const { return SIZE; }
-
- size_t size() const { return mCount; }
-
- T& next() {
- mHead = static_cast<size_t>(mHead + 1) % SIZE;
- if (mCount < SIZE) {
- mCount++;
- }
- return mBuffer[static_cast<size_t>(mHead)];
- }
-
- T& front() { return (*this)[0]; }
- const T& front() const { return (*this)[0]; }
-
- T& back() { return (*this)[size() - 1]; }
- const T& back() const { return (*this)[size() - 1]; }
-
- T& operator[](size_t index) {
- return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
- }
-
- const T& operator[](size_t index) const {
- return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
- }
-
- void clear() {
- mCount = 0;
- mHead = -1;
- }
-
-private:
- std::array<T, SIZE> mBuffer;
- int mHead = -1;
- size_t mCount = 0;
-};
-
-} // namespace android::utils
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 8786f6e..c68513e 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -16,12 +16,13 @@
],
shared_libs: [
"libSurfaceFlingerProp",
- "server_configurable_flags",
"libaconfig_storage_read_api_cc",
"libtracing_perfetto",
+ "server_configurable_flags",
],
static_libs: [
"librenderengine_includes",
+ "libgui_window_info_static",
],
srcs: [
"FlagManager.cpp",
@@ -37,11 +38,12 @@
"libsurfaceflinger_common_defaults",
],
static_libs: [
- "libsurfaceflingerflags",
"aconfig_hardware_flags_c_lib",
+ "android.companion.virtualdevice.flags-aconfig-cc",
"android.os.flags-aconfig-cc",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
+ "libsurfaceflingerflags",
],
}
@@ -51,44 +53,47 @@
"libsurfaceflinger_common_defaults",
],
static_libs: [
- "libsurfaceflingerflags_test",
"aconfig_hardware_flags_c_lib",
+ "android.companion.virtualdevice.flags-aconfig-cc",
"android.os.flags-aconfig-cc-test",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
+ "libsurfaceflingerflags_test",
],
}
cc_defaults {
name: "libsurfaceflinger_common_deps",
shared_libs: [
- "server_configurable_flags",
"libaconfig_storage_read_api_cc",
"libtracing_perfetto",
+ "server_configurable_flags",
],
static_libs: [
- "libsurfaceflinger_common",
- "libsurfaceflingerflags",
"aconfig_hardware_flags_c_lib",
+ "android.companion.virtualdevice.flags-aconfig-cc",
"android.os.flags-aconfig-cc",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
],
}
cc_defaults {
name: "libsurfaceflinger_common_test_deps",
shared_libs: [
- "server_configurable_flags",
"libaconfig_storage_read_api_cc",
"libtracing_perfetto",
+ "server_configurable_flags",
],
static_libs: [
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
"aconfig_hardware_flags_c_lib",
+ "android.companion.virtualdevice.flags-aconfig-cc",
"android.os.flags-aconfig-cc-test",
"android.server.display.flags-aconfig-cc",
"libguiflags_no_apex",
+ "libsurfaceflinger_common_test",
+ "libsurfaceflingerflags_test",
],
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 5e78426..ebf4515 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -26,8 +26,9 @@
#include <server_configurable_flags/get_flags.h>
#include <cinttypes>
-#include <android_os.h>
+#include <android_companion_virtualdevice_flags.h>
#include <android_hardware_flags.h>
+#include <android_os.h>
#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <com_android_server_display_feature_flags.h>
@@ -104,68 +105,85 @@
dumpFlag(result, (aconfig), #name, std::bind(&FlagManager::name, this))
#define DUMP_LEGACY_SERVER_FLAG(name) DUMP_FLAG_INTERNAL(name, false)
#define DUMP_ACONFIG_FLAG(name) DUMP_FLAG_INTERNAL(name, true)
+#define DUMP_SYSPROP_FLAG(name) \
+ dumpFlag(result, (true), "debug.sf." #name, std::bind(&FlagManager::name, this))
base::StringAppendF(&result, "FlagManager values: \n");
+ /// Sysprop flags ///
+ DUMP_SYSPROP_FLAG(disable_sched_fifo_sf);
+ DUMP_SYSPROP_FLAG(disable_sched_fifo_sf_binder);
+ DUMP_SYSPROP_FLAG(disable_sched_fifo_sf_sched);
+ DUMP_SYSPROP_FLAG(disable_sched_fifo_re);
+ DUMP_SYSPROP_FLAG(disable_sched_fifo_composer);
+ DUMP_SYSPROP_FLAG(disable_sched_fifo_composer_callback);
+
/// Legacy server flags ///
DUMP_LEGACY_SERVER_FLAG(use_adpf_cpu_hint);
DUMP_LEGACY_SERVER_FLAG(use_skia_tracing);
/// Trunk stable server (R/W) flags ///
- DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
DUMP_ACONFIG_FLAG(adpf_gpu_sf);
DUMP_ACONFIG_FLAG(adpf_native_session_manager);
DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
+ DUMP_ACONFIG_FLAG(correct_virtual_display_power_state);
DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
+ DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold);
+ DUMP_ACONFIG_FLAG(monitor_buffer_fences);
+ DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
+ DUMP_ACONFIG_FLAG(vsync_predictor_recovery);
/// Trunk stable readonly flags ///
- DUMP_ACONFIG_FLAG(adpf_fmq_sf);
- DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum);
- DUMP_ACONFIG_FLAG(connected_display);
- DUMP_ACONFIG_FLAG(enable_small_area_detection);
- DUMP_ACONFIG_FLAG(stable_edid_ids);
- DUMP_ACONFIG_FLAG(frame_rate_category_mrr);
- DUMP_ACONFIG_FLAG(misc1);
- DUMP_ACONFIG_FLAG(vrr_config);
- DUMP_ACONFIG_FLAG(hdcp_level_hal);
- DUMP_ACONFIG_FLAG(multithreaded_present);
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
DUMP_ACONFIG_FLAG(add_sf_skipped_frames_to_trace);
- DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
- DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
- DUMP_ACONFIG_FLAG(enable_fro_dependent_features);
- DUMP_ACONFIG_FLAG(display_protected);
- DUMP_ACONFIG_FLAG(fp16_client_target);
- DUMP_ACONFIG_FLAG(game_default_frame_rate);
- DUMP_ACONFIG_FLAG(enable_layer_command_batching);
- DUMP_ACONFIG_FLAG(vulkan_renderengine);
- DUMP_ACONFIG_FLAG(renderable_buffer_usage);
- DUMP_ACONFIG_FLAG(vrr_bugfix_24q4);
- DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame);
- DUMP_ACONFIG_FLAG(restore_blur_step);
- DUMP_ACONFIG_FLAG(dont_skip_on_early_ro);
- DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
- DUMP_ACONFIG_FLAG(protected_if_client);
- DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
- DUMP_ACONFIG_FLAG(graphite_renderengine);
- DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts);
- DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
- DUMP_ACONFIG_FLAG(deprecate_vsync_sf);
+ DUMP_ACONFIG_FLAG(adpf_fmq_sf);
DUMP_ACONFIG_FLAG(allow_n_vsyncs_in_targeter);
- DUMP_ACONFIG_FLAG(detached_mirror);
+ DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum);
+ DUMP_ACONFIG_FLAG(begone_bright_hlg);
+ DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
DUMP_ACONFIG_FLAG(commit_not_composited);
+ DUMP_ACONFIG_FLAG(connected_display_hdr);
DUMP_ACONFIG_FLAG(correct_dpi_with_display_size);
- DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
- DUMP_ACONFIG_FLAG(override_trusted_overlay);
+ DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
+ DUMP_ACONFIG_FLAG(deprecate_vsync_sf);
+ DUMP_ACONFIG_FLAG(detached_mirror);
+ DUMP_ACONFIG_FLAG(display_config_error_hal);
+ DUMP_ACONFIG_FLAG(display_protected);
+ DUMP_ACONFIG_FLAG(dont_skip_on_early_ro);
+ DUMP_ACONFIG_FLAG(enable_fro_dependent_features);
+ DUMP_ACONFIG_FLAG(enable_layer_command_batching);
+ DUMP_ACONFIG_FLAG(enable_small_area_detection);
+ DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts);
DUMP_ACONFIG_FLAG(flush_buffer_slots_to_uncache);
DUMP_ACONFIG_FLAG(force_compile_graphite_renderengine);
+ DUMP_ACONFIG_FLAG(fp16_client_target);
+ DUMP_ACONFIG_FLAG(frame_rate_category_mrr);
+ DUMP_ACONFIG_FLAG(game_default_frame_rate);
+ DUMP_ACONFIG_FLAG(graphite_renderengine);
+ DUMP_ACONFIG_FLAG(hdcp_level_hal);
+ DUMP_ACONFIG_FLAG(hdcp_negotiation);
+ DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
+ DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
+ DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
+ DUMP_ACONFIG_FLAG(misc1);
+ DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
+ DUMP_ACONFIG_FLAG(override_trusted_overlay);
+ DUMP_ACONFIG_FLAG(protected_if_client);
+ DUMP_ACONFIG_FLAG(reject_dupe_layerstacks);
+ DUMP_ACONFIG_FLAG(renderable_buffer_usage);
+ DUMP_ACONFIG_FLAG(restore_blur_step);
+ DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
+ DUMP_ACONFIG_FLAG(stable_edid_ids);
+ DUMP_ACONFIG_FLAG(synced_resolution_switch);
DUMP_ACONFIG_FLAG(trace_frame_rate_override);
DUMP_ACONFIG_FLAG(true_hdr_screenshots);
- DUMP_ACONFIG_FLAG(display_config_error_hal);
- DUMP_ACONFIG_FLAG(connected_display_hdr);
- DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
- DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
- DUMP_ACONFIG_FLAG(begone_bright_hlg);
+ DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
+ DUMP_ACONFIG_FLAG(vrr_bugfix_24q4);
+ DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame);
+ DUMP_ACONFIG_FLAG(vrr_config);
+ DUMP_ACONFIG_FLAG(vulkan_renderengine);
DUMP_ACONFIG_FLAG(window_blur_kawase2);
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
#undef DUMP_ACONFIG_FLAG
#undef DUMP_LEGACY_SERVER_FLAG
@@ -182,6 +200,12 @@
const auto res = parseBool(value.c_str());
return res.has_value() && res.value();
}
+#define FLAG_MANAGER_SYSPROP_FLAG(name, defaultVal) \
+ bool FlagManager::name() const { \
+ static const bool kFlagValue = \
+ base::GetBoolProperty("debug.sf." #name, /* default value*/ defaultVal); \
+ return kFlagValue; \
+ }
#define FLAG_MANAGER_LEGACY_SERVER_FLAG(name, syspropOverride, serverFlagName) \
bool FlagManager::name() const { \
@@ -212,6 +236,14 @@
#define FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(name, syspropOverride, owner) \
FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner)
+/// Debug sysprop flags - default value is always false ///
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf_binder, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_sf_sched, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_re, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_composer, /* default */ false)
+FLAG_MANAGER_SYSPROP_FLAG(disable_sched_fifo_composer_callback, /* default */ false)
+
/// Legacy server flags ///
FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint",
@@ -222,14 +254,13 @@
/// Trunk stable readonly flags ///
FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "")
FLAG_MANAGER_ACONFIG_FLAG(arr_setframerate_gte_enum, "debug.sf.arr_setframerate_gte_enum")
-FLAG_MANAGER_ACONFIG_FLAG(connected_display, "")
FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "")
FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids")
FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
FLAG_MANAGER_ACONFIG_FLAG(misc1, "")
FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config")
FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "")
-FLAG_MANAGER_ACONFIG_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
+FLAG_MANAGER_ACONFIG_FLAG(hdcp_negotiation, "debug.sf.hdcp_negotiation");
FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "")
FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "")
FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved,
@@ -266,15 +297,22 @@
FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, "");
+FLAG_MANAGER_ACONFIG_FLAG(reject_dupe_layerstacks, "");
+FLAG_MANAGER_ACONFIG_FLAG(synced_resolution_switch, "");
/// Trunk stable server (R/W) flags ///
FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
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, "");
+FLAG_MANAGER_ACONFIG_FLAG(monitor_buffer_fences, "");
+FLAG_MANAGER_ACONFIG_FLAG(vsync_predictor_recovery, "");
/// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(correct_virtual_display_power_state, "",
+ android::companion::virtualdevice::flags)
/// Trunk stable readonly flags from outside SurfaceFlinger ///
FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index d8887f5..72b3bc3 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -42,68 +42,83 @@
void setUnitTestMode();
+ /// Debug sysprop flags ///
+ bool disable_sched_fifo_sf() const;
+ bool disable_sched_fifo_sf_binder() const;
+ bool disable_sched_fifo_sf_sched() const;
+ bool disable_sched_fifo_re() const;
+ bool disable_sched_fifo_composer() const;
+ bool disable_sched_fifo_composer_callback() const;
+
/// Legacy server flags ///
bool test_flag() const;
bool use_adpf_cpu_hint() const;
bool use_skia_tracing() const;
/// Trunk stable server (R/W) flags ///
- bool refresh_rate_overlay_on_external_display() const;
bool adpf_gpu_sf() const;
- bool adpf_use_fmq_channel() const;
bool adpf_native_session_manager() const;
+ bool adpf_use_fmq_channel() const;
bool adpf_use_fmq_channel_fixed() const;
+ bool correct_virtual_display_power_state() const;
bool graphite_renderengine_preview_rollout() const;
+ bool increase_missed_frame_jank_threshold() const;
+ bool monitor_buffer_fences() const;
+ bool refresh_rate_overlay_on_external_display() const;
+ bool vsync_predictor_recovery() const;
/// Trunk stable readonly flags ///
- bool arr_setframerate_gte_enum() const;
- bool adpf_fmq_sf() const;
- bool connected_display() const;
- bool frame_rate_category_mrr() const;
- bool enable_small_area_detection() const;
- bool stable_edid_ids() const;
- bool misc1() const;
- bool vrr_config() const;
- bool hdcp_level_hal() const;
- bool multithreaded_present() const;
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
bool add_sf_skipped_frames_to_trace() const;
- bool use_known_refresh_rate_for_fps_consistency() const;
- bool cache_when_source_crop_layer_only_moved() const;
- bool enable_fro_dependent_features() const;
- bool display_protected() const;
- bool fp16_client_target() const;
- bool game_default_frame_rate() const;
- bool enable_layer_command_batching() const;
- bool vulkan_renderengine() const;
- bool vrr_bugfix_24q4() const;
- bool vrr_bugfix_dropped_frame() const;
- bool renderable_buffer_usage() const;
- bool restore_blur_step() const;
- bool dont_skip_on_early_ro() const;
- bool no_vsyncs_on_screen_off() const;
- bool protected_if_client() const;
- bool idle_screen_refresh_rate_timeout() const;
- bool graphite_renderengine() const;
- bool filter_frames_before_trace_starts() const;
- bool latch_unsignaled_with_auto_refresh_changed() const;
- bool deprecate_vsync_sf() const;
+ bool adpf_fmq_sf() const;
bool allow_n_vsyncs_in_targeter() const;
- bool detached_mirror() const;
+ bool arr_setframerate_gte_enum() const;
+ bool begone_bright_hlg() const;
+ bool cache_when_source_crop_layer_only_moved() const;
bool commit_not_composited() const;
+ bool connected_display_hdr() const;
bool correct_dpi_with_display_size() const;
- bool local_tonemap_screenshots() const;
- bool override_trusted_overlay() const;
+ bool deprecate_frame_tracker() const;
+ bool deprecate_vsync_sf() const;
+ bool detached_mirror() const;
+ bool display_config_error_hal() const;
+ bool display_protected() const;
+ bool dont_skip_on_early_ro() const;
+ bool enable_fro_dependent_features() const;
+ bool enable_layer_command_batching() const;
+ bool enable_small_area_detection() const;
+ bool filter_frames_before_trace_starts() const;
bool flush_buffer_slots_to_uncache() const;
bool force_compile_graphite_renderengine() const;
+ bool fp16_client_target() const;
+ bool frame_rate_category_mrr() const;
+ bool game_default_frame_rate() const;
+ bool graphite_renderengine() const;
+ bool hdcp_level_hal() const;
+ bool hdcp_negotiation() const;
+ bool idle_screen_refresh_rate_timeout() const;
+ bool latch_unsignaled_with_auto_refresh_changed() const;
+ bool local_tonemap_screenshots() const;
+ bool luts_api() const;
+ bool misc1() const;
+ bool no_vsyncs_on_screen_off() const;
+ bool override_trusted_overlay() const;
+ bool protected_if_client() const;
+ bool reject_dupe_layerstacks() const;
+ bool renderable_buffer_usage() const;
+ bool restore_blur_step() const;
+ bool skip_invisible_windows_in_input() const;
+ bool stable_edid_ids() const;
+ bool synced_resolution_switch() const;
bool trace_frame_rate_override() const;
bool true_hdr_screenshots() const;
- bool display_config_error_hal() const;
- bool connected_display_hdr() const;
- bool deprecate_frame_tracker() const;
- bool skip_invisible_windows_in_input() const;
- bool begone_bright_hlg() const;
- bool luts_api() const;
+ bool use_known_refresh_rate_for_fps_consistency() const;
+ bool vrr_bugfix_24q4() const;
+ bool vrr_bugfix_dropped_frame() const;
+ bool vrr_config() const;
+ bool vulkan_renderengine() const;
bool window_blur_kawase2() const;
+ /// IMPORTANT - please keep alphabetize to reduce merge conflicts
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/common/include/common/WorkloadTracer.h b/services/surfaceflinger/common/include/common/WorkloadTracer.h
new file mode 100644
index 0000000..c4074f7
--- /dev/null
+++ b/services/surfaceflinger/common/include/common/WorkloadTracer.h
@@ -0,0 +1,29 @@
+
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <ftl/flags.h>
+#include <stdint.h>
+namespace android::WorkloadTracer {
+
+static constexpr int32_t COMPOSITION_TRACE_COOKIE = 1;
+static constexpr int32_t POST_COMPOSITION_TRACE_COOKIE = 2;
+static constexpr size_t COMPOSITION_SUMMARY_SIZE = 64;
+static constexpr const char* TRACK_NAME = "CriticalWorkload";
+
+} // namespace android::WorkloadTracer
\ No newline at end of file
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
index dc5716b..9a7e97f 100644
--- a/services/surfaceflinger/common/include/common/trace.h
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -65,6 +65,8 @@
#define SFTRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(name)
// SFTRACE_CALL is an SFTRACE_NAME that uses the current function name.
#define SFTRACE_CALL() SFTRACE_NAME(__FUNCTION__)
+#define SFTRACE_NAME_FOR_TRACK(trackName, name) \
+ ::android::ScopedTraceForTrack PASTE(___tracer, __LINE__)(trackName, name)
#define SFTRACE_FORMAT(fmt, ...) \
::android::ScopedTrace PASTE(___tracer, __LINE__)(fmt, ##__VA_ARGS__)
@@ -87,4 +89,21 @@
inline ~ScopedTrace() { SFTRACE_END(); }
};
+class ScopedTraceForTrack {
+public:
+ inline ScopedTraceForTrack(const char* trackName, const char* name)
+ : mCookie(getUniqueCookie()), mTrackName(trackName) {
+ SFTRACE_ASYNC_FOR_TRACK_BEGIN(mTrackName, name, mCookie);
+ }
+ inline ~ScopedTraceForTrack() { SFTRACE_ASYNC_FOR_TRACK_END(mTrackName, mCookie); }
+
+private:
+ static int32_t getUniqueCookie() {
+ static std::atomic<int32_t> sUniqueCookie = 1000;
+ return sUniqueCookie++;
+ }
+ int32_t mCookie;
+ const char* mTrackName;
+};
+
} // namespace android
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 6c8972f..4afcd00 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -77,7 +77,7 @@
}
}
-int main(int, char**) {
+int main() {
signal(SIGPIPE, SIG_IGN);
hardware::configureRpcThreadpool(1 /* maxThreads */,
@@ -91,9 +91,7 @@
// Set uclamp.min setting on all threads, maybe an overkill but we want
// to cover important threads like RenderEngine.
- if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
- ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno));
- }
+ SurfaceFlinger::setSchedAttr(true, __func__);
// The binder threadpool we start will inherit sched policy and priority
// of (this) creating thread. We want the binder thread pool to have
@@ -132,7 +130,8 @@
// Set the minimum policy of surfaceflinger node to be SCHED_FIFO.
// So any thread with policy/priority lower than {SCHED_FIFO, 1}, will run
// at least with SCHED_FIFO policy and priority 1.
- if (errorInPriorityModification == 0) {
+ if (errorInPriorityModification == 0 &&
+ !FlagManager::getInstance().disable_sched_fifo_sf_binder()) {
flinger->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
}
@@ -150,7 +149,8 @@
// publish gui::ISurfaceComposer, the new AIDL interface
sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
- if (FlagManager::getInstance().misc1()) {
+ if (FlagManager::getInstance().misc1() &&
+ !FlagManager::getInstance().disable_sched_fifo_composer()) {
composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
}
sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
@@ -158,14 +158,8 @@
startDisplayService(); // dependency on SF getting registered above
- if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
- ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno));
- }
-
- // run surface flinger in this thread
+ SurfaceFlinger::setSchedFifo(true, __func__);
flinger->run();
-
- return 0;
}
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index bdd826d..e8b75cf 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -190,6 +190,23 @@
} # graphite_renderengine_preview_rollout
flag {
+ name: "hdcp_negotiation"
+ namespace: "core_graphics"
+ description: "detect secure layers to start HDCP negotiation"
+ bug: "375340594"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # hdcp_negotiation
+
+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"
@@ -209,6 +226,13 @@
} # local_tonemap_screenshots
flag {
+ name: "monitor_buffer_fences"
+ namespace: "core_graphics"
+ description: "Monitors fences for each buffer"
+ bug: "360932099"
+} # monitor_buffer_fences
+
+flag {
name: "no_vsyncs_on_screen_off"
namespace: "core_graphics"
description: "Stop vsync / Choreographer callbacks to apps when the screen is off"
@@ -217,6 +241,17 @@
} # no_vsyncs_on_screen_off
flag {
+ name: "reject_dupe_layerstacks"
+ namespace: "window_surfaces"
+ description: "Reject duplicate layerstacks for displays"
+ bug: "370358572"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # reject_dupe_layerstacks
+
+flag {
name: "single_hop_screenshot"
namespace: "window_surfaces"
description: "Only access SF main thread once during a screenshot"
@@ -247,6 +282,13 @@
} # stable_edid_ids
flag {
+ name: "synced_resolution_switch"
+ namespace: "core_graphics"
+ description: "Synchronize resolution modeset with framebuffer resizing"
+ bug: "355427258"
+} # synced_resolution_switch
+
+flag {
name: "true_hdr_screenshots"
namespace: "core_graphics"
description: "Enables screenshotting display content in HDR, sans tone mapping"
@@ -296,6 +338,16 @@
} # vrr_bugfix_dropped_frame
flag {
+ name: "vsync_predictor_recovery"
+ namespace: "core_graphics"
+ description: "Recover the vsync predictor from bad vsync model"
+ bug: "385059265"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # vsync_predictor_recovery
+
+flag {
name: "window_blur_kawase2"
namespace: "core_graphics"
description: "Flag for using Kawase2 algorithm for window blur"
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 0ad5ac9..bfafb65 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -483,6 +483,16 @@
prop_name: "ro.surface_flinger.min_acquired_buffers"
}
+# Defines the maximum acquired buffers SurfaceFlinger will suggest via
+# ISurfaceComposer.getMaxAcquiredBufferCount().
+prop {
+ api_name: "max_acquired_buffers"
+ type: Long
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_acquired_buffers"
+}
+
# When enabled, SurfaceFlinger will attempt to clear the per-layer HAL buffer cache slots for
# buffers when they are evicted from the app cache by using additional setLayerBuffer commands.
# Ideally, this behavior would always be enabled to reduce graphics memory consumption. However,
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 0017300..e2ac233 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -82,6 +82,11 @@
prop_name: "ro.surface_flinger.ignore_hdr_camera_layers"
}
prop {
+ api_name: "max_acquired_buffers"
+ type: Long
+ prop_name: "ro.surface_flinger.max_acquired_buffers"
+ }
+ prop {
api_name: "max_frame_buffer_acquired_buffers"
type: Long
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 4d5c0fd..37f3aa7 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -52,7 +52,7 @@
"LayerTypeTransaction_test.cpp",
"LayerUpdate_test.cpp",
"MirrorLayer_test.cpp",
- "MultiDisplayLayerBounds_test.cpp",
+ "MultiDisplay_test.cpp",
"RefreshRateOverlay_test.cpp",
"RelativeZ_test.cpp",
"ReleaseBufferCallback_test.cpp",
@@ -63,7 +63,10 @@
"VirtualDisplay_test.cpp",
"WindowInfosListener_test.cpp",
],
- data: ["SurfaceFlinger_test.filter"],
+ data: [
+ "SurfaceFlinger_test.filter",
+ "testdata/*",
+ ],
static_libs: [
"android.hardware.graphics.composer@2.1",
"libsurfaceflinger_common",
@@ -76,6 +79,7 @@
"libcutils",
"libEGL",
"libGLESv2",
+ "libjnigraphics",
"libgui",
"liblog",
"libnativewindow",
@@ -83,6 +87,7 @@
"libui",
"libutils",
"server_configurable_flags",
+ "libc++",
],
header_libs: [
"libnativewindow_headers",
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 000628f..b199ddb 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -14,14 +14,26 @@
limitations under the License.
-->
<configuration description="Config for SurfaceFlinger_test">
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="throw-if-cmd-fail" value="true" />
+ <option name="run-command" value="mkdir -p /data/local/tmp/SurfaceFlinger_test_screenshots" />
+ <option name="teardown-command" value="rm -fr /data/local/tmp/SurfaceFlinger_test_screenshots"/>
+ </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="SurfaceFlinger_test" />
</test>
-</configuration>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name = "pull-pattern-keys" value = ".*png" />
+ <option name = "directory-keys" value = "/data/local/tmp/SurfaceFlinger_test_screenshots" />
+ </metrics_collector>
+</configuration>
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
index 46b98f9..192602d 100644
--- a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
+++ b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
@@ -52,6 +52,8 @@
};
TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
+ // Last strong pointer is removed, the layer is destroyed and is removed
+ // from compostion.
fgLayer = nullptr;
{
SCOPED_TRACE("after setting null");
@@ -61,7 +63,9 @@
}
TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
- auto transaction = Transaction().show(fgLayer);
+ Transaction transaction;
+ transaction.show(fgLayer);
+ // |transaction| retains a strong pointer, so layer is retained.
fgLayer = nullptr;
{
SCOPED_TRACE("after setting null");
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 18bd3b9..94cb878 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -42,14 +42,22 @@
using TCLHash = SurfaceComposerClient::TCLHash;
using android::hardware::graphics::common::V1_1::BufferUsage;
-class TransactionHelper : public Transaction {
+class TransactionHelper : public Transaction, public Parcelable {
public:
+ TransactionHelper() : Transaction() {}
size_t getNumListeners() { return mListenerCallbacks.size(); }
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
getListenerCallbacks() {
return mListenerCallbacks;
}
+ status_t writeToParcel(Parcel* parcel) const override {
+ return Transaction::writeToParcel(parcel);
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ return Transaction::readFromParcel(parcel);
+ }
};
class IPCTestUtils {
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index b4496d3..5a82914 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -147,7 +147,7 @@
<< "Timeout waiting for vsync event";
DisplayEventReceiver::Event event;
while (mDisplayEventReceiver.getEvents(&event, 1) > 0) {
- if (event.header.type != DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ if (event.header.type != DisplayEventType::DISPLAY_EVENT_VSYNC) {
continue;
}
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index f247c9f..ada9862 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -585,6 +585,170 @@
}
}
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetClientDrawnCornerRadius) {
+ sp<SurfaceControl> layer;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+ Transaction()
+ .setClientDrawnCornerRadius(layer, cornerRadius)
+ .setCornerRadius(layer, cornerRadius)
+ .setCrop(layer, Rect(size, size))
+ .apply();
+
+ {
+ const uint8_t bottom = size - 1;
+ const uint8_t right = size - 1;
+ auto shot = getScreenCapture();
+ // Solid corners
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::RED);
+ shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::RED);
+ shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::RED);
+ shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::RED);
+ // Solid center
+ shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+ size / 2 + testArea / 2, size / 2 + testArea / 2),
+ Color::RED);
+ }
+}
+
+// Test if ParentCornerRadiusTakesPrecedence if the parent's client drawn corner radius crop
+// is fully contained by the child corner radius crop.
+TEST_P(LayerTypeAndRenderTypeTransactionTest, ParentCornerRadiusPrecedenceClientDrawnCornerRadius) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint32_t size = 64;
+ const uint32_t parentSize = size * 3;
+ const Rect parentCrop(size, size, size, size);
+ const uint32_t testLength = 4;
+ const float cornerRadius = 20.0f;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ Transaction()
+ .setCornerRadius(parent, cornerRadius)
+ .setCrop(parent, parentCrop)
+ .setClientDrawnCornerRadius(parent, cornerRadius)
+ .reparent(child, parent)
+ .setPosition(child, size, size)
+ .apply(true);
+
+ {
+ const uint32_t top = size;
+ const uint32_t left = size;
+ const uint32_t bottom = size * 2;
+ const uint32_t right = size * 2;
+ auto shot = getScreenCapture();
+ // Corners are RED because parent's client drawn corner radius is actually 0
+ // and the child is fully within the parent's crop
+ // TL
+ shot->expectColor(Rect(left, top, testLength, testLength), Color::RED);
+ // TR
+ shot->expectColor(Rect(right - testLength, top, testLength, testLength), Color::RED);
+ // BL
+ shot->expectColor(Rect(left, bottom - testLength, testLength, testLength), Color::RED);
+ // BR
+ shot->expectColor(Rect(right - testLength, bottom - testLength, testLength, testLength),
+ Color::RED);
+ // Solid center
+ shot->expectColor(Rect(parentSize / 2 - testLength, parentSize / 2 - testLength, testLength,
+ testLength),
+ Color::GREEN);
+ }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBorderSettings) {
+ sp<SurfaceControl> parent;
+ sp<SurfaceControl> child;
+ const uint32_t size = 64;
+ const uint32_t parentSize = size * 3;
+ ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, parentSize, parentSize));
+ ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+ gui::BorderSettings outline;
+ outline.strokeWidth = 3;
+ outline.color = 0xff0000ff;
+ Transaction()
+ .setCrop(parent, Rect(0, 0, parentSize, parentSize))
+ .reparent(child, parent)
+ .setPosition(child, size, size)
+ .setCornerRadius(child, 20.0f)
+ .setBorderSettings(child, outline)
+ .apply(true);
+
+ {
+ auto shot = getScreenCapture();
+
+ shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
+ "testdata/SetBorderSettings_Opaque.png");
+ }
+
+ {
+ Transaction().setAlpha(child, 0.5f).apply(true);
+ auto shot = getScreenCapture();
+
+ shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
+ "testdata/SetBorderSettings_HalfAlpha.png");
+ }
+
+ {
+ Transaction().setAlpha(child, 0.0f).apply(true);
+
+ auto shot = getScreenCapture();
+
+ shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
+ "testdata/SetBorderSettings_ZeroAlpha.png");
+ }
+
+ {
+ Transaction()
+ .setAlpha(child, 1.0f)
+ .setCrop(parent, Rect(0, 0, parentSize / 2, parentSize))
+ .apply(true);
+
+ auto shot = getScreenCapture();
+
+ shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
+ "testdata/SetBorderSettings_Cropped.png");
+ }
+
+ {
+ outline.color = 0xff0000ff;
+ outline.strokeWidth = 1;
+ Transaction()
+ .setCrop(parent, Rect(0, 0, parentSize, parentSize))
+ .setBorderSettings(child, outline)
+ .apply(true);
+
+ auto shot = getScreenCapture();
+
+ shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
+ "testdata/SetBorderSettings_StrokeWidth1.png");
+ }
+
+ {
+ outline.color = 0x440000ff;
+ outline.strokeWidth = 3;
+ Transaction()
+ .setCrop(parent, Rect(0, 0, parentSize, parentSize))
+ .setBorderSettings(child, outline)
+ .apply(true);
+
+ auto shot = getScreenCapture();
+
+ shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
+ "testdata/"
+ "SetBorderSettings_StrokeColorWithAlpha.png");
+ }
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) {
if (!deviceSupportsBlurs()) GTEST_SKIP();
if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplay_test.cpp
similarity index 76%
rename from services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
rename to services/surfaceflinger/tests/MultiDisplay_test.cpp
index 65add63..cf6d328 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplay_test.cpp
@@ -33,7 +33,7 @@
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
-class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+class MultiDisplayTest : public LayerTransactionTest {
protected:
virtual void SetUp() {
LayerTransactionTest::SetUp();
@@ -51,7 +51,7 @@
mConsumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
mMainDisplayMode.resolution.getHeight());
- class StubConsumerListener : public BnConsumerListener {
+ class StubConsumerListener : public IConsumerListener {
virtual void onFrameAvailable(const BufferItem&) override {}
virtual void onBuffersReleased() override {}
virtual void onSidebandStreamChanged() override {}
@@ -105,7 +105,7 @@
Color mExpectedColor = {63, 63, 195, 255};
};
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+TEST_F(MultiDisplayTest, RenderLayerInVirtualDisplay) {
constexpr ui::LayerStack kLayerStack{1u};
createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack);
createColorLayer(kLayerStack);
@@ -124,7 +124,7 @@
sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 255});
}
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+TEST_F(MultiDisplayTest, RenderLayerInMirroredVirtualDisplay) {
// Create a display and set its layer stack to the main display's layer stack so
// the contents of the main display are mirrored on to the virtual display.
@@ -150,7 +150,7 @@
sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
}
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) {
+TEST_F(MultiDisplayTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) {
// Create a display and use a unique layerstack ID for mirrorDisplay() so
// the contents of the main display are mirrored on to the virtual display.
@@ -181,6 +181,51 @@
sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+TEST_F(MultiDisplayTest, rejectDuplicateLayerStacks) {
+ if (!FlagManager::getInstance().reject_dupe_layerstacks()) return;
+
+ // Setup
+ sp<CpuConsumer> cpuConsumer1 = sp<CpuConsumer>::make(static_cast<size_t>(1));
+ cpuConsumer1->setName(String8("consumer 1"));
+ cpuConsumer1->setDefaultBufferSize(100, 100);
+ sp<IGraphicBufferProducer> cpuProducer1 =
+ cpuConsumer1->getSurface()->getIGraphicBufferProducer();
+ CpuConsumer::LockedBuffer buffer1;
+
+ sp<CpuConsumer> cpuConsumer2 = sp<CpuConsumer>::make(static_cast<size_t>(1));
+ cpuConsumer2->setName(String8("consumer 2"));
+ cpuConsumer2->setDefaultBufferSize(100, 100);
+ sp<IGraphicBufferProducer> cpuProducer2 =
+ cpuConsumer2->getSurface()->getIGraphicBufferProducer();
+ CpuConsumer::LockedBuffer buffer2;
+
+ SurfaceComposerClient::Transaction t;
+ constexpr ui::LayerStack layerStack = {123u};
+ createColorLayer(layerStack);
+
+ static const std::string kDisplayName1("VirtualDisplay1 - rejectDuplicateLayerStacks");
+ sp<IBinder> virtualDisplay1 =
+ SurfaceComposerClient::createVirtualDisplay(kDisplayName1, false /*isSecure*/);
+
+ t.setDisplaySurface(virtualDisplay1, cpuProducer1);
+ t.setDisplayLayerStack(virtualDisplay1, layerStack);
+ t.apply(true);
+
+ static const std::string kDisplayName2("VirtualDisplay2 - rejectDuplicateLayerStacks");
+ sp<IBinder> virtualDisplay2 =
+ SurfaceComposerClient::createVirtualDisplay(kDisplayName2, false /*isSecure*/);
+
+ t.setDisplaySurface(virtualDisplay2, cpuProducer2);
+ t.setDisplayLayerStack(virtualDisplay2, layerStack);
+ t.apply(true);
+
+ // The second consumer will not be able to lock a buffer because
+ // the duplicate layer stack should be rejected.
+ ASSERT_EQ(NO_ERROR, cpuConsumer1->lockNextBuffer(&buffer1));
+ ASSERT_NE(NO_ERROR, cpuConsumer2->lockNextBuffer(&buffer2));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index c95c875..c91f1ea 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -48,7 +48,11 @@
ui::DisplayMode displayMode;
SurfaceComposerClient::getActiveDisplayMode(displayToken, &displayMode);
- const ui::Size& resolution = displayMode.resolution;
+ ui::Size resolution = displayMode.resolution;
+ if (displayState.orientation == ui::Rotation::Rotation90 ||
+ displayState.orientation == ui::Rotation::Rotation270) {
+ std::swap(resolution.width, resolution.height);
+ }
sp<IBinder> vDisplay;
@@ -93,8 +97,8 @@
#else
t.setDisplaySurface(vDisplay, producer);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
- t.setDisplayProjection(vDisplay, displayState.orientation,
- Rect(displayState.layerStackSpaceRect), Rect(resolution));
+ t.setDisplayProjection(vDisplay, ui::Rotation::Rotation0, Rect(resolution),
+ Rect(resolution));
t.setDisplayLayerStack(vDisplay, layerStack);
t.setLayerStack(mirrorSc, layerStack);
t.apply();
diff --git a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
index 7641a45..fec6242 100644
--- a/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
+++ b/services/surfaceflinger/tests/benchmarks/LayerLifecycleManager_benchmarks.cpp
@@ -50,7 +50,7 @@
layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
lifecycleManager.commitChanges();
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
auto& transactionState = transactions.back().states.front();
@@ -74,7 +74,7 @@
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(LayerLifecycleManagerHelper::rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
auto& transactionState = transactions.back().states.front();
@@ -90,5 +90,46 @@
}
BENCHMARK(updateClientStatesNoChanges);
+static void propagateManyHiddenChildren(benchmark::State& state) {
+ LayerLifecycleManager lifecycleManager;
+ LayerLifecycleManagerHelper helper(lifecycleManager);
+
+ helper.createRootLayer(0);
+ for (uint32_t i = 1; i < 50; ++i) {
+ helper.createLayer(i, i - 1);
+ }
+
+ helper.hideLayer(0);
+
+ LayerHierarchyBuilder hierarchyBuilder;
+ DisplayInfo info;
+ info.info.logicalHeight = 100;
+ info.info.logicalWidth = 100;
+ DisplayInfos displayInfos;
+ displayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ ShadowSettings globalShadowSettings;
+
+ LayerSnapshotBuilder snapshotBuilder;
+
+ int i = 1;
+ for (auto _ : state) {
+ i++;
+ helper.setAlpha(0, (1 + (i % 255)) / 255.0f);
+
+ if (lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ hierarchyBuilder.update(lifecycleManager);
+ }
+ LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = lifecycleManager,
+ .displays = displayInfos,
+ .globalShadowSettings = globalShadowSettings,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ snapshotBuilder.update(args);
+ lifecycleManager.commitChanges();
+ }
+}
+BENCHMARK(propagateManyHiddenChildren);
+
} // namespace
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index 9794620..1bee27b 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -66,8 +66,8 @@
/*mirror=*/UNASSIGNED_LAYER_ID));
}
- static std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
- std::vector<TransactionState> transactions;
+ static std::vector<QueuedTransactionState> setZTransaction(uint32_t id, int32_t z) {
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -109,8 +109,9 @@
mLifecycleManager.addLayers(std::move(layers));
}
- std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> reparentLayerTransaction(uint32_t id,
+ uint32_t newParentId) {
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().parentId = newParentId;
@@ -124,8 +125,9 @@
mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
}
- std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> relativeLayerTransaction(uint32_t id,
+ uint32_t relativeParentId) {
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().relativeParentId = relativeParentId;
@@ -139,7 +141,7 @@
}
void removeRelativeZ(uint32_t id) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
@@ -148,7 +150,7 @@
}
void setPosition(uint32_t id, float x, float y) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
@@ -167,7 +169,7 @@
}
void updateBackgroundColor(uint32_t id, half alpha) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
@@ -183,7 +185,7 @@
}
void setCrop(uint32_t id, const FloatRect& crop) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -196,7 +198,7 @@
void setCrop(uint32_t id, const Rect& crop) { setCrop(id, crop.toFloatRect()); }
void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -208,7 +210,7 @@
}
void setAlpha(uint32_t id, float alpha) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -219,7 +221,7 @@
}
void setAutoRefresh(uint32_t id, bool autoRefresh) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -236,7 +238,7 @@
void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eColorChanged;
@@ -246,7 +248,7 @@
}
void setLayerStack(uint32_t id, int32_t layerStack) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -257,30 +259,28 @@
}
void setTouchableRegion(uint32_t id, Region region) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->touchableRegion = region;
inputInfo->token = sp<BBinder>::make();
mLifecycleManager.applyTransactions(transactions);
}
void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
if (!inputInfo->token) {
inputInfo->token = sp<BBinder>::make();
}
@@ -291,15 +291,14 @@
void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
bool replaceTouchableRegionWithCrop) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.windowInfoHandle =
- sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->touchableRegion = region;
inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
transactions.back().states.front().touchCropId = touchCropId;
@@ -309,7 +308,7 @@
}
void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -320,7 +319,7 @@
}
void setFrameRateSelectionPriority(uint32_t id, int32_t priority) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -332,7 +331,7 @@
void setFrameRate(uint32_t id, float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -345,7 +344,7 @@
}
void setFrameRate(uint32_t id, Layer::FrameRate framerate) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -358,7 +357,7 @@
}
void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -369,7 +368,7 @@
}
void setFrameRateSelectionStrategy(uint32_t id, int8_t strategy) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -381,7 +380,7 @@
}
void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -394,7 +393,7 @@
}
void setRoundedCorners(uint32_t id, float radius) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -405,7 +404,7 @@
}
void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -438,7 +437,7 @@
}
void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -449,18 +448,17 @@
}
void setDamageRegion(uint32_t id, const Region& damageRegion) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eSurfaceDamageRegionChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.surfaceDamageRegion = damageRegion;
+ transactions.back().states.front().state.updateSurfaceDamageRegion(damageRegion);
mLifecycleManager.applyTransactions(transactions);
}
void setDataspace(uint32_t id, ui::Dataspace dataspace) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -473,7 +471,7 @@
void setMatrix(uint32_t id, float dsdx, float dtdx, float dtdy, float dsdy) {
layer_state_t::matrix22_t matrix{dsdx, dtdx, dtdy, dsdy};
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -483,8 +481,20 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setClientDrawnCornerRadius(uint32_t id, float clientDrawnCornerRadius) {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eClientDrawnCornerRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.clientDrawnCornerRadius = clientDrawnCornerRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setShadowRadius(uint32_t id, float shadowRadius) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -494,8 +504,19 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setBorderSettings(uint32_t id, gui::BorderSettings settings) {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eBorderSettingsChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.borderSettings = settings;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -506,7 +527,7 @@
}
void setDropInputMode(uint32_t id, gui::DropInputMode dropInputMode) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -517,7 +538,7 @@
}
void setGameMode(uint32_t id, gui::GameMode gameMode) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
@@ -529,7 +550,7 @@
}
void setEdgeExtensionEffect(uint32_t id, int edge) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
diff --git a/services/surfaceflinger/tests/end2end/.clang-format b/services/surfaceflinger/tests/end2end/.clang-format
new file mode 120000
index 0000000..5e8e20b
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clang-format
@@ -0,0 +1 @@
+../../../../../../build/soong/scripts/system-clang-format
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/end2end/.clang-tidy b/services/surfaceflinger/tests/end2end/.clang-tidy
new file mode 100644
index 0000000..29f3b47
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clang-tidy
@@ -0,0 +1,380 @@
+# 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.
+
+FormatStyle: file
+InheritParentConfig: true
+
+# Please add checks explicitly rather than using wildcards like "modernize-*".
+# These check names are current as of LLVM 20.0.0, as reported by "clang-tidy --list-checks --checks=*"
+# For more information on each check, see https://clang.llvm.org/extra/clang-tidy/checks/list.html.
+Checks:
+ # from android-*
+ - android-cloexec-accept
+ - android-cloexec-accept4
+ - android-cloexec-creat
+ - android-cloexec-dup
+ - android-cloexec-epoll-create
+ - android-cloexec-epoll-create1
+ - android-cloexec-fopen
+ - android-cloexec-inotify-init
+ - android-cloexec-inotify-init1
+ - android-cloexec-memfd-create
+ - android-cloexec-open
+ - android-cloexec-pipe
+ - android-cloexec-pipe2
+ - android-cloexec-socket
+ - android-comparison-in-temp-failure-retry
+
+ # from bugprone-*
+ - bugprone-argument-comment
+ - bugprone-assert-side-effect
+ - bugprone-assignment-in-if-condition
+ - bugprone-bad-signal-to-kill-thread
+ - bugprone-bool-pointer-implicit-conversion
+ - bugprone-branch-clone
+ - bugprone-casting-through-void
+ - bugprone-chained-comparison
+ - bugprone-compare-pointer-to-member-virtual-function
+ - bugprone-copy-constructor-init
+ - bugprone-crtp-constructor-accessibility
+ - bugprone-dangling-handle
+ - bugprone-dynamic-static-initializers
+ - bugprone-easily-swappable-parameters
+ - bugprone-empty-catch
+ - bugprone-exception-escape
+ - bugprone-fold-init-type
+ - bugprone-forward-declaration-namespace
+ - bugprone-forwarding-reference-overload
+ - bugprone-implicit-widening-of-multiplication-result
+ - bugprone-inaccurate-erase
+ - bugprone-inc-dec-in-conditions
+ - bugprone-incorrect-enable-if
+ - bugprone-incorrect-roundings
+ - bugprone-infinite-loop
+ - bugprone-integer-division
+ - bugprone-lambda-function-name
+ - bugprone-macro-parentheses
+ - bugprone-macro-repeated-side-effects
+ - bugprone-misplaced-operator-in-strlen-in-alloc
+ - bugprone-misplaced-pointer-arithmetic-in-alloc
+ - bugprone-misplaced-widening-cast
+ - bugprone-move-forwarding-reference
+ - bugprone-multi-level-implicit-pointer-conversion
+ - bugprone-multiple-new-in-one-expression
+ - bugprone-multiple-statement-macro
+ - bugprone-narrowing-conversions
+ - bugprone-no-escape
+ - bugprone-non-zero-enum-to-bool-conversion
+ - bugprone-not-null-terminated-result
+ - bugprone-optional-value-conversion
+ - bugprone-parent-virtual-call
+ - bugprone-pointer-arithmetic-on-polymorphic-object
+ - bugprone-posix-return
+ - bugprone-redundant-branch-condition
+ - bugprone-reserved-identifier
+ - bugprone-return-const-ref-from-parameter
+ - bugprone-shared-ptr-array-mismatch
+ - bugprone-signal-handler
+ - bugprone-signed-char-misuse
+ - bugprone-sizeof-container
+ - bugprone-sizeof-expression
+ - bugprone-spuriously-wake-up-functions
+ - bugprone-standalone-empty
+ - bugprone-string-constructor
+ - bugprone-string-integer-assignment
+ - bugprone-string-literal-with-embedded-nul
+ - bugprone-stringview-nullptr
+ - bugprone-suspicious-enum-usage
+ - bugprone-suspicious-include
+ - bugprone-suspicious-memory-comparison
+ - bugprone-suspicious-memset-usage
+ - bugprone-suspicious-missing-comma
+ - bugprone-suspicious-realloc-usage
+ - bugprone-suspicious-semicolon
+ - bugprone-suspicious-string-compare
+ - bugprone-suspicious-stringview-data-usage
+ - bugprone-swapped-arguments
+ - bugprone-switch-missing-default-case
+ - bugprone-terminating-continue
+ - bugprone-throw-keyword-missing
+ - bugprone-too-small-loop-variable
+ - bugprone-unchecked-optional-access
+ - bugprone-undefined-memory-manipulation
+ - bugprone-undelegated-constructor
+ - bugprone-unhandled-exception-at-new
+ - bugprone-unhandled-self-assignment
+ - bugprone-unique-ptr-array-mismatch
+ - bugprone-unsafe-functions
+ - bugprone-unused-local-non-trivial-variable
+ - bugprone-unused-raii
+ - bugprone-unused-return-value
+ - bugprone-use-after-move
+ - bugprone-virtual-near-miss
+
+ # from cert-*
+ - cert-con36-c
+ - cert-con54-cpp
+ - cert-ctr56-cpp
+ - cert-dcl03-c
+ - cert-dcl16-c
+ - cert-dcl37-c
+ - cert-dcl50-cpp
+ - cert-dcl51-cpp
+ - cert-dcl54-cpp
+ - cert-dcl58-cpp
+ - cert-dcl59-cpp
+ - cert-env33-c
+ - cert-err09-cpp
+ - cert-err33-c
+ - cert-err34-c
+ - cert-err52-cpp
+ - cert-err58-cpp
+ - cert-err60-cpp
+ - cert-err61-cpp
+ - cert-exp42-c
+ - cert-fio38-c
+ - cert-flp30-c
+ - cert-flp37-c
+ - cert-int09-c
+ - cert-mem57-cpp
+ - cert-msc24-c
+ - cert-msc30-c
+ - cert-msc32-c
+ - cert-msc33-c
+ - cert-msc50-cpp
+ - cert-msc51-cpp
+ - cert-msc54-cpp
+ - cert-oop11-cpp
+ - cert-oop54-cpp
+ - cert-oop57-cpp
+ - cert-oop58-cpp
+ - cert-pos44-c
+ - cert-pos47-c
+ - cert-sig30-c
+ - cert-str34-c
+
+ # from concurrency-*
+ - concurrency-mt-unsafe
+ - concurrency-thread-canceltype-asynchronous
+
+ # from cppcoreguidelines-*
+ - cppcoreguidelines-avoid-c-arrays
+ - cppcoreguidelines-avoid-capturing-lambda-coroutines
+ - cppcoreguidelines-avoid-const-or-ref-data-members
+ - cppcoreguidelines-avoid-do-while
+ - cppcoreguidelines-avoid-goto
+ - cppcoreguidelines-avoid-magic-numbers
+ - cppcoreguidelines-avoid-non-const-global-variables
+ - cppcoreguidelines-avoid-reference-coroutine-parameters
+ - cppcoreguidelines-c-copy-assignment-signature
+ - cppcoreguidelines-explicit-virtual-functions
+ - cppcoreguidelines-init-variables
+ - cppcoreguidelines-interfaces-global-init
+ - cppcoreguidelines-macro-to-enum
+ - cppcoreguidelines-macro-usage
+ - cppcoreguidelines-misleading-capture-default-by-value
+ - cppcoreguidelines-missing-std-forward
+ - cppcoreguidelines-narrowing-conversions
+ - cppcoreguidelines-no-malloc
+ - cppcoreguidelines-no-suspend-with-lock
+ - cppcoreguidelines-noexcept-destructor
+ - cppcoreguidelines-noexcept-move-operations
+ - cppcoreguidelines-noexcept-swap
+ - cppcoreguidelines-non-private-member-variables-in-classes
+ - cppcoreguidelines-owning-memory
+ - cppcoreguidelines-prefer-member-initializer
+ - cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ - cppcoreguidelines-pro-bounds-constant-array-index
+ - cppcoreguidelines-pro-bounds-pointer-arithmetic
+ - cppcoreguidelines-pro-type-const-cast
+ - cppcoreguidelines-pro-type-cstyle-cast
+ - cppcoreguidelines-pro-type-member-init
+ - cppcoreguidelines-pro-type-reinterpret-cast
+ - cppcoreguidelines-pro-type-static-cast-downcast
+ - cppcoreguidelines-pro-type-union-access
+ - cppcoreguidelines-pro-type-vararg
+ - cppcoreguidelines-rvalue-reference-param-not-moved
+ - cppcoreguidelines-slicing
+ - cppcoreguidelines-special-member-functions
+ - cppcoreguidelines-use-default-member-init
+ - cppcoreguidelines-virtual-class-destructor
+
+ # from google-*
+ - google-build-explicit-make-pair
+ - google-build-namespaces
+ - google-build-using-namespace
+ - google-default-arguments
+ - google-explicit-constructor
+ - google-global-names-in-headers
+ - google-objc-avoid-nsobject-new
+ - google-objc-avoid-throwing-exception
+ - google-objc-function-naming
+ - google-objc-global-variable-declaration
+ - google-readability-avoid-underscore-in-googletest-name
+ - google-readability-braces-around-statements
+ - google-readability-casting
+ - google-readability-function-size
+ - google-readability-namespace-comments
+ - google-readability-todo
+ - google-runtime-int
+ - google-runtime-operator
+ - google-upgrade-googletest-case
+
+ # from misc-*
+ - misc-confusable-identifiers
+ - misc-const-correctness
+ - misc-coroutine-hostile-raii
+ - misc-definitions-in-headers
+ - misc-header-include-cycle
+ - misc-include-cleaner
+ - misc-misleading-bidirectional
+ - misc-misleading-identifier
+ - misc-misplaced-const
+ - misc-new-delete-overloads
+ - misc-no-recursion
+ - misc-non-copyable-objects
+ - misc-non-private-member-variables-in-classes
+ - misc-redundant-expression
+ - misc-static-assert
+ - misc-throw-by-value-catch-by-reference
+ - misc-unconventional-assign-operator
+ - misc-uniqueptr-reset-release
+ - misc-unused-alias-decls
+ - misc-unused-parameters
+ - misc-unused-using-decls
+ - misc-use-anonymous-namespace
+ - misc-use-internal-linkage
+
+ # from modernize-*
+ - modernize-avoid-bind
+ - modernize-avoid-c-arrays
+ - modernize-concat-nested-namespaces
+ - modernize-deprecated-headers
+ - modernize-deprecated-ios-base-aliases
+ - modernize-loop-convert
+ - modernize-macro-to-enum
+ - modernize-make-shared
+ - modernize-make-unique
+ - modernize-min-max-use-initializer-list
+ - modernize-pass-by-value
+ - modernize-raw-string-literal
+ - modernize-redundant-void-arg
+ - modernize-replace-auto-ptr
+ - modernize-replace-disallow-copy-and-assign-macro
+ - modernize-replace-random-shuffle
+ - modernize-return-braced-init-list
+ - modernize-shrink-to-fit
+ - modernize-type-traits
+ - modernize-unary-static-assert
+ - modernize-use-auto
+ - modernize-use-bool-literals
+ - modernize-use-constraints
+ - modernize-use-default-member-init
+ - modernize-use-designated-initializers
+ - modernize-use-emplace
+ - modernize-use-equals-default
+ - modernize-use-equals-delete
+ - modernize-use-nodiscard
+ - modernize-use-noexcept
+ - modernize-use-nullptr
+ - modernize-use-override
+ - modernize-use-ranges
+ - modernize-use-starts-ends-with
+ - modernize-use-std-format
+ - modernize-use-std-numbers
+ - modernize-use-std-print
+ - modernize-use-trailing-return-type
+ - modernize-use-transparent-functors
+ - modernize-use-uncaught-exceptions
+ - modernize-use-using
+
+ # from performance-*
+ - performance-avoid-endl
+ - performance-enum-size
+ - performance-faster-string-find
+ - performance-for-range-copy
+ - performance-implicit-conversion-in-loop
+ - performance-inefficient-algorithm
+ - performance-inefficient-string-concatenation
+ - performance-inefficient-vector-operation
+ - performance-move-const-arg
+ - performance-move-constructor-init
+ - performance-no-automatic-move
+ - performance-no-int-to-ptr
+ - performance-noexcept-destructor
+ - performance-noexcept-move-constructor
+ - performance-noexcept-swap
+ - performance-trivially-destructible
+ - performance-type-promotion-in-math-fn
+ - performance-unnecessary-copy-initialization
+ - performance-unnecessary-value-param
+
+ # from portability-*
+ - portability-restrict-system-includes
+ - portability-simd-intrinsics
+ - portability-std-allocator-const
+
+ # from readability-*
+ - readability-avoid-const-params-in-decls
+ - readability-avoid-nested-conditional-operator
+ - readability-avoid-return-with-void-value
+ - readability-avoid-unconditional-preprocessor-if
+ - readability-braces-around-statements
+ - readability-const-return-type
+ - readability-container-contains
+ - readability-container-data-pointer
+ - readability-container-size-empty
+ - readability-convert-member-functions-to-static
+ - readability-delete-null-pointer
+ - readability-duplicate-include
+ - readability-else-after-return
+ - readability-enum-initial-value
+ - readability-function-cognitive-complexity
+ - readability-function-size
+ - readability-identifier-length
+ - readability-identifier-naming
+ - readability-implicit-bool-conversion
+ - readability-inconsistent-declaration-parameter-name
+ - readability-isolate-declaration
+ - readability-magic-numbers
+ - readability-make-member-function-const
+ - readability-math-missing-parentheses
+ - readability-misleading-indentation
+ - readability-misplaced-array-index
+ - readability-named-parameter
+ - readability-non-const-parameter
+ - readability-operators-representation
+ - readability-qualified-auto
+ - readability-redundant-access-specifiers
+ - readability-redundant-casting
+ - readability-redundant-control-flow
+ - readability-redundant-declaration
+ - readability-redundant-function-ptr-dereference
+ - readability-redundant-inline-specifier
+ - readability-redundant-member-init
+ - readability-redundant-preprocessor
+ - readability-redundant-smartptr-get
+ - readability-redundant-string-cstr
+ - readability-redundant-string-init
+ - readability-reference-to-constructed-temporary
+ - readability-simplify-boolean-expr
+ - readability-simplify-subscript-expr
+ - readability-static-accessed-through-instance
+ - readability-static-definition-in-anonymous-namespace
+ - readability-string-compare
+ - readability-suspicious-call-argument
+ - readability-uniqueptr-delete-release
+ - readability-uppercase-literal-suffix
+ - readability-use-anyofallof
+ - readability-use-std-min-max
diff --git a/services/surfaceflinger/tests/end2end/.clangd b/services/surfaceflinger/tests/end2end/.clangd
new file mode 100644
index 0000000..d64d2f8
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clangd
@@ -0,0 +1,20 @@
+# 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.
+
+Diagnostics:
+ UnusedIncludes: Strict
+ MissingIncludes: Strict
+ ClangTidy:
+ FastCheckFilter: None
+ # See the .clang-tidy files for additional configuration
diff --git a/services/surfaceflinger/tests/end2end/Android.bp b/services/surfaceflinger/tests/end2end/Android.bp
new file mode 100644
index 0000000..8810330
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/Android.bp
@@ -0,0 +1,68 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "surfaceflinger_end2end_tests",
+ test_suites: ["device-tests"],
+ require_root: true,
+
+ cpp_std: "experimental",
+ cflags: [
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-DNODISCARD_EXPECTED",
+ "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
+ "-Wall",
+ "-Wconversion",
+ "-Werror",
+ "-Wextra",
+ "-Wformat",
+ "-Wno-non-virtual-dtor",
+ "-Wno-sign-compare",
+ "-Wno-sign-conversion",
+ "-Wshadow",
+ "-Wthread-safety",
+ "-Wunreachable-code",
+ "-Wunused",
+ ],
+ srcs: [
+ "main.cpp",
+ "test_framework/core/TestService.cpp",
+ "test_framework/fake_hwc3/Hwc3Composer.cpp",
+ "test_framework/fake_hwc3/Hwc3Controller.cpp",
+ "test_framework/surfaceflinger/SFController.cpp",
+ "tests/Placeholder_test.cpp",
+ ],
+ tidy: true,
+ tidy_flags: [
+ "--config=", // Use the .clang-tidy closest to each source file for the configuration
+ ],
+ tidy_checks_as_errors: [
+ "*",
+ ],
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+ local_include_dirs: ["."],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbinder_ndk",
+ "libcutils",
+ "libgui",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.graphics.common-V6-ndk",
+ "android.hardware.graphics.composer3-V3-ndk",
+ "libgtest",
+ ],
+}
diff --git a/services/surfaceflinger/tests/end2end/AndroidTest.xml b/services/surfaceflinger/tests/end2end/AndroidTest.xml
new file mode 100644
index 0000000..99cb7b3
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Configuration for surfaceflinger_end2end_tests">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Stop everything to run SurfaceFlinger in isolation, with relaxed SELinux permissions -->
+ <option name="teardown-command" value="stop" />
+ <option name="teardown-command" value="setprop debug.sf.nobootanimation 1" />
+
+ <!-- Restart everything with normal settings after the test finishes. -->
+ <option name="teardown-command" value="stop" />
+ <option name="teardown-command" value="setprop debug.sf.nobootanimation 0" />
+ <option name="teardown-command" value="setprop debug.sf.hwc_service_name default" />
+ <option name="teardown-command" value="start" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="surfaceflinger_end2end_tests->/data/local/tests/surfaceflinger_end2end_tests" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tests" />
+ <option name="module-name" value="surfaceflinger_end2end_tests" />
+ <option name="native-test-timeout" value="15m"/>
+ </test>
+</configuration>
diff --git a/services/surfaceflinger/tests/end2end/OWNERS b/services/surfaceflinger/tests/end2end/OWNERS
new file mode 100644
index 0000000..aa5b595
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/OWNERS
@@ -0,0 +1,7 @@
+lpique@google.com # primary POC
+bwidawsk@google.com
+ddavenport@google.com
+markyacoub@google.com
+sukoo@google.com
+
+include platform/frameworks/native:/services/surfaceflinger/OWNERS
diff --git a/services/surfaceflinger/tests/end2end/README.md b/services/surfaceflinger/tests/end2end/README.md
new file mode 100644
index 0000000..2f58cec
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/README.md
@@ -0,0 +1,75 @@
+# `surfaceflinger_end2end_tests`
+
+Tests to cover end to end testing of SurfaceFlinger.
+
+In particular the test framework allows you to simulate various display
+configurations, so the test can confirm displays are handled correctly.
+
+## Quick Useful info
+
+### Running the tests
+
+At present the tests should run on any target, though the typical target would
+be a [Cuttlefish](https://source.android.com/docs/devices/cuttlefish) VM
+target such as `aosp_cf_x86_64_phone`.
+
+At some future time the test may be rewritten to require
+[`vkms`](https://dri.freedesktop.org/docs/drm/gpu/vkms.html) and
+[`drm_hwcomposer`](https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer)
+
+```
+atest surfaceflinger_end2end_tests
+```
+
+You can also run the google test binary directly. However you will also need
+to run a few other set-up and tear-down commands that are part of the
+AndroidTest.xml configuration, so that SurfaceFlinger can be used run isolation
+from the rest of the system.
+
+```
+# Set-up
+adb root
+adb shell stop
+adb shell setenforce 0
+adb shell setprop debug.sf.nobootanimation 1
+
+# Sync and run the test
+adb sync data
+adb shell data/nativetest64/surfaceflinger_end2end_tests/surfaceflinger_end2end_tests
+
+# Tear-down
+adb shell stop
+adb shell setenforce 1
+adb shell setprop debug.sf.nobootanimation 0
+adb shell setprop debug.sf.hwc_service_name default
+```
+
+### Manual clang-tidy checks via Soong
+
+At present Android does not run the clang-tidy checks as part of its
+presubmit checks.
+
+You can run them through the build system by using phony target that are
+automatically created for each source subdirectory.
+
+For the code under `frameworks/native/services/surfaceflinger/tests/end2end`,
+you would build:
+
+```
+m tidy-frameworks-native-services-surfaceflinger-tests-end2end
+```
+
+For more information see the build documentation:
+
+* <https://android.googlesource.com/platform/build/soong/+/main/docs/tidy.md#the-tidy_directory-targets>
+
+### Seeing clang-tidy checks in your editor
+
+If your editor supports using [`clangd`](https://clangd.llvm.org/) as a
+C++ language server, you can build and export a compilation database using
+Soong. With the local `.clangd` configuration file, you should see the same
+checks in editor, along with all the other checks `clangd` runs.
+
+See the build documentation for the compilation database instructions:
+
+https://android.googlesource.com/platform/build/soong/+/main/docs/compdb.md
diff --git a/services/surfaceflinger/tests/end2end/main.cpp b/services/surfaceflinger/tests/end2end/main.cpp
new file mode 100644
index 0000000..ddf6900
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/main.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 <cstdlib>
+#include <string_view>
+
+#include <android-base/logging.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+void init(int argc, char** argv) {
+ using namespace std::string_view_literals;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+
+ auto minimumSeverity = android::base::INFO;
+ for (int i = 1; i < argc; i++) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ const std::string_view arg = argv[i];
+
+ if (arg == "-v"sv) {
+ minimumSeverity = android::base::DEBUG;
+ } else if (arg == "-vv"sv) {
+ minimumSeverity = android::base::VERBOSE;
+ }
+ }
+ ::android::base::SetMinimumLogSeverity(minimumSeverity);
+}
+
+} // namespace
+
+auto main(int argc, char** argv) -> int {
+ init(argc, argv);
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
similarity index 61%
rename from services/surfaceflinger/RenderArea.cpp
rename to services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
index 5fea521..c3a535e 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#include "RenderArea.h"
+#pragma once
-namespace android {
+#include <cstdint>
-float RenderArea::getCaptureFillValue(CaptureFill captureFill) {
- switch(captureFill) {
- case CaptureFill::CLEAR:
- return 0.0f;
- case CaptureFill::OPAQUE:
- default:
- return 1.0f;
- }
-}
+namespace android::surfaceflinger::tests::end2end::test_framework::core {
-} // namespace android
+struct DisplayConfiguration final {
+ using Id = int64_t;
+
+ Id id{};
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::core
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp
new file mode 100644
index 0000000..0531f18
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <memory>
+#include <span>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/expected.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/core/TestService.h"
+#include "test_framework/fake_hwc3/Hwc3Controller.h"
+#include "test_framework/surfaceflinger/SFController.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::core {
+
+struct TestService::Passkey final {};
+
+auto TestService::startWithDisplays(const std::vector<DisplayConfiguration>& displays)
+ -> base::expected<std::unique_ptr<TestService>, std::string> {
+ using namespace std::string_literals;
+
+ auto service = std::make_unique<TestService>(TestService::Passkey{});
+ if (service == nullptr) {
+ return base::unexpected("Failed to construct the TestService instance."s);
+ }
+
+ if (auto result = service->init(displays); !result) {
+ return base::unexpected("Failed to init the TestService instance: "s + result.error());
+ }
+
+ return service;
+}
+
+TestService::TestService(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto TestService::init(std::span<const DisplayConfiguration> displays)
+ -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto hwcResult = fake_hwc3::Hwc3Controller::make(displays);
+ if (!hwcResult) {
+ return base::unexpected(std::move(hwcResult).error());
+ }
+ auto hwc = *std::move(hwcResult);
+
+ auto flingerResult = surfaceflinger::SFController::make();
+ if (!flingerResult) {
+ return base::unexpected(std::move(flingerResult).error());
+ }
+ auto flinger = *std::move(flingerResult);
+
+ surfaceflinger::SFController::useHwcService(fake_hwc3::Hwc3Controller::getServiceName());
+
+ if (auto result = flinger->startAndConnect(); !result) {
+ return base::unexpected(std::move(result).error());
+ }
+
+ mHwc = std::move(hwc);
+ mFlinger = std::move(flinger);
+ return {};
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::core
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h
new file mode 100644
index 0000000..21e6426
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework {
+
+namespace surfaceflinger {
+
+class SFController;
+
+} // namespace surfaceflinger
+
+namespace fake_hwc3 {
+
+class Hwc3Controller;
+
+} // namespace fake_hwc3
+
+namespace core {
+
+class TestService final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Constructs the test service, and starts it with the given displays as connected at boot.
+ [[nodiscard]] static auto startWithDisplays(const std::vector<DisplayConfiguration>& displays)
+ -> base::expected<std::unique_ptr<TestService>, std::string>;
+
+ explicit TestService(Passkey passkey);
+
+ // Obtains the HWC3 back-end controller
+ [[nodiscard]] auto hwc() -> fake_hwc3::Hwc3Controller& {
+ CHECK(mHwc);
+ return *mHwc;
+ }
+
+ // Obtains the SurfaceFlinger front-end controller
+ [[nodiscard]] auto flinger() -> surfaceflinger::SFController& {
+ CHECK(mFlinger);
+ return *mFlinger;
+ }
+
+ private:
+ [[nodiscard]] auto init(std::span<const DisplayConfiguration> displays)
+ -> base::expected<void, std::string>;
+
+ std::shared_ptr<fake_hwc3::Hwc3Controller> mHwc;
+ std::shared_ptr<surfaceflinger::SFController> mFlinger;
+};
+
+} // namespace core
+} // namespace android::surfaceflinger::tests::end2end::test_framework
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp
new file mode 100644
index 0000000..5349ef0
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/graphics/composer3/BnComposer.h>
+#include <aidl/android/hardware/graphics/composer3/Capability.h>
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <aidl/android/hardware/graphics/composer3/PowerMode.h>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_status.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/fake_hwc3/Hwc3Composer.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer::Hwc3ComposerImpl final
+ : public aidl::android::hardware::graphics::composer3::BnComposer {
+ using Capability = aidl::android::hardware::graphics::composer3::Capability;
+ using IComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
+ using Hwc3PowerMode = aidl::android::hardware::graphics::composer3::PowerMode;
+
+ // begin IComposer overrides
+
+ auto dump(int dumpFd, const char** args, uint32_t num_args) -> binder_status_t override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(dumpFd, args, num_args);
+ return static_cast<binder_status_t>(STATUS_NO_MEMORY);
+ }
+
+ auto createClient(std::shared_ptr<IComposerClient>* out_client) -> ndk::ScopedAStatus override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(out_client);
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IComposer::EX_NO_RESOURCES, "Client failed to initialize");
+ }
+
+ auto getCapabilities(std::vector<Capability>* out_capabilities) -> ndk::ScopedAStatus override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(out_capabilities);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ // end IComposer overrides
+};
+
+struct Hwc3Composer::Passkey final {};
+
+auto Hwc3Composer::getServiceName(std::string_view baseServiceName) -> std::string {
+ return Hwc3ComposerImpl::makeServiceName(baseServiceName);
+}
+
+auto Hwc3Composer::make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string> {
+ using namespace std::string_literals;
+
+ auto composer = std::make_shared<Hwc3Composer>(Passkey{});
+ if (composer == nullptr) {
+ return base::unexpected("Failed to construct the Hwc3Composer instance."s);
+ }
+
+ if (auto result = composer->init(); !result) {
+ return base::unexpected("Failed to init the Hwc3Composer instance: "s + result.error());
+ }
+
+ return composer;
+}
+
+Hwc3Composer::Hwc3Composer(Hwc3Composer::Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto Hwc3Composer::init() -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto impl = ndk::SharedRefBase::make<Hwc3ComposerImpl>();
+ if (!impl) {
+ return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s);
+ }
+
+ mImpl = std::move(impl);
+
+ return {};
+}
+
+auto Hwc3Composer::getComposer() -> std::shared_ptr<Hwc3IComposer> {
+ return mImpl;
+}
+
+void Hwc3Composer::addDisplay(const core::DisplayConfiguration& display) {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(display, mImpl);
+}
+
+void Hwc3Composer::removeDisplay(core::DisplayConfiguration::Id displayId) {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(displayId, mImpl);
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h
new file mode 100644
index 0000000..6d6b737
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <android-base/expected.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ class Hwc3ComposerImpl; // An internal class implements the AIDL interface.
+
+ public:
+ using Hwc3IComposer = aidl::android::hardware::graphics::composer3::IComposer;
+
+ // Gets the full qualified service name given a base name for the service.
+ [[nodiscard]] static auto getServiceName(std::string_view baseServiceName) -> std::string;
+
+ // Constructs a Hwc3Composer instance.
+ [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string>;
+
+ explicit Hwc3Composer(Passkey passkey);
+
+ // Obtains the AIDL composer3::IComposer interface for the internal instance.
+ [[nodiscard]] auto getComposer() -> std::shared_ptr<Hwc3IComposer>;
+
+ // Adds a display to the composer. This will sent a hotplug connect event.
+ void addDisplay(const core::DisplayConfiguration& display);
+
+ // Removes a display from the composer. This will sent a hotplug disconnect event.
+ void removeDisplay(core::DisplayConfiguration::Id displayId);
+
+ private:
+ [[nodiscard]] auto init() -> base::expected<void, std::string>;
+
+ std::shared_ptr<Hwc3ComposerImpl> mImpl;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp
new file mode 100644
index 0000000..ea985c0
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 <memory>
+#include <span>
+#include <string>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_stability.h>
+#include <android/binder_status.h>
+#include <fmt/format.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/fake_hwc3/Hwc3Composer.h"
+#include "test_framework/fake_hwc3/Hwc3Controller.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+struct Hwc3Controller::Passkey final {};
+
+auto Hwc3Controller::make(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string> {
+ using namespace std::string_literals;
+
+ auto controller = std::make_unique<Hwc3Controller>(Passkey{});
+ if (controller == nullptr) {
+ return base::unexpected("Failed to construct the Hwc3Controller instance"s);
+ }
+
+ if (auto result = controller->init(displays); !result) {
+ return base::unexpected("Failed to construct the Hwc3Controller instance: "s +
+ result.error());
+ }
+
+ return controller;
+}
+
+Hwc3Controller::Hwc3Controller(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto Hwc3Controller::init(const std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto qualifiedServiceName = Hwc3Composer::getServiceName(baseServiceName);
+
+ auto composerResult = Hwc3Composer::make();
+ if (!composerResult) {
+ return base::unexpected(std::move(composerResult).error());
+ }
+ auto composer = *std::move(composerResult);
+
+ for (const auto& display : displays) {
+ composer->addDisplay(display);
+ }
+
+ auto binder = composer->getComposer()->asBinder();
+
+ // This downgrade allows us to use the fake service name without it being defined in the
+ // VINTF manifest.
+ AIBinder_forceDowngradeToLocalStability(binder.get());
+
+ auto status = AServiceManager_addService(binder.get(), qualifiedServiceName.c_str());
+ if (status != STATUS_OK) {
+ return base::unexpected(fmt::format("Failed to register service {}. Error {}.",
+ qualifiedServiceName, status));
+ }
+ LOG(INFO) << "Registered service " << qualifiedServiceName << ". Error: " << status;
+
+ mComposer = std::move(composer);
+ return {};
+}
+
+auto Hwc3Controller::getServiceName() -> std::string {
+ return Hwc3Composer::getServiceName(baseServiceName);
+}
+
+void Hwc3Controller::addDisplay(const core::DisplayConfiguration& config) {
+ CHECK(mComposer);
+ mComposer->addDisplay(config);
+}
+
+void Hwc3Controller::removeDisplay(core::DisplayConfiguration::Id displayId) {
+ CHECK(mComposer);
+ mComposer->removeDisplay(displayId);
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h
new file mode 100644
index 0000000..e53d2cf
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <string>
+
+#include <android-base/expected.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer;
+
+class Hwc3Controller final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Gets the service name for the HWC3 instance that will be created and registered
+ [[nodiscard]] static auto getServiceName() -> std::string;
+
+ // Makes the HWC3 controller instance.
+ [[nodiscard]] static auto make(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string>;
+
+ explicit Hwc3Controller(Passkey passkey);
+
+ // Adds a new display to the HWC3, which will become a hotplug connect event.
+ void addDisplay(const core::DisplayConfiguration& config);
+
+ // Removes a new display from the HWC3, which will become a hotplug disconnect event.
+ void removeDisplay(core::DisplayConfiguration::Id displayId);
+
+ private:
+ static constexpr std::string baseServiceName = "fake";
+
+ [[nodiscard]] auto init(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<void, std::string>;
+
+ std::shared_ptr<Hwc3Composer> mComposer;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp
new file mode 100644
index 0000000..1cf49c5
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 <chrono>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <ftl/finalizer.h>
+#include <ftl/ignore.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "test_framework/surfaceflinger/SFController.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger {
+
+namespace {
+
+auto waitForSurfaceFlingerAIDL() -> sp<gui::ISurfaceComposer> {
+ constexpr auto kTimeout = std::chrono::seconds(30);
+ constexpr auto kSurfaceFlingerServiceName = "SurfaceFlingerAIDL";
+ const sp<android::IServiceManager> serviceManager(android::defaultServiceManager());
+ const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout;
+
+ LOG(INFO) << "Waiting " << kTimeout << " for service manager registration....";
+ sp<android::IBinder> flingerService;
+ while (flingerService == nullptr) {
+ if (std::chrono::steady_clock::now() > kTimeoutAfter) {
+ LOG(INFO) << "... Timeout!";
+ return nullptr;
+ }
+
+ constexpr auto sleepTime = std::chrono::milliseconds(10);
+ std::this_thread::sleep_for(sleepTime);
+ flingerService = serviceManager->checkService(String16(kSurfaceFlingerServiceName));
+ }
+ LOG(INFO) << "Obtained surfaceflinger interface from service manager.";
+
+ return interface_cast<gui::ISurfaceComposer>(flingerService);
+}
+
+} // namespace
+
+struct SFController::Passkey final {};
+
+void SFController::useHwcService(std::string_view fqn) {
+ base::SetProperty("debug.sf.hwc_service_name", std::string(fqn));
+}
+
+auto SFController::make() -> base::expected<std::shared_ptr<SFController>, std::string> {
+ using namespace std::string_literals;
+
+ auto controller = std::make_unique<SFController>(Passkey{});
+ if (controller == nullptr) {
+ return base::unexpected("Failed to construct the SFController instance."s);
+ }
+
+ if (auto result = controller->init(); !result) {
+ return base::unexpected("Failed to init the SFController instance: "s + result.error());
+ }
+
+ return controller;
+}
+
+SFController::SFController(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto SFController::init() -> base::expected<void, std::string> {
+ LOG(INFO) << "Stopping everything to prepare for tests";
+ // NOLINTBEGIN(cert-env33-c)
+ system("stop");
+ // NOLINTEND(cert-env33-c)
+
+ mCleanup = ftl::Finalizer([this]() { stop(); });
+
+ return {};
+}
+
+auto SFController::startAndConnect() -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ start();
+
+ LOG(VERBOSE) << "Getting ISurfaceComposer....";
+ auto surfaceComposerAidl = waitForSurfaceFlingerAIDL();
+ if (surfaceComposerAidl == nullptr) {
+ return base::unexpected("Failed to obtain the surfaceComposerAidl interface."s);
+ }
+ LOG(VERBOSE) << "Getting ISurfaceComposerClient....";
+ sp<gui::ISurfaceComposerClient> surfaceComposerClientAidl;
+ if (!surfaceComposerAidl->createConnection(&surfaceComposerClientAidl).isOk()) {
+ return base::unexpected("Failed to obtain the surfaceComposerClientAidl interface."s);
+ }
+ if (surfaceComposerClientAidl == nullptr) {
+ return base::unexpected("Failed to obtain a valid surfaceComposerClientAidl interface."s);
+ }
+ auto surfaceComposerClient = sp<SurfaceComposerClient>::make(surfaceComposerClientAidl);
+ if (surfaceComposerClient == nullptr) {
+ return base::unexpected(
+ "Failed to construct a surfaceComposerClient around the aidl interface."s);
+ }
+
+ mSurfaceComposerAidl = std::move(surfaceComposerAidl);
+ mSurfaceComposerClientAidl = std::move(surfaceComposerClientAidl);
+ mSurfaceComposerClient = std::move(surfaceComposerClient);
+
+ LOG(INFO) << "Connected to surfaceflinger";
+ return {};
+}
+
+void SFController::start() {
+ LOG(INFO) << "Starting surfaceflinger";
+ // NOLINTBEGIN(cert-env33-c)
+ system("start surfaceflinger");
+ // NOLINTEND(cert-env33-c)
+}
+
+void SFController::stop() {
+ LOG(INFO) << "Stopping surfaceflinger";
+ // NOLINTBEGIN(cert-env33-c)
+ system("stop surfaceflinger");
+ // NOLINTEND(cert-env33-c)
+
+ if (mSurfaceComposerAidl != nullptr) {
+ LOG(INFO) << "Waiting for SF AIDL interface to die";
+
+ constexpr auto kTimeout = std::chrono::seconds(30);
+ const auto binder = android::gui::ISurfaceComposer::asBinder(mSurfaceComposerAidl);
+ const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout;
+
+ while (binder->isBinderAlive()) {
+ if (std::chrono::steady_clock::now() > kTimeoutAfter) {
+ LOG(INFO) << "... Timeout!";
+ break;
+ }
+
+ ftl::ignore = binder->pingBinder();
+
+ constexpr auto kPollInterval = std::chrono::milliseconds(10);
+ std::this_thread::sleep_for(kPollInterval);
+ }
+
+ constexpr auto kShutdownWait = std::chrono::milliseconds(500);
+ std::this_thread::sleep_for(kShutdownWait);
+ }
+
+ mSurfaceComposerClient = nullptr;
+ mSurfaceComposerClientAidl = nullptr;
+ mSurfaceComposerAidl = nullptr;
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger
diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h
new file mode 100644
index 0000000..58bac91
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <android-base/expected.h>
+#include <ftl/finalizer.h>
+#include <utils/StrongPointer.h>
+
+namespace android::gui {
+
+class ISurfaceComposer;
+class ISurfaceComposerClient;
+
+} // namespace android::gui
+
+namespace android {
+
+class SurfaceComposerClient;
+
+} // namespace android
+
+namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger {
+
+class SFController final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Sets a property so that SurfaceFlinger uses the named HWC service.
+ static void useHwcService(std::string_view fqn);
+
+ // Makes an instance of the SFController.
+ [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<SFController>, std::string>;
+
+ explicit SFController(Passkey pass);
+
+ // Starts SurfaceFlinger and establishes the AIDL interface connections.
+ [[nodiscard]] auto startAndConnect() -> base::expected<void, std::string>;
+
+ private:
+ [[nodiscard]] auto init() -> base::expected<void, std::string>;
+ static void start();
+ void stop();
+
+ sp<gui::ISurfaceComposer> mSurfaceComposerAidl;
+ sp<gui::ISurfaceComposerClient> mSurfaceComposerClientAidl;
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+ // Finalizers should be last so their destructors are invoked first.
+ ftl::FinalizerFtl mCleanup;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger
diff --git a/services/surfaceflinger/tests/end2end/tests/.clang-tidy b/services/surfaceflinger/tests/end2end/tests/.clang-tidy
new file mode 100644
index 0000000..4924c46
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/tests/.clang-tidy
@@ -0,0 +1,32 @@
+# 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.
+
+FormatStyle: file
+InheritParentConfig: true
+
+# Note: For tests, we are actually turning off certain checks enabled for the
+# non-test code in the parent .clang-tidy file.
+Checks:
+ - -cppcoreguidelines-avoid-magic-numbers # Allow tests to use magic numbers.
+ - -cppcoreguidelines-avoid-goto # Google Test macros use goto.
+ - -cppcoreguidelines-avoid-non-const-global-variables # Google Test macros define global variables.
+ - -cppcoreguidelines-macro-usage # Google Benchmark defines function-like macros.
+ - -cppcoreguidelines-owning-memory # Google Test macros use operator new directly.
+ - -google-runtime-int # Tests might intentionally use the base "short"/"long" types and not want to use "int16"/"int64".
+ - -misc-use-anonymous-namespace # Google Test macros declare some static global variables to not export them.
+ - -modernize-use-trailing-return-type # Google Test macros use non-trailing return types.
+ - -performance-move-const-arg # Tests might std::move() a trivially copyable value as part of testing that moving works.
+ - -readability-function-cognitive-complexity # Assertions turn into extra branches, increasing apparent complexity.
+ - -readability-magic-numbers # Allow tests to use magic numbers
+
diff --git a/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp
new file mode 100644
index 0000000..3c4277f
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "test_framework/core/TestService.h"
+
+namespace android::surfaceflinger::tests::end2end {
+namespace {
+
+struct Placeholder : public ::testing::Test {};
+
+TEST_F(Placeholder, Bringup) {
+ auto serviceResult = test_framework::core::TestService::startWithDisplays({
+ {.id = 123},
+ });
+ if (!serviceResult) {
+ LOG(WARNING) << "End2End service not available. " << serviceResult.error();
+ GTEST_SKIP() << "End2End service not available. " << serviceResult.error();
+ }
+}
+
+} // namespace
+} // namespace android::surfaceflinger::tests::end2end
diff --git a/services/surfaceflinger/tests/testdata/SetBorderSettings_Cropped.png b/services/surfaceflinger/tests/testdata/SetBorderSettings_Cropped.png
new file mode 100644
index 0000000..b52d517
--- /dev/null
+++ b/services/surfaceflinger/tests/testdata/SetBorderSettings_Cropped.png
Binary files differ
diff --git a/services/surfaceflinger/tests/testdata/SetBorderSettings_HalfAlpha.png b/services/surfaceflinger/tests/testdata/SetBorderSettings_HalfAlpha.png
new file mode 100644
index 0000000..e1ab54b
--- /dev/null
+++ b/services/surfaceflinger/tests/testdata/SetBorderSettings_HalfAlpha.png
Binary files differ
diff --git a/services/surfaceflinger/tests/testdata/SetBorderSettings_Opaque.png b/services/surfaceflinger/tests/testdata/SetBorderSettings_Opaque.png
new file mode 100644
index 0000000..bbaf0af
--- /dev/null
+++ b/services/surfaceflinger/tests/testdata/SetBorderSettings_Opaque.png
Binary files differ
diff --git a/services/surfaceflinger/tests/testdata/SetBorderSettings_StrokeColorWithAlpha.png b/services/surfaceflinger/tests/testdata/SetBorderSettings_StrokeColorWithAlpha.png
new file mode 100644
index 0000000..0fe2ed8
--- /dev/null
+++ b/services/surfaceflinger/tests/testdata/SetBorderSettings_StrokeColorWithAlpha.png
Binary files differ
diff --git a/services/surfaceflinger/tests/testdata/SetBorderSettings_StrokeWidth1.png b/services/surfaceflinger/tests/testdata/SetBorderSettings_StrokeWidth1.png
new file mode 100644
index 0000000..3ee5ac6
--- /dev/null
+++ b/services/surfaceflinger/tests/testdata/SetBorderSettings_StrokeWidth1.png
Binary files differ
diff --git a/services/surfaceflinger/tests/testdata/SetBorderSettings_ZeroAlpha.png b/services/surfaceflinger/tests/testdata/SetBorderSettings_ZeroAlpha.png
new file mode 100644
index 0000000..e5e8850
--- /dev/null
+++ b/services/surfaceflinger/tests/testdata/SetBorderSettings_ZeroAlpha.png
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
new file mode 100644
index 0000000..8011309
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp
@@ -0,0 +1,482 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <mock/DisplayHardware/MockComposer.h>
+#include <mock/MockLayer.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "ActivePictureTracker.h"
+#include "LayerFE.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using android::compositionengine::LayerFECompositionState;
+using android::gui::ActivePicture;
+using android::gui::IActivePictureListener;
+using android::mock::MockLayer;
+using surfaceflinger::frontend::LayerSnapshot;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+using testing::SizeIs;
+using testing::StrictMock;
+
+class TestableLayerFE : public LayerFE {
+public:
+ TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
+ mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
+ }
+
+ LayerSnapshot& snapshot;
+};
+
+class MockActivePictureListener : public gui::BnActivePictureListener {
+public:
+ operator ActivePictureTracker::Listeners const() {
+ return {sp<IActivePictureListener>::fromExisting(this)};
+ }
+
+ MOCK_METHOD(binder::Status, onActivePicturesChanged, (const std::vector<ActivePicture>&),
+ (override));
+};
+
+class ActivePictureTrackerTest : public testing::Test {
+protected:
+ const static ActivePictureTracker::Listeners NO_LISTENERS;
+
+ SurfaceFlinger* flinger() {
+ if (!mFlingerSetup) {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
+ mFlingerSetup = true;
+ }
+ return mFlinger.flinger();
+ }
+
+ sp<NiceMock<MockLayer>> createMockLayer(int layerId, int ownerUid) {
+ auto layer = sp<NiceMock<MockLayer>>::make(flinger(), layerId);
+ EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(ownerUid)));
+ return layer;
+ }
+
+ sp<StrictMock<MockActivePictureListener>> createMockListener() {
+ return sp<StrictMock<MockActivePictureListener>>::make();
+ }
+
+ ActivePictureTracker::Listeners mListenersToAdd;
+ ActivePictureTracker::Listeners mListenersToRemove;
+
+private:
+ TestableSurfaceFlinger mFlinger;
+ bool mFlingerSetup = false;
+};
+
+const ActivePictureTracker::Listeners ActivePictureTrackerTest::NO_LISTENERS;
+
+// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
+// Parcelable, which has a virtual destructor.
+auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
+ std::vector<ActivePicture> activePictures;
+ for (auto tuple : tuples) {
+ ActivePicture ap;
+ ap.layerId = std::get<0>(tuple);
+ ap.ownerUid = std::get<1>(tuple);
+ ap.pictureProfileId = std::get<2>(tuple);
+ activePictures.push_back(ap);
+ }
+ return testing::UnorderedElementsAreArray(activePictures);
+}
+
+// Parcelables don't define this for matchers, which is unfortunate
+void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
+ *os << activePicture.toString();
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_called) {
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_withListenerAlreadyAdded_notCalled) {
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_withUncommittedProfile_calledWithNone) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+ {
+ auto listener = createMockListener();
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenListenerAdded_withCommittedProfile_calledWithActivePicture) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ auto listener = createMockListener();
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileAdded_calledWithActivePicture) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenContinuesUsingProfile_notCalled) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileIsRemoved_calledWithNoActivePictures) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileIsNotCommitted_calledWithNoActivePictures) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1);
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenProfileChanges_calledWithDifferentProfile) {
+ auto layer = createMockLayer(100, 10);
+ TestableLayerFE layerFE;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1)))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1)))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenMultipleCommittedProfiles_calledWithMultipleActivePictures) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 20);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(2)))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenNonCommittedProfileChanges_notCalled) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 20);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenDifferentLayerUsesSameProfile_called) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 20);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenSameUidDifferentLayerUsesSameProfile_called) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ auto layer2 = createMockLayer(200, 10);
+ TestableLayerFE layerFE2;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+TEST_F(ActivePictureTrackerTest, whenNewLayerUsesSameProfile_called) {
+ auto layer1 = createMockLayer(100, 10);
+ TestableLayerFE layerFE1;
+
+ ActivePictureTracker tracker;
+ auto listener = createMockListener();
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1);
+ tracker.updateAndNotifyListeners(*listener, NO_LISTENERS);
+ }
+
+ auto layer2 = createMockLayer(200, 10);
+ TestableLayerFE layerFE2;
+ {
+ layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE1.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+ layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+ layerFE2.onPictureProfileCommitted();
+ tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+ EXPECT_CALL(*listener, onActivePicturesChanged(_))
+ .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) {
+ EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+ return binder::Status::ok();
+ });
+ tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
deleted file mode 100644
index b926d2f..0000000
--- a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * 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.
- */
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <android/gui/ActivePicture.h>
-#include <android/gui/IActivePictureListener.h>
-#include <compositionengine/mock/CompositionEngine.h>
-#include <mock/DisplayHardware/MockComposer.h>
-#include <mock/MockLayer.h>
-#include <renderengine/mock/RenderEngine.h>
-
-#include "ActivePictureUpdater.h"
-#include "LayerFE.h"
-#include "TestableSurfaceFlinger.h"
-
-namespace android {
-
-using android::compositionengine::LayerFECompositionState;
-using android::gui::ActivePicture;
-using android::gui::IActivePictureListener;
-using android::mock::MockLayer;
-using surfaceflinger::frontend::LayerSnapshot;
-using testing::_;
-using testing::NiceMock;
-using testing::Return;
-
-class TestableLayerFE : public LayerFE {
-public:
- TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
- mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
- }
-
- LayerSnapshot& snapshot;
-};
-
-class ActivePictureUpdaterTest : public testing::Test {
-protected:
- SurfaceFlinger* flinger() {
- if (!mFlingerSetup) {
- mFlinger.setupMockScheduler();
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
- mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
- mFlingerSetup = true;
- }
- return mFlinger.flinger();
- }
-
-private:
- TestableSurfaceFlinger mFlinger;
- bool mFlingerSetup = false;
-};
-
-// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
-// Parcelable, which has a virtual destructor.
-auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
- std::vector<ActivePicture> activePictures;
- for (auto tuple : tuples) {
- ActivePicture ap;
- ap.layerId = std::get<0>(tuple);
- ap.ownerUid = std::get<1>(tuple);
- ap.pictureProfileId = std::get<2>(tuple);
- activePictures.push_back(ap);
- }
- return testing::UnorderedElementsAreArray(activePictures);
-}
-
-// Parcelables don't define this for matchers, which is unfortunate
-void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
- *os << activePicture.toString();
-}
-
-TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) {
- sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE;
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE.onPictureProfileCommitted();
- updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_FALSE(updater.updateAndHasChanged());
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
- }
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
- }
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
- }
-}
-
-TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) {
- sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
- TestableLayerFE layerFE1;
- EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- ActivePictureUpdater updater;
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
- }
-
- sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
- TestableLayerFE layerFE2;
- EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
-
- {
- layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE1.onPictureProfileCommitted();
- updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
-
- layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
- layerFE2.onPictureProfileCommitted();
- updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
-
- ASSERT_TRUE(updater.updateAndHasChanged());
- EXPECT_THAT(updater.getActivePictures(),
- UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
- }
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 6af5143..f1c1549 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -65,9 +65,9 @@
"mock/MockFrameTracer.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockTimeStats.cpp",
- "mock/MockVsyncController.cpp",
"mock/MockVSyncDispatch.cpp",
"mock/MockVSyncTracker.cpp",
+ "mock/MockVsyncController.cpp",
],
}
@@ -87,10 +87,10 @@
test_suites: ["device-tests"],
header_libs: ["surfaceflinger_tests_common_headers"],
srcs: [
+ "*.cpp",
":libsurfaceflinger_backend_mock_sources",
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
- "*.cpp",
],
}
@@ -103,6 +103,7 @@
"librenderengine_deps",
"libsurfaceflinger_common_test_deps",
"libsurfaceflinger_proto_deps",
+ "poweradvisor_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
@@ -116,9 +117,8 @@
"android.hardware.power@1.2",
"android.hardware.power@1.3",
"libaidlcommonsupport",
- "libcompositionengine_mocks",
"libcompositionengine",
- "libframetimeline",
+ "libcompositionengine_mocks",
"libgmock",
"libgui_mocks",
"libperfetto_client_experimental",
@@ -139,14 +139,15 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.common@1.2",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libSurfaceFlingerProp",
"libbase",
"libbinder",
"libbinder_ndk",
"libcutils",
- "libEGL",
"libfmq",
- "libGLESv1_CM",
- "libGLESv2",
"libgui",
"libhidlbase",
"libinput",
@@ -156,11 +157,10 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
"libstatslog_surfaceflinger",
- "libSurfaceFlingerProp",
"libsync",
+ "libtracing_perfetto",
"libui",
"libutils",
- "libtracing_perfetto",
],
header_libs: [
"android.hardware.graphics.composer3-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index b517ff0..c858d9a 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -54,8 +54,8 @@
compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
std::move(compostionEngineDisplayArgs));
mDisplay = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
- ui::DisplayConnectionType::Internal, HWC_DISPLAY,
- kIsPrimary)
+ ui::DisplayConnectionType::Internal,
+ DEFAULT_DISPLAY_PORT, HWC_DISPLAY, kIsPrimary)
.setDisplaySurface(mDisplaySurface)
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
@@ -68,7 +68,9 @@
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
static constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
- static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+ static constexpr uint8_t DEFAULT_DISPLAY_PORT = 42u;
+ static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID =
+ PhysicalDisplayId::fromPort(DEFAULT_DISPLAY_PORT);
static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 860ad2e..c342e1e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -36,7 +36,6 @@
#include <system/window.h>
#include <utils/String8.h>
-#include "DisplayRenderArea.h"
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
@@ -76,7 +75,8 @@
constexpr hal::HWLayerId HWC_LAYER = 5000;
constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr uint8_t DEFAULT_DISPLAY_PORT = 42u;
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(DEFAULT_DISPLAY_PORT);
constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
@@ -198,25 +198,21 @@
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
constexpr bool regionSampling = false;
- auto renderArea =
- DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
- ui::Dataspace::V0_SRGB,
- RenderArea::Options::CAPTURE_SECURE_LAYERS |
- RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION);
-
auto getLayerSnapshotsFn = mFlinger.getLayerSnapshotsForScreenshotsFn(mDisplay->getLayerStack(),
CaptureArgs::UNSET_UID);
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
mCaptureScreenBuffer =
- std::make_shared<renderengine::mock::FakeExternalTexture>(renderArea->getReqWidth(),
- renderArea->getReqHeight(),
+ std::make_shared<renderengine::mock::FakeExternalTexture>(sourceCrop.getSize().width,
+ sourceCrop.getSize().height,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
usage);
- auto future = mFlinger.renderScreenImpl(mDisplay, std::move(renderArea), getLayerSnapshotsFn,
- mCaptureScreenBuffer, regionSampling);
+ auto future = mFlinger.renderScreenImpl(mDisplay, sourceCrop, ui::Dataspace::V0_SRGB,
+ getLayerSnapshotsFn, mCaptureScreenBuffer,
+ regionSampling, mDisplay->isSecure(),
+ /* seamlessTransition */ true);
ASSERT_TRUE(future.valid());
const auto fenceResult = future.get();
@@ -276,7 +272,8 @@
test->mDisplay =
FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
+ kDisplayConnectionType, DEFAULT_DISPLAY_PORT, HWC_DISPLAY,
+ kIsPrimary)
.setDisplaySurface(test->mDisplaySurface)
.setNativeWindow(test->mNativeWindow)
.setSecure(Derived::IS_SECURE)
@@ -471,7 +468,7 @@
layer.externalTexture = buffer;
layer.bufferData->acquireFence = Fence::NO_FENCE;
layer.dataspace = ui::Dataspace::UNKNOWN;
- layer.surfaceDamageRegion = Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH));
+ layer.setSurfaceDamageRegion(Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
Mock::VerifyAndClear(test->mRenderEngine);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index 29a1fab..84dc5fc 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -67,11 +67,12 @@
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;
- mDisplaySnapshotOpt.emplace(mDisplayId, ui::DisplayConnectionType::Internal,
+ mDisplaySnapshotOpt.emplace(mDisplayId, infoOpt->port, ui::DisplayConnectionType::Internal,
makeModes(kMode60, kMode90, kMode120), ui::ColorModes{},
std::nullopt);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index fa976c8..75182e5 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -114,7 +114,11 @@
// Test instances
TestableSurfaceFlinger mFlinger;
+
sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+ sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+ sp<compositionengine::mock::DisplaySurface>::make();
+
sp<GraphicBuffer> mBuffer =
sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
@@ -179,7 +183,7 @@
template <typename PhysicalDisplay>
struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static PhysicalDisplayId get() {
+ static DisplayIdVariant get() {
if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
? LEGACY_DISPLAY_TYPE_PRIMARY
@@ -193,14 +197,14 @@
}
};
-template <uint64_t displayId>
+template <VirtualDisplayId::BaseId displayId>
struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
- static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+ static DisplayIdVariant get() { return HalVirtualDisplayId(displayId); }
};
template <>
struct DisplayIdGetter<GpuVirtualDisplayIdType> {
- static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+ static DisplayIdVariant get() { return GpuVirtualDisplayId(0); }
};
template <typename>
@@ -231,6 +235,16 @@
static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
};
+template <typename>
+struct PortGetter {
+ static constexpr std::optional<uint8_t> value;
+};
+
+template <typename PhysicalDisplay>
+struct PortGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<uint8_t> value = PhysicalDisplay::PORT;
+};
+
// DisplayIdType can be:
// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
@@ -241,6 +255,7 @@
using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
+ using PORT = PortGetter<DisplayIdType>;
static constexpr int WIDTH = width;
static constexpr int HEIGHT = height;
@@ -277,11 +292,13 @@
TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
compositionDisplay,
CONNECTION_TYPE::value,
+ PORT::value,
HWC_DISPLAY_ID_OPT::value,
static_cast<bool>(PRIMARY));
injector.setSecure(static_cast<bool>(SECURE));
injector.setNativeWindow(test->mNativeWindow);
+ injector.setDisplaySurface(test->mDisplaySurface);
// Creating a DisplayDevice requires getting default dimensions from the
// native window along with some other initial setup.
@@ -348,17 +365,17 @@
// 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
template <hal::PowerMode kPowerMode = hal::PowerMode::ON>
static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
- const auto displayId = DisplayVariant::DISPLAY_ID::get();
- ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
- TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(DisplayVariant::DISPLAY_ID::get(),
+ HWC_DISPLAY_TYPE,
static_cast<bool>(DisplayVariant::PRIMARY))
.setHwcDisplayId(HWC_DISPLAY_ID)
.setResolution(DisplayVariant::RESOLUTION)
@@ -520,13 +537,14 @@
static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
};
-template <ui::DisplayConnectionType connectionType, bool hasIdentificationData, bool secure>
+template <ui::DisplayConnectionType connectionType, bool hasIdentificationData, bool secure,
+ HWDisplayId hwDisplayId = 1002>
struct SecondaryDisplay {
static constexpr auto CONNECTION_TYPE = connectionType;
static constexpr Primary PRIMARY = Primary::FALSE;
static constexpr bool SECURE = secure;
static constexpr uint8_t PORT = 254;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = hwDisplayId;
static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
static constexpr auto GET_IDENTIFICATION_DATA =
connectionType == ui::DisplayConnectionType::Internal ? getInternalEdid
@@ -558,6 +576,11 @@
/*hasIdentificationData=*/true, kNonSecure>,
1080, 2092>;
+template <HWDisplayId hwDisplayId = 1002>
+using ExternalDisplayWithIdentificationVariant = PhysicalDisplayVariant<
+ SecondaryDisplay<ui::DisplayConnectionType::External,
+ /*hasIdentificationData=*/true, kNonSecure, hwDisplayId>,
+ 1920, 1280>;
using ExternalDisplayVariant =
PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External,
/*hasIdentificationData=*/false, kSecure>,
@@ -639,9 +662,8 @@
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
- const auto displayId = Base::DISPLAY_ID::get();
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setId(displayId)
+ .setId(Base::DISPLAY_ID::get())
.setPixels(Base::RESOLUTION)
.setIsSecure(static_cast<bool>(Base::SECURE))
.setPowerAdvisor(&test->mPowerAdvisor)
@@ -654,7 +676,12 @@
ceDisplayArgs);
// Insert display data so that the HWC thinks it created the virtual display.
- test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ const auto ceDisplayIdVar = compositionDisplay->getDisplayIdVariant();
+ LOG_ALWAYS_FATAL_IF(!ceDisplayIdVar);
+ EXPECT_EQ(*ceDisplayIdVar, Base::DISPLAY_ID::get());
+ const auto displayId = asHalDisplayId(*ceDisplayIdVar);
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
return compositionDisplay;
}
@@ -792,8 +819,9 @@
inline DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
ui::Size resolution = ui::Size(1920, 1080)) {
- return mock::createDisplayMode(modeId, refreshRate, group, resolution,
- PrimaryDisplayVariant::DISPLAY_ID::get());
+ const auto physicalDisplayId = asPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get());
+ LOG_ALWAYS_FATAL_IF(!physicalDisplayId);
+ return mock::createDisplayMode(modeId, refreshRate, group, resolution, *physicalDisplayId);
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
index 90e716f..edcb639 100644
--- a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
+++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
@@ -48,8 +48,10 @@
}
}
- static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
- static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+ static inline PhysicalDisplayId kInnerDisplayId =
+ asPhysicalDisplayId(InnerDisplayVariant::DISPLAY_ID::get()).value();
+ static inline PhysicalDisplayId kOuterDisplayId =
+ asPhysicalDisplayId(OuterDisplayVariant::DISPLAY_ID::get()).value();
sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
};
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 268a6c4..76e01a6 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -23,14 +23,13 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <gui/DisplayEventReceiver.h>
#include <log/log.h>
#include <scheduler/VsyncConfig.h>
#include <utils/Errors.h>
#include "AsyncCallRecorder.h"
#include "DisplayHardware/DisplayMode.h"
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "Scheduler/EventThread.h"
#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
@@ -112,8 +111,6 @@
void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime);
void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
std::vector<FrameRateOverride>);
- void expectQueuedBufferCountReceivedByConnection(
- ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount);
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
nsecs_t deadlineTimestamp) {
@@ -147,7 +144,6 @@
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
- std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders;
std::chrono::nanoseconds mVsyncPeriod;
@@ -270,7 +266,7 @@
ASSERT_TRUE(args.has_value()) << name << " did not receive an event for timestamp "
<< expectedTimestamp;
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_VSYNC, event.header.type)
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_VSYNC, event.header.type)
<< name << " did not get the correct event for timestamp " << expectedTimestamp;
EXPECT_EQ(expectedTimestamp, event.header.timestamp)
<< name << " did not get the expected timestamp for timestamp " << expectedTimestamp;
@@ -344,7 +340,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_HOTPLUG, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(expectedConnected, event.hotplug.connected);
}
@@ -355,7 +351,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_MODE_CHANGE, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(expectedConfigId, event.modeChange.modeId);
EXPECT_EQ(expectedVsyncPeriod, event.modeChange.vsyncPeriod);
@@ -367,7 +363,7 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
EXPECT_EQ(uid, event.frameRateOverride.uid);
EXPECT_EQ(frameRateHz, event.frameRateOverride.frameRateHz);
@@ -376,18 +372,10 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH, event.header.type);
EXPECT_EQ(expectedDisplayId, event.header.displayId);
}
-void EventThreadTest::expectQueuedBufferCountReceivedByConnection(
- ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) {
- auto args = connectionEventRecorder.waitForCall();
- ASSERT_TRUE(args.has_value());
- const auto& event = std::get<0>(args.value());
- EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers);
-}
-
namespace {
using namespace testing;
@@ -874,69 +862,12 @@
auto args = mConnectionEventCallRecorder.waitForCall();
ASSERT_TRUE(args.has_value());
const auto& event = std::get<0>(args.value());
- EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type);
+ EXPECT_EQ(DisplayEventType::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type);
EXPECT_EQ(EXTERNAL_DISPLAY_ID, event.header.displayId);
EXPECT_EQ(HDCP_V1, event.hdcpLevelsChange.connectedLevel);
EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
}
-TEST_F(EventThreadTest, connectionReceivesBufferStuffing) {
- setupEventThread();
-
- // Create a connection that will experience buffer stuffing.
- ConnectionEventRecorder stuffedConnectionEventRecorder{0};
- sp<MockEventThreadConnection> stuffedConnection =
- createConnection(stuffedConnectionEventRecorder,
- gui::ISurfaceComposer::EventRegistration::modeChanged |
- gui::ISurfaceComposer::EventRegistration::frameRateOverride,
- 111);
-
- // Add a connection and buffer count to the list of stuffed Uids that will receive
- // data in the next vsync event.
- BufferStuffingMap bufferStuffedUids;
- bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3);
- mThread->addBufferStuffedUids(bufferStuffedUids);
- mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder);
-
- // Signal that we want the next vsync event to be posted to two connections.
- mThread->requestNextVsync(mConnection);
- mThread->requestNextVsync(stuffedConnection);
- onVSyncEvent(123, 456, 789);
-
- // Vsync event data contains number of queued buffers.
- expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0);
- expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3);
-}
-
-TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) {
- setupEventThread();
-
- // Create a connection with the same Uid as another connection.
- ConnectionEventRecorder secondConnectionEventRecorder{0};
- sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder,
- gui::ISurfaceComposer::EventRegistration::modeChanged |
- gui::ISurfaceComposer::EventRegistration::frameRateOverride,
- mConnectionUid);
-
- // Add connection Uid and buffer count to the list of stuffed Uids that will receive
- // data in the next vsync event.
- BufferStuffingMap bufferStuffedUids;
- bufferStuffedUids.try_emplace(mConnectionUid, 3);
- mThread->addBufferStuffedUids(bufferStuffedUids);
- mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder);
- mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder);
-
- // Signal that we want the next vsync event to be posted to two connections.
- mThread->requestNextVsync(mConnection);
- mThread->requestNextVsync(secondConnection);
- onVSyncEvent(123, 456, 789);
-
- // Vsync event data contains number of queued buffers.
- expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3);
- expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3);
-}
-
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
index 744c536..5f7a9f2 100644
--- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -24,12 +24,15 @@
namespace android {
+static constexpr uint8_t kDefaultPort = 255u;
+
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
using android::adpf::mock::PowerAdvisor;
using android::hardware::graphics::composer::hal::HWDisplayId;
struct FakeDisplayInjectorArgs {
- PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
+ PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(kDefaultPort);
+ uint8_t port = kDefaultPort;
HWDisplayId hwcDisplayId = 0;
bool isPrimary = true;
};
@@ -73,7 +76,7 @@
.build());
auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
- ui::DisplayConnectionType::Internal,
+ ui::DisplayConnectionType::Internal, args.port,
args.hwcDisplayId, args.isPrimary);
injector.setNativeWindow(mNativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index a5b347a..c6da1a1 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -125,13 +125,13 @@
TEST_F(FlagManagerTest, ignoresOverrideInUnitTestMode) {
mFlagManager.setUnitTestMode();
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+ SET_FLAG_FOR_TEST(flags::no_vsyncs_on_screen_off, true);
// If this has not been called in this process, it will be called.
// Regardless, the result is ignored.
EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(false));
- EXPECT_EQ(true, mFlagManager.multithreaded_present());
+ EXPECT_EQ(true, mFlagManager.no_vsyncs_on_screen_off());
}
TEST_F(FlagManagerTest, returnsValue) {
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/JankTrackerTest.cpp b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
index 2941a14..0f16073 100644
--- a/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/JankTrackerTest.cpp
@@ -213,4 +213,33 @@
EXPECT_EQ(listenerCount(), 0u);
}
-} // namespace android
\ No newline at end of file
+TEST_F(JankTrackerTest, multipleLayersAreTrackedIndependently) {
+ size_t jankDataReceived = 0;
+ size_t numBatchesReceived = 0;
+
+ EXPECT_CALL(*mListener.get(), onJankData(_))
+ .WillRepeatedly([&](const std::vector<gui::JankData>& jankData) {
+ jankDataReceived += jankData.size();
+ numBatchesReceived++;
+ return binder::Status::ok();
+ });
+ addJankListener(123);
+ addJankListener(321);
+ addJankData(123, 1);
+ addJankData(123, 2);
+ addJankData(123, 3);
+ addJankData(321, 4);
+ addJankData(321, 5);
+
+ JankTracker::flushJankData(123);
+ flushBackgroundThread();
+ EXPECT_EQ(numBatchesReceived, 1u);
+ EXPECT_EQ(jankDataReceived, 3u);
+
+ JankTracker::flushJankData(321);
+ flushBackgroundThread();
+ EXPECT_EQ(numBatchesReceived, 2u);
+ EXPECT_EQ(jankDataReceived, 5u);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 53a9062..f3d6dcc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -584,7 +584,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -623,7 +623,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
@@ -662,7 +662,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
EXPECT_EQ(1u, layerCount());
@@ -694,7 +694,7 @@
auto layer = createLegacyAndFrontedEndLayer(1);
showLayer(1);
- setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST,
ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
setFrameRateCategory(1, 0);
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 119e182..35ec536 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -21,7 +21,7 @@
#include "FrontEnd/LayerLifecycleManager.h"
#include "LayerHierarchyTest.h"
-#include "TransactionState.h"
+#include "QueuedTransactionState.h"
using namespace android::surfaceflinger;
@@ -104,7 +104,7 @@
EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
// apply transactions that do not affect the hierarchy
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.backgroundBlurRadius = 22;
@@ -297,7 +297,7 @@
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.bgColor.a = 0.5;
@@ -326,7 +326,7 @@
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.bgColor.a = 0.5;
@@ -360,7 +360,7 @@
layers.emplace_back(rootLayer(1));
lifecycleManager.addLayers(std::move(layers));
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.bgColor.a = 0.5;
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index bb54138..d045eb8 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -28,6 +28,7 @@
#include "ui/GraphicTypes.h"
#include <com_android_graphics_libgui_flags.h>
+#include <cmath>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -260,6 +261,40 @@
EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f);
}
+TEST_F(LayerSnapshotTest, AlphaInheritedByChildWhenParentIsHiddenByInvalidTransform) {
+ setMatrix(1, 0, 0, 0, 0);
+ update(mSnapshotBuilder);
+ mLifecycleManager.commitChanges();
+
+ setAlpha(1, 0.5);
+ update(mSnapshotBuilder);
+ mLifecycleManager.commitChanges();
+
+ setMatrix(1, 1, 0, 0, 1);
+ update(mSnapshotBuilder);
+ mLifecycleManager.commitChanges();
+
+ EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
+ EXPECT_EQ(getSnapshot(11)->alpha, 0.5f);
+}
+
+TEST_F(LayerSnapshotTest, AlphaInheritedByChildWhenParentIsHidden) {
+ hideLayer(1);
+ update(mSnapshotBuilder);
+ mLifecycleManager.commitChanges();
+
+ setAlpha(1, 0.5);
+ update(mSnapshotBuilder);
+ mLifecycleManager.commitChanges();
+
+ showLayer(1);
+ update(mSnapshotBuilder);
+ mLifecycleManager.commitChanges();
+
+ EXPECT_EQ(getSnapshot(1)->alpha, 0.5f);
+ EXPECT_EQ(getSnapshot(11)->alpha, 0.5f);
+}
+
// Change states
TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) {
setCrop(1, Rect(1, 2, 3, 4));
@@ -329,7 +364,7 @@
}
TEST_F(LayerSnapshotTest, UpdateMetadata) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
@@ -374,7 +409,7 @@
TEST_F(LayerSnapshotTest, UpdateMetadataOfHiddenLayers) {
hideLayer(1);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
@@ -1425,6 +1460,85 @@
EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
}
+TEST_F(LayerSnapshotTest, setCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect{1000, 1000});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, ignoreCornerRadius) {
+ static constexpr float RADIUS = 123.f;
+ setClientDrawnCornerRadius(1, RADIUS);
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect{1000, 1000});
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius());
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 0.f);
+}
+
+TEST_F(LayerSnapshotTest, childInheritsParentScaledSettings) {
+ // ROOT
+ // ├── 1 (crop rect set to contain child layer)
+ // │ ├── 11
+ static constexpr float RADIUS = 123.f;
+
+ setRoundedCorners(1, RADIUS);
+ FloatRect parentCropRect(1, 1, 999, 999);
+ setCrop(1, parentCropRect);
+ // Rotate surface by 90
+ setMatrix(11, 0.f, -1.f, 1.f, 0.f);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ ui::Transform t = getSnapshot({.id = 11})->localTransform.inverse();
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.cropRect, t.transform(parentCropRect));
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, RADIUS * t.getScaleX());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.y, RADIUS * t.getScaleY());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.requestedRadius.x, RADIUS * t.getScaleX());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.requestedRadius.y, RADIUS * t.getScaleY());
+}
+
+TEST_F(LayerSnapshotTest, childInheritsParentClientDrawnCornerRadius) {
+ // ROOT
+ // ├── 1 (crop rect set to contain child layers )
+ // │ ├── 11
+ // │ │ └── 111
+
+ static constexpr float RADIUS = 123.f;
+
+ setClientDrawnCornerRadius(1, RADIUS);
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect(1, 1, 999, 999));
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasClientDrawnRadius());
+ EXPECT_TRUE(getSnapshot({.id = 11})->roundedCorner.hasRoundedCorners());
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, RADIUS);
+}
+
+TEST_F(LayerSnapshotTest, childIgnoreCornerRadiusOverridesParent) {
+ // ROOT
+ // ├── 1 (crop rect set to contain child layers )
+ // │ ├── 11
+ // │ │ └── 111
+
+ static constexpr float RADIUS = 123.f;
+
+ setRoundedCorners(1, RADIUS);
+ setCrop(1, Rect(1, 1, 999, 999));
+
+ setClientDrawnCornerRadius(11, RADIUS);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, RADIUS);
+ EXPECT_EQ(getSnapshot({.id = 11})->roundedCorner.radius.x, 0.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->roundedCorner.radius.x, RADIUS);
+}
+
TEST_F(LayerSnapshotTest, setShadowRadius) {
static constexpr float SHADOW_RADIUS = 123.f;
setShadowRadius(1, SHADOW_RADIUS);
@@ -1432,6 +1546,14 @@
EXPECT_EQ(getSnapshot(1)->shadowSettings.length, SHADOW_RADIUS);
}
+TEST_F(LayerSnapshotTest, setBorderSettings) {
+ gui::BorderSettings settings;
+ settings.strokeWidth = 5;
+ setBorderSettings(1, settings);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->borderSettings.strokeWidth, settings.strokeWidth);
+}
+
TEST_F(LayerSnapshotTest, setTrustedOverlayForNonVisibleInput) {
hideLayer(1);
setTrustedOverlay(1, gui::TrustedOverlay::ENABLED);
@@ -1557,13 +1679,13 @@
setColor(3, {-1._hf, -1._hf, -1._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = 3;
- transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->token = sp<BBinder>::make();
mLifecycleManager.applyTransactions(transactions);
@@ -1586,13 +1708,13 @@
setColor(3, {-1._hf, -1._hf, -1._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
transactions.back().states.front().layerId = 3;
- transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
- auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ auto inputInfo = transactions.back().states.front().state.editWindowInfo();
+ *inputInfo = {};
inputInfo->token = sp<BBinder>::make();
hideLayer(3);
mLifecycleManager.applyTransactions(transactions);
@@ -1799,9 +1921,6 @@
}
TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) {
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
setCrop(1, Rect(0, 0, 20, 20));
setBuffer(1221,
std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */,
@@ -1840,9 +1959,6 @@
TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The left bound is extended when shifting to the right
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
setCrop(1, Rect(0, 0, 20, 20));
const int texSize = 10;
setBuffer(1221,
@@ -1862,9 +1978,6 @@
TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The right bound is extended when shifting to the left
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
const int crop = 20;
setCrop(1, Rect(0, 0, crop, crop));
const int texSize = 10;
@@ -1885,9 +1998,6 @@
TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The top bound is extended when shifting to the bottom
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
setCrop(1, Rect(0, 0, 20, 20));
const int texSize = 10;
setBuffer(1221,
@@ -1907,9 +2017,6 @@
TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The bottom bound is extended when shifting to the top
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
const int crop = 20;
setCrop(1, Rect(0, 0, crop, crop));
const int texSize = 10;
@@ -1930,9 +2037,6 @@
TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) {
// The left bound is extended when shifting to the right
- if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
- GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
- }
const int crop = 20;
setCrop(1, Rect(0, 0, crop, crop));
const int texSize = 10;
@@ -2021,16 +2125,13 @@
EXPECT_FALSE(getSnapshot(1)->contentDirty);
}
TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
- if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
- GTEST_SKIP() << "Flag disabled, skipping test";
- }
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
- transactions.back().states.front().layerId = 1;
- transactions.back().states.front().state.layerId = 1;
- transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged;
- transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3);
+ transactions.back().states.back().layerId = 1;
+ transactions.back().states.back().state.layerId = 1;
+ transactions.back().states.back().state.what = layer_state_t::ePictureProfileHandleChanged;
+ transactions.back().states.back().state.pictureProfileHandle = PictureProfileHandle(3);
mLifecycleManager.applyTransactions(transactions);
EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
@@ -2042,23 +2143,50 @@
}
TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
- if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
- GTEST_SKIP() << "Flag disabled, skipping test";
+ {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 1;
+ transactions.back().states.back().state.layerId = 1;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = 1;
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 2;
+ transactions.back().states.back().state.layerId = 2;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = -1;
+
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+ update(mSnapshotBuilder);
+
+ EXPECT_GT(getSnapshot(1)->pictureProfilePriority, getSnapshot(2)->pictureProfilePriority);
+ EXPECT_EQ(getSnapshot(1)->pictureProfilePriority - getSnapshot(2)->pictureProfilePriority,
+ 2);
}
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().layerId = 1;
- transactions.back().states.front().state.layerId = 1;
- transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged;
- transactions.back().states.front().state.appContentPriority = 3;
+ {
+ std::vector<QueuedTransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 1;
+ transactions.back().states.back().state.layerId = 1;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = INT_MIN;
+ transactions.back().states.push_back({});
+ transactions.back().states.back().layerId = 2;
+ transactions.back().states.back().state.layerId = 2;
+ transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged;
+ transactions.back().states.back().state.appContentPriority = INT_MAX;
- mLifecycleManager.applyTransactions(transactions);
- EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+ mLifecycleManager.applyTransactions(transactions);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
- update(mSnapshotBuilder);
+ update(mSnapshotBuilder);
- EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3);
+ EXPECT_GT(getSnapshot(2)->pictureProfilePriority, getSnapshot(1)->pictureProfilePriority);
+ }
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 908637a..e9b86b2 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -22,7 +22,7 @@
#include <scheduler/interface/ICompositor.h>
-#include "FrameTimeline.h"
+#include "FrameTimeline/FrameTimeline.h"
#include "Scheduler/MessageQueue.h"
#include "mock/MockVSyncDispatch.h"
#include "utils/Timers.h"
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 5c25f34..d7f7bdb 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -39,6 +39,7 @@
using namespace std::chrono_literals;
using namespace testing;
using namespace android::power;
+using namespace ftl::flag_operators;
namespace android::adpf::impl {
@@ -54,6 +55,8 @@
void setTimingTestingMode(bool testinMode);
void allowReportActualToAcquireMutex();
bool sessionExists();
+ ftl::Flags<Workload> getCommittedWorkload() const;
+ ftl::Flags<Workload> getQueuedWorkload() const;
int64_t toNanos(Duration d);
struct GpuTestConfig {
@@ -315,6 +318,14 @@
return mPowerAdvisor->sTargetSafetyMargin;
}
+ftl::Flags<Workload> PowerAdvisorTest::getCommittedWorkload() const {
+ return mPowerAdvisor->mCommittedWorkload;
+}
+
+ftl::Flags<Workload> PowerAdvisorTest::getQueuedWorkload() const {
+ return ftl::Flags<Workload>{mPowerAdvisor->mQueuedWorkload.load()};
+}
+
namespace {
TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
@@ -842,5 +853,32 @@
ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
}
+TEST_F(PowerAdvisorTest, trackQueuedWorkloads) {
+ mPowerAdvisor->setQueuedWorkload(ftl::Flags<Workload>());
+ ASSERT_EQ(getQueuedWorkload(), ftl::Flags<Workload>());
+
+ // verify workloads are queued
+ mPowerAdvisor->setQueuedWorkload(ftl::Flags<Workload>(Workload::VISIBLE_REGION));
+ ASSERT_EQ(getQueuedWorkload(), ftl::Flags<Workload>(Workload::VISIBLE_REGION));
+
+ mPowerAdvisor->setQueuedWorkload(ftl::Flags<Workload>(Workload::EFFECTS));
+ ASSERT_EQ(getQueuedWorkload(), Workload::VISIBLE_REGION | Workload::EFFECTS);
+
+ // verify queued workloads are cleared after commit
+ mPowerAdvisor->setCommittedWorkload(ftl::Flags<Workload>());
+ ASSERT_EQ(getQueuedWorkload(), ftl::Flags<Workload>());
+}
+
+TEST_F(PowerAdvisorTest, trackCommittedWorkloads) {
+ // verify queued workloads are cleared after commit
+ mPowerAdvisor->setCommittedWorkload(Workload::SCREENSHOT | Workload::VISIBLE_REGION);
+ ASSERT_EQ(getCommittedWorkload(), Workload::SCREENSHOT | Workload::VISIBLE_REGION);
+
+ // on composite, verify we update the committed workload so we track workload increases for the
+ // next frame accurately
+ mPowerAdvisor->setCompositedWorkload(Workload::VISIBLE_REGION | Workload::DISPLAY_CHANGES);
+ ASSERT_EQ(getCommittedWorkload(), Workload::VISIBLE_REGION | Workload::DISPLAY_CHANGES);
+}
+
} // namespace
} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1fc874d..116fcd9 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -143,7 +143,7 @@
kDisplay1Mode60->getId()));
// TODO(b/241285191): Restore once VsyncSchedule::getPendingHardwareVsyncState is called by
- // Scheduler::setDisplayPowerMode rather than SF::setPowerModeInternal.
+ // Scheduler::setDisplayPowerMode rather than SF::setPhysicalDisplayPowerMode.
#if 0
// Hardware VSYNC should be disabled for newly registered displays.
EXPECT_CALL(mSchedulerCallback, requestHardwareVsync(kDisplayId2, false)).Times(1);
@@ -254,18 +254,43 @@
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
- EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms));
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms));
- EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(120_Hz, 30ms));
+ struct TestCase {
+ Fps refreshRate;
+ std::chrono::nanoseconds presentLatency;
+ int expectedBufferCount;
+ };
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
+ const auto verifyTestCases = [&](std::vector<TestCase> tests) {
+ for (const auto testCase : tests) {
+ EXPECT_EQ(testCase.expectedBufferCount,
+ mFlinger.calculateMaxAcquiredBufferCount(testCase.refreshRate,
+ testCase.presentLatency));
+ }
+ };
- EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+ std::vector<TestCase> testCases{{60_Hz, 30ms, 1},
+ {90_Hz, 30ms, 2},
+ {120_Hz, 30ms, 3},
+ {60_Hz, 40ms, 2},
+ {60_Hz, 10ms, 1}};
+ verifyTestCases(testCases);
const auto savedMinAcquiredBuffers = mFlinger.mutableMinAcquiredBuffers();
mFlinger.mutableMinAcquiredBuffers() = 2;
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
+ verifyTestCases({{60_Hz, 10ms, 2}});
mFlinger.mutableMinAcquiredBuffers() = savedMinAcquiredBuffers;
+
+ const auto savedMaxAcquiredBuffers = mFlinger.mutableMaxAcquiredBuffers();
+ mFlinger.mutableMaxAcquiredBuffers() = 2;
+ testCases = {{60_Hz, 30ms, 1},
+ {90_Hz, 30ms, 2},
+ {120_Hz, 30ms, 2}, // max buffers allowed is 2
+ {60_Hz, 40ms, 2},
+ {60_Hz, 10ms, 1}};
+ verifyTestCases(testCases);
+ mFlinger.mutableMaxAcquiredBuffers() = 3; // max buffers allowed is 3
+ verifyTestCases({{120_Hz, 30ms, 3}});
+ mFlinger.mutableMaxAcquiredBuffers() = savedMaxAcquiredBuffers;
}
MATCHER(Is120Hz, "") {
@@ -716,8 +741,6 @@
}
TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
-
// resyncAllToHardwareVsync will result in requesting hardware VSYNC on display 1, which is on,
// but not on display 2, which is off.
EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
@@ -738,28 +761,6 @@
mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
}
-TEST_F(SchedulerTest, resyncAllLegacyAppliesToOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, false);
-
- // In the legacy code, prior to the flag, resync applied to OFF displays.
- EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1);
- EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1);
-
- mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
-
- mScheduler->registerDisplay(kDisplayId2,
- std::make_shared<RefreshRateSelector>(kDisplay2Modes,
- kDisplay2Mode60->getId()));
- ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2));
-
- static constexpr bool kDisallow = true;
- mScheduler->disableHardwareVsync(kDisplayId1, kDisallow);
- mScheduler->disableHardwareVsync(kDisplayId2, kDisallow);
-
- static constexpr bool kAllowToEnable = true;
- mScheduler->resyncAllToHardwareVsync(kAllowToEnable);
-}
-
class AttachedChoreographerTest : public SchedulerTest {
protected:
void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 2d3ebb4..aa48c1b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -27,7 +27,8 @@
class CreateDisplayTest : public DisplayTransactionTest {
public:
- void createDisplayWithRequestedRefreshRate(const std::string& name, uint64_t displayId,
+ void createDisplayWithRequestedRefreshRate(const std::string& name,
+ VirtualDisplayId::BaseId baseId,
float pacesetterDisplayRefreshRate,
float requestedRefreshRate,
float expectedAdjustedRefreshRate) {
@@ -49,12 +50,10 @@
EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate));
EXPECT_EQ(name.c_str(), display.displayName);
- std::optional<VirtualDisplayId> vid =
- DisplayId::fromValue<VirtualDisplayId>(displayId | DisplayId::FLAG_VIRTUAL);
- ASSERT_TRUE(vid.has_value());
-
sp<DisplayDevice> device =
- mFlinger.createVirtualDisplayDevice(displayToken, *vid, requestedRefreshRate);
+ mFlinger.createVirtualDisplayDevice(displayToken, GpuVirtualDisplayId(baseId),
+ requestedRefreshRate);
+
EXPECT_TRUE(device->isVirtual());
device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate));
// verifying desired value
@@ -142,7 +141,11 @@
// --------------------------------------------------------------------
// Invocation
- sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, false, kUniqueId);
+ sp<IBinder> displayToken =
+ mFlinger.createVirtualDisplay(kDisplayName, false,
+ gui::ISurfaceComposer::OptimizationPolicy::
+ optimizeForPower,
+ kUniqueId);
// --------------------------------------------------------------------
// Postconditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index b0dd5c2..3f710fd 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -126,8 +126,9 @@
static constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
static constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
-
- static constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+ static constexpr uint8_t kOuterDisplayPort = 254u;
+ static constexpr PhysicalDisplayId kOuterDisplayId =
+ PhysicalDisplayId::fromPort(kOuterDisplayPort);
auto injectOuterDisplay() {
// For the inner display, this is handled by setupHwcHotplugCallExpectations.
@@ -149,6 +150,7 @@
kModeId120);
},
{.displayId = kOuterDisplayId,
+ .port = kOuterDisplayPort,
.hwcDisplayId = kOuterDisplayHwcId,
.isPrimary = kIsPrimary});
@@ -169,16 +171,22 @@
static constexpr DisplayModeId kModeId90{1};
static constexpr DisplayModeId kModeId120{2};
static constexpr DisplayModeId kModeId90_4K{3};
+ static constexpr DisplayModeId kModeId60_8K{4};
static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz, 0);
static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz, 1);
static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz, 2);
static constexpr ui::Size kResolution4K{3840, 2160};
+ static constexpr ui::Size kResolution8K{7680, 4320};
+
static inline const DisplayModePtr kMode90_4K =
createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+ static inline const DisplayModePtr kMode60_8K =
+ createDisplayMode(kModeId60_8K, 60_Hz, 4, kResolution8K);
- static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
+ static inline const DisplayModes kModes =
+ makeModes(kMode60, kMode90, kMode120, kMode90_4K, kMode60_8K);
};
void DisplayModeSwitchingTest::setupScheduler(
@@ -324,6 +332,8 @@
}
TEST_F(DisplayModeSwitchingTest, changeResolutionWithoutRefreshRequired) {
+ SET_FLAG_FOR_TEST(flags::synced_resolution_switch, false);
+
EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_EQ(NO_ERROR,
@@ -358,9 +368,45 @@
EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90_4K));
}
-TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
+TEST_F(DisplayModeSwitchingTest, changeResolutionSynced) {
+ SET_FLAG_FOR_TEST(flags::synced_resolution_switch, true);
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ // PrimaryDisplayVariant has a 4K size, so switch to 8K.
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60_8K,
+ 60_Hz)));
+
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+
+ // The mode should not be set until the commit that resizes the display.
+ mFlinger.commit();
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+ mFlinger.commit();
+ EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId60_8K));
+
+ // Set the display size to match the resolution.
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = mDisplay->getDisplayToken().promote();
+ state.width = static_cast<uint32_t>(kResolution8K.width);
+ state.height = static_cast<uint32_t>(kResolution8K.height);
+
+ // The next commit should set the mode and resize the framebuffer.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_CALL(*mDisplaySurface, resizeBuffers(kResolution8K));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60_8K);
+
+ constexpr bool kModeset = true;
+ mFlinger.setDisplayStateLocked(state);
+ mFlinger.configureAndCommit(kModeset);
+
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60_8K));
+}
+
+TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) {
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -369,8 +415,8 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
- mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -400,8 +446,8 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
- mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
- mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
@@ -425,8 +471,6 @@
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -435,8 +479,8 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
- mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -478,7 +522,7 @@
EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
// Power off the display before the mode has been set.
- mFlinger.setPowerModeInternal(mDisplay, hal::PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mDisplay, hal::PowerMode::OFF);
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -495,8 +539,6 @@
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
const auto [innerDisplay, outerDisplay] = injectOuterDisplay();
EXPECT_TRUE(innerDisplay->isPoweredOn());
@@ -505,8 +547,8 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
- mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
@@ -523,7 +565,7 @@
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
// Power off the outer display before the mode has been set.
- mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::OFF);
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -540,8 +582,8 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
- mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
- mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(innerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(outerDisplay, hal::PowerMode::ON);
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 9bf344c..eac5a8e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -68,13 +68,9 @@
template <typename Case, bool connected>
void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
- const auto convert = [](auto physicalDisplayId) {
- return std::make_optional(DisplayId{physicalDisplayId});
- };
-
- EXPECT_CALL(*eventThread,
- onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
- .Times(1);
+ const auto physicalDisplayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(physicalDisplayId);
+ EXPECT_CALL(*eventThread, onHotplugReceived(*physicalDisplayId, connected)).Times(1);
}
template <typename Case>
@@ -111,7 +107,7 @@
std::optional<DisplayDeviceState::Physical> expectedPhysical;
if (Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
@@ -137,10 +133,10 @@
EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
// SF should have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ const auto displayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayIdOpt);
- const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+ const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(*displayIdOpt);
ASSERT_TRUE(displayOpt);
const auto& display = displayOpt->get();
@@ -163,7 +159,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 +193,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 +215,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);
@@ -246,9 +242,9 @@
EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
// SF should not have a PhysicalDisplay.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
+ const auto physicalDisplayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(physicalDisplayIdOpt);
+ ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt));
// The existing token should have been removed.
verifyDisplayIsNotConnected(existing.token());
@@ -327,9 +323,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
@@ -355,9 +352,10 @@
EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
// SF should not have a PhysicalDisplay.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
+ const auto physicalDisplayIdOpt =
+ asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(physicalDisplayIdOpt);
+ ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt));
}(),
testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}
@@ -378,9 +376,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
@@ -398,10 +397,12 @@
// The existing token should have been removed.
verifyDisplayIsNotConnected(existing.token());
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ const auto physicalDisplayIdOpt =
+ asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(physicalDisplayIdOpt);
- const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+ const auto displayOpt =
+ mFlinger.mutablePhysicalDisplays().get(*physicalDisplayIdOpt);
ASSERT_TRUE(displayOpt);
EXPECT_NE(existing.token(), displayOpt->get().token());
@@ -538,9 +539,9 @@
// Preconditions
// A virtual display is set up but is removed from the current state.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
- mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
Case::Display::injectHwcDisplay(this);
auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
existing.inject();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index 19f8deb..8972840 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -38,30 +38,30 @@
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
// ...and should still be after powering on.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
}
TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) {
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
// The outer display should become the pacesetter after folding.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
// The inner display should become the pacesetter after unfolding.
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
}
TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) {
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
// The inner display should stay the pacesetter if both are powered on.
// TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
// The outer display should become the pacesetter if designated.
@@ -74,20 +74,20 @@
}
TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) {
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
// The outer display should become the pacesetter if the inner display powers off.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
// The outer display should stay the pacesetter if both are powered on.
// TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
// The inner display should become the pacesetter if the outer display powers off.
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::OFF);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
}
@@ -114,8 +114,8 @@
.Times(0);
// The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
- // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
EXPECT_TRUE(mInnerDisplay->isPoweredOn());
EXPECT_FALSE(mOuterDisplay->isPoweredOn());
@@ -133,10 +133,10 @@
.Times(1);
// The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
- // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
EXPECT_FALSE(mInnerDisplay->isPoweredOn());
EXPECT_TRUE(mOuterDisplay->isPoweredOn());
@@ -154,9 +154,9 @@
.Times(1);
// The injected VsyncSchedule uses TestableScheduler::mockRequestHardwareVsync, so no calls to
- // ISchedulerCallback::requestHardwareVsync are expected during setPowerModeInternal.
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ // ISchedulerCallback::requestHardwareVsync are expected during setPhysicalDisplayPowerMode.
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
EXPECT_TRUE(mInnerDisplay->isPoweredOn());
EXPECT_TRUE(mOuterDisplay->isPoweredOn());
@@ -167,18 +167,16 @@
}
TEST_F(FoldableTest, requestVsyncOnPowerOn) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true))
.Times(1);
EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
.Times(1);
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
}
TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
// When the device boots, the inner display should be the pacesetter.
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
@@ -192,10 +190,10 @@
EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, false))
.Times(1);
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(mOuterDisplay, PowerMode::ON);
- mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(mInnerDisplay, PowerMode::OFF);
// Other display is now the pacesetter.
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index aef467a..b8b1b95 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) {
@@ -59,6 +59,141 @@
EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
}
+TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) {
+ // Configure a primary display with identification data.
+ using PrimaryDisplay = InnerDisplayVariant;
+ PrimaryDisplay::setupHwcHotplugCallExpectations(this);
+ PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
+ PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+
+ // TODO: b/241286146 - Remove this unnecessary call.
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+
+ // A single commit should be scheduled.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
+
+ mFlinger.configure();
+
+ // Configure an external display with identification info.
+ using ExternalDisplay = ExternalDisplayWithIdentificationVariant<>;
+ ExternalDisplay::setupHwcHotplugCallExpectations(this);
+ ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+
+ // TODO: b/241286146 - Remove this unnecessary call.
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+
+ mFlinger.configure();
+
+ EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
+ const auto primaryDisplayId = asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get());
+ ASSERT_TRUE(primaryDisplayId);
+ EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*primaryDisplayId));
+ const auto primaryDisplayIdOpt =
+ mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
+ ASSERT_TRUE(primaryDisplayIdOpt.has_value());
+ const auto primaryPhysicalDisplayOpt =
+ mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
+ ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
+ const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
+ EXPECT_EQ(*primaryDisplayId, primaryDisplaySnapshotRef.get().displayId());
+ EXPECT_EQ(PrimaryDisplay::PORT::value, primaryDisplaySnapshotRef.get().port());
+ EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
+ primaryDisplaySnapshotRef.get().connectionType());
+
+ EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+ const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
+ ASSERT_TRUE(externalDisplayId);
+ EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
+ const auto externalDisplayIdOpt =
+ mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
+ ASSERT_TRUE(externalDisplayIdOpt.has_value());
+ const auto externalPhysicalDisplayOpt =
+ mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
+ ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
+ const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
+ EXPECT_EQ(*externalDisplayId, externalDisplaySnapshotRef.get().displayId());
+ EXPECT_EQ(ExternalDisplay::PORT::value, externalDisplaySnapshotRef.get().port());
+ EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
+ externalDisplaySnapshotRef.get().connectionType());
+}
+
+TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithoutIdentificationData) {
+ // Configure a primary display without identification data.
+ using PrimaryDisplay = PrimaryDisplayVariant;
+ PrimaryDisplay::setupHwcHotplugCallExpectations(this);
+ PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
+ PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+
+ // TODO: b/241286146 - Remove this unnecessary call.
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+
+ // A single commit should be scheduled.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
+
+ mFlinger.configure();
+
+ // Configure an external display with identification info.
+ using ExternalDisplay = ExternalDisplayWithIdentificationVariant<>;
+ ExternalDisplay::setupHwcHotplugCallExpectations(this);
+ ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
+ ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+
+ // TODO: b/241286146 - Remove this unnecessary call.
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+
+ mFlinger.configure();
+
+ // Both ID and port are expected to be equal to 0 for primary internal display due to no
+ // identification data.
+ constexpr uint8_t primaryInternalDisplayPort = 0u;
+ constexpr PhysicalDisplayId primaryInternalDisplayId =
+ PhysicalDisplayId::fromPort(primaryInternalDisplayPort);
+ EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
+ ASSERT_EQ(primaryInternalDisplayId, asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get()));
+ EXPECT_TRUE(mFlinger.getHwComposer().isConnected(primaryInternalDisplayId));
+ const auto primaryDisplayIdOpt =
+ mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
+ ASSERT_TRUE(primaryDisplayIdOpt.has_value());
+ const auto primaryPhysicalDisplayOpt =
+ mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
+ ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
+ const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
+ EXPECT_EQ(primaryInternalDisplayId, primaryDisplaySnapshotRef.get().displayId());
+ EXPECT_EQ(primaryInternalDisplayPort, primaryDisplaySnapshotRef.get().port());
+ EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
+ primaryDisplaySnapshotRef.get().connectionType());
+
+ // Even though the external display has identification data available, the lack of data for the
+ // internal display has set of the legacy multi-display mode in SF and therefore the external
+ // display's identification data will be ignored.
+ // Both ID and port are expected to be equal to 1 for external internal display.
+ constexpr uint8_t externalDisplayPort = 1u;
+ constexpr PhysicalDisplayId externalDisplayId =
+ PhysicalDisplayId::fromPort(externalDisplayPort);
+ EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+ EXPECT_TRUE(mFlinger.getHwComposer().isConnected(externalDisplayId));
+ const auto externalDisplayIdOpt =
+ mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
+ ASSERT_TRUE(externalDisplayIdOpt.has_value());
+ const auto externalPhysicalDisplayOpt =
+ mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
+ ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
+ const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
+ EXPECT_EQ(externalDisplayId, externalDisplaySnapshotRef.get().displayId());
+ EXPECT_EQ(externalDisplayPort, externalDisplaySnapshotRef.get().port());
+ EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
+ externalDisplaySnapshotRef.get().connectionType());
+}
+
TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -67,7 +202,7 @@
ExternalDisplay::setupHwcHotplugCallExpectations(this);
ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
@@ -75,26 +210,26 @@
// 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
// still exist but be marked as disconnected.
EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
- EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+ const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
+ ASSERT_TRUE(externalDisplayId);
+ EXPECT_FALSE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
}
TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
- SET_FLAG_FOR_TEST(flags::connected_display, true);
-
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -111,24 +246,71 @@
EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
.WillRepeatedly(Return(Error::BAD_DISPLAY));
- // TODO(b/241286146): Remove this unnecessary call.
+ // TODO: b/241286146 - Remove this unnecessary call.
EXPECT_CALL(*mComposer,
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
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));
}
+TEST_F(HotplugTest, rejectsHotplugOnActivePortsDuplicate) {
+ // Inject a primary display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+
+ // Second display should come up properly.
+ using SecondDisplay = ExternalDisplayWithIdentificationVariant<>;
+ SecondDisplay::setupHwcHotplugCallExpectations(this);
+ SecondDisplay::setupHwcGetActiveConfigCallExpectations(this);
+
+ // TODO: b/241286146 - Remove this unnecessary call.
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(SecondDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
+
+ SecondDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+ mFlinger.configure();
+
+ EXPECT_TRUE(hasPhysicalHwcDisplay(SecondDisplay::HWC_DISPLAY_ID));
+
+ // Third display will return the same port ID as the second, and the hotplug
+ // should fail.
+ constexpr HWDisplayId kHwDisplayId = 1234;
+ using DuplicatePortDisplay = ExternalDisplayWithIdentificationVariant<kHwDisplayId>;
+
+ // We expect display identification to be fetched correctly, since EDID and
+ // port are available and successfully retrieved from HAL.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(DuplicatePortDisplay::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(*DuplicatePortDisplay::PORT::value),
+ SetArgPointee<2>(getExternalEedid()), Return(Error::NONE)));
+
+ DuplicatePortDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
+ mFlinger.configure();
+
+ // The hotplug should be rejected due to an attempt to connect a display to an already active
+ // port. No HWComposer::DisplayData should be created.
+ EXPECT_FALSE(hasPhysicalHwcDisplay(DuplicatePortDisplay::HWC_DISPLAY_ID));
+
+ // Disconnecting a display that was not successfully configured should be a no-op.
+ DuplicatePortDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
+ mFlinger.configure();
+
+ EXPECT_FALSE(hasPhysicalHwcDisplay(DuplicatePortDisplay::HWC_DISPLAY_ID));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 6cc6322..9c143fd 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -45,28 +45,15 @@
void setTransactionState() {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
TransactionInfo transaction;
- mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays,
- transaction.flags, transaction.applyToken,
- transaction.inputWindowCommands,
- TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp,
- transaction.unCachedBuffers,
- /*HasListenerCallbacks=*/false, transaction.callbacks,
- transaction.id, transaction.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transaction));
}
- struct TransactionInfo {
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags = 0;
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- InputWindowCommands inputWindowCommands;
- int64_t desiredPresentTime = 0;
- bool isAutoTimestamp = false;
- FrameTimelineInfo frameTimelineInfo{};
- std::vector<client_cache_t> unCachedBuffers;
- uint64_t id = static_cast<uint64_t>(-1);
- std::vector<uint64_t> mergedTransactionIds;
- std::vector<ListenerCallbacks> callbacks;
+ struct TransactionInfo : public TransactionState {
+ TransactionInfo() {
+ mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ mIsAutoTimestamp = false;
+ mId = static_cast<uint64_t>(-1);
+ }
};
struct Compositor final : ICompositor {
@@ -383,4 +370,4 @@
}
}
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
similarity index 86%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
index fed7b2e..d5c22a9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
@@ -315,7 +315,7 @@
EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
TransitionVariant>;
-class SetPowerModeInternalTest : public DisplayTransactionTest {
+class SetPhysicalDisplayPowerModeTest : public DisplayTransactionTest {
public:
template <typename Case>
void transitionDisplayCommon();
@@ -331,15 +331,14 @@
struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
+void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() {
// --------------------------------------------------------------------
// Preconditions
Case::Doze::setupComposerCallExpectations(this);
auto display =
Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
- auto displayId = display->getId();
- if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) {
+ if (auto physicalDisplayId = asPhysicalDisplayId(display->getDisplayIdVariant())) {
Case::setInitialHwVsyncEnabled(this, *physicalDisplayId,
PowerModeInitialVSyncEnabled<
Case::Transition::INITIAL_POWER_MODE>::value);
@@ -353,7 +352,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE);
+ mFlinger.setPhysicalDisplayPowerMode(display, Case::Transition::TARGET_POWER_MODE);
// --------------------------------------------------------------------
// Postconditions
@@ -361,7 +360,7 @@
Case::Transition::verifyPostconditions(this);
}
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfNoChange) {
using Case = SimplePrimaryDisplayCase;
// --------------------------------------------------------------------
@@ -378,7 +377,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+ mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::ON);
// --------------------------------------------------------------------
// Postconditions
@@ -386,16 +385,16 @@
EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
}
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, setPhysicalDisplayPowerModeDoesNothingIfVirtualDisplay) {
using Case = HwcVirtualDisplayCase;
// --------------------------------------------------------------------
// Preconditions
// Insert display data so that the HWC thinks it created the virtual display.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
- mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ ASSERT_TRUE(mFlinger.mutableHwcDisplayData().try_emplace(*displayId).second);
// A virtual display device is set up
Case::Display::injectHwcDisplay(this);
@@ -408,7 +407,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+ mFlinger.setPhysicalDisplayPowerMode(display.mutableDisplayDevice(), PowerMode::OFF);
// --------------------------------------------------------------------
// Postconditions
@@ -416,88 +415,83 @@
EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToOnExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToOffExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeToOnExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
- SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
}
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+TEST_F(SetPhysicalDisplayPowerModeTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 352000e..23e73de 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -235,11 +235,14 @@
constexpr auto kConnectionTypeOpt = Case::Display::CONNECTION_TYPE::value;
if constexpr (kConnectionTypeOpt) {
- const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt);
+ const auto port = Case::Display::PORT::value;
+ ASSERT_TRUE(port);
+ mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, *port,
+ std::nullopt);
DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
.setResolution(Case::Display::RESOLUTION)
.setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
@@ -250,6 +253,7 @@
state.physical = {.id = *displayId,
.hwcDisplayId = *hwcDisplayId,
+ .port = *port,
.activeMode = activeMode};
ui::ColorModes colorModes;
@@ -258,7 +262,7 @@
}
const auto it = mFlinger.mutablePhysicalDisplays()
- .emplace_or_replace(*displayId, displayToken, *displayId,
+ .emplace_or_replace(*displayId, displayToken, *displayId, *port,
*kConnectionTypeOpt, makeModes(activeMode),
std::move(colorModes), std::nullopt)
.first;
@@ -278,7 +282,7 @@
// Postconditions
ASSERT_NE(nullptr, device);
- EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+ EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getDisplayIdVariant());
EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
@@ -299,6 +303,13 @@
mFlinger.mutableDisplayModeController()
.getActiveMode(device->getPhysicalId())
.modePtr->getHwcId());
+
+ EXPECT_EQ(Case::Display::PORT::value,
+ mFlinger.physicalDisplays()
+ .get(device->getPhysicalId())
+ .transform([](const display::PhysicalDisplay& display) {
+ return display.snapshot().port();
+ }));
}
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 7f0b7a6..13c32bd 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -42,7 +42,6 @@
#include "FrontEnd/RequestedLayerState.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
-#include "RenderArea.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/VSyncTracker.h"
#include "Scheduler/VsyncController.h"
@@ -184,8 +183,8 @@
}
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
- mFlinger->mCompositionEngine->setHwComposer(
- std::make_unique<impl::HWComposer>(std::move(composer)));
+ mFlinger->mHWComposer = std::make_unique<impl::HWComposer>(std::move(composer));
+ mFlinger->mCompositionEngine->setHwComposer(mFlinger->mHWComposer.get());
mFlinger->mDisplayModeController.setHwComposer(
&mFlinger->mCompositionEngine->getHwComposer());
}
@@ -338,9 +337,9 @@
mFlinger->configure();
}
- void configureAndCommit() {
+ void configureAndCommit(bool modeset = false) {
configure();
- commitTransactionsLocked(eDisplayTransactionNeeded);
+ commitTransactionsLocked(eDisplayTransactionNeeded, modeset);
}
void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
@@ -400,12 +399,16 @@
float requestedRefreshRate = 0.0f) {
static const std::string kTestId =
"virtual:libsurfaceflinger_unittest:TestableSurfaceFlinger";
- return mFlinger->createVirtualDisplay(displayName, isSecure, kTestId, requestedRefreshRate);
+ return mFlinger
+ ->createVirtualDisplay(displayName, isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy::optimizeForPower,
+ kTestId, requestedRefreshRate);
}
auto createVirtualDisplay(const std::string& displayName, bool isSecure,
+ gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy,
const std::string& uniqueId, float requestedRefreshRate = 0.0f) {
- return mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId,
+ return mFlinger->createVirtualDisplay(displayName, isSecure, optimizationPolicy, uniqueId,
requestedRefreshRate);
}
@@ -430,11 +433,14 @@
dispSurface, producer);
}
- void commitTransactionsLocked(uint32_t transactionFlags) {
+ void commitTransactionsLocked(uint32_t transactionFlags, bool modeset = false) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
mFlinger->processDisplayChangesLocked();
mFlinger->commitTransactionsLocked(transactionFlags);
+ if (modeset) {
+ mFlinger->initiateDisplayModeChanges();
+ }
}
void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) {
@@ -456,26 +462,39 @@
}
// Allow reading display state without locking, as if called on the SF main thread.
- auto setPowerModeInternal(const sp<DisplayDevice>& display,
- hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
- return mFlinger->setPowerModeInternal(display, mode);
+ auto setPhysicalDisplayPowerMode(const sp<DisplayDevice>& display,
+ hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->setPhysicalDisplayPowerMode(display, mode);
}
- auto renderScreenImpl(const sp<DisplayDevice> display,
- std::unique_ptr<const RenderArea> renderArea,
+ auto renderScreenImpl(const sp<DisplayDevice> display, const Rect sourceCrop,
+ ui::Dataspace dataspace,
SurfaceFlinger::GetLayerSnapshotsFunction getLayerSnapshotsFn,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- bool regionSampling) {
+ bool regionSampling, bool isSecure, bool seamlessTransition) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
ScreenCaptureResults captureResults;
- auto displayState = std::optional{display->getCompositionDisplay()->getState()};
+ const auto& state = display->getCompositionDisplay()->getState();
auto layers = getLayerSnapshotsFn();
- return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling,
+ SurfaceFlinger::ScreenshotArgs screenshotArgs;
+ screenshotArgs.captureTypeVariant = display;
+ screenshotArgs.displayIdVariant = std::nullopt;
+ screenshotArgs.sourceCrop = sourceCrop;
+ screenshotArgs.reqSize = sourceCrop.getSize();
+ screenshotArgs.dataspace = dataspace;
+ screenshotArgs.isSecure = isSecure;
+ screenshotArgs.seamlessTransition = seamlessTransition;
+ screenshotArgs.displayBrightnessNits = state.displayBrightnessNits;
+ screenshotArgs.sdrWhitePointNits = state.sdrWhitePointNits;
+ screenshotArgs.renderIntent = state.renderIntent;
+ screenshotArgs.colorMode = state.colorMode;
+
+ return mFlinger->renderScreenImpl(screenshotArgs, buffer, regionSampling,
false /* grayscale */, false /* isProtected */,
- captureResults, displayState, layers);
+ captureResults, layers);
}
auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
@@ -500,21 +519,11 @@
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
- auto setTransactionState(
- const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
- bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
- uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
- return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
- inputWindowCommands, desiredPresentTime,
- isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
- listenerCallbacks, transactionId,
- mergedTransactionIds);
+ auto setTransactionState(TransactionState&& state) {
+ return mFlinger->setTransactionState(std::move(state));
}
- auto setTransactionStateInternal(TransactionState& transaction) {
+ auto setTransactionStateInternal(QueuedTransactionState& transaction) {
return FTL_FAKE_GUARD(kMainThreadContext,
mFlinger->mTransactionHandler.queueTransaction(
std::move(transaction)));
@@ -564,7 +573,7 @@
}
sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken,
- VirtualDisplayId displayId,
+ GpuVirtualDisplayId displayId,
float requestedRefreshRate) {
constexpr ui::Size kResolution = {1080, 1920};
auto compositionDisplay = compositionengine::impl::
@@ -701,6 +710,7 @@
}
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
+ auto& mutableMaxAcquiredBuffers() { return SurfaceFlinger::maxAcquiredBuffersOpt; }
auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS {
return mFlinger->mLayerSnapshotBuilder;
}
@@ -771,7 +781,8 @@
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
mFlinger->mScheduler.reset();
- mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
+ mFlinger->mHWComposer = std::unique_ptr<HWComposer>();
+ mFlinger->mCompositionEngine->setHwComposer(mFlinger->mHWComposer.get());
mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
mFlinger->mTransactionTracing.reset();
@@ -806,9 +817,11 @@
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
- FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+ FakeHwcDisplayInjector(DisplayIdVariant displayIdVariant, hal::DisplayType hwcDisplayType,
bool isPrimary)
- : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
+ : mDisplayIdVariant(displayIdVariant),
+ mHwcDisplayType(hwcDisplayType),
+ mIsPrimary(isPrimary) {}
auto& setHwcDisplayId(hal::HWDisplayId displayId) {
mHwcDisplayId = displayId;
@@ -873,7 +886,9 @@
display->setPowerMode(mPowerMode);
- flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
+ const auto halDisplayId = asHalDisplayId(mDisplayIdVariant);
+ ASSERT_TRUE(halDisplayId);
+ flinger->mutableHwcDisplayData()[*halDisplayId].hwcDisplay = std::move(display);
EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
.WillRepeatedly(
@@ -911,9 +926,10 @@
DoAll(SetArgPointee<3>(mConfigGroup), Return(hal::Error::NONE)));
if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
- const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
- LOG_ALWAYS_FATAL_IF(!physicalId);
- flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
+ const auto physicalDisplayId = asPhysicalDisplayId(mDisplayIdVariant);
+ ASSERT_TRUE(physicalDisplayId);
+ flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId,
+ *physicalDisplayId);
if (mIsPrimary) {
flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
} else {
@@ -926,7 +942,7 @@
}
private:
- const HalDisplayId mDisplayId;
+ const DisplayIdVariant mDisplayIdVariant;
const hal::DisplayType mHwcDisplayType;
const bool mIsPrimary;
@@ -947,11 +963,13 @@
FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
std::shared_ptr<compositionengine::Display> display,
std::optional<ui::DisplayConnectionType> connectionType,
+ std::optional<uint8_t> port,
std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary)
: mFlinger(flinger),
mCreationArgs(flinger.mFlinger, flinger.mFlinger->getHwComposer(), mDisplayToken,
display),
mConnectionType(connectionType),
+ mPort(port),
mHwcDisplayId(hwcDisplayId) {
mCreationArgs.isPrimary = isPrimary;
mCreationArgs.initialPowerMode = hal::PowerMode::ON;
@@ -960,8 +978,8 @@
sp<IBinder> token() const { return mDisplayToken; }
auto physicalDisplay() const {
- return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId())
- .and_then(&PhysicalDisplayId::tryCast)
+ return mCreationArgs.compositionDisplay->getDisplayIdVariant()
+ .and_then(asPhysicalDisplayId)
.and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays()));
}
@@ -1059,7 +1077,9 @@
DisplayDeviceState state;
state.isSecure = mCreationArgs.isSecure;
- if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+ if (const auto physicalId =
+ mCreationArgs.compositionDisplay->getDisplayIdVariant().and_then(
+ asPhysicalDisplayId)) {
LOG_ALWAYS_FATAL_IF(!mConnectionType);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
@@ -1100,11 +1120,12 @@
.hwcDisplayId = *mHwcDisplayId,
.activeMode = activeModeOpt->get()};
- const auto it = mFlinger.mutablePhysicalDisplays()
- .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
- *mConnectionType, std::move(modes),
- ui::ColorModes(), std::nullopt)
- .first;
+ const auto it =
+ mFlinger.mutablePhysicalDisplays()
+ .emplace_or_replace(*physicalId, mDisplayToken, *physicalId, *mPort,
+ *mConnectionType, std::move(modes),
+ ui::ColorModes(), std::nullopt)
+ .first;
mFlinger.mutableDisplayModeController()
.registerDisplay(*physicalId, it->second.snapshot(),
@@ -1138,6 +1159,7 @@
DisplayModeId mActiveModeId;
bool mSchedulerRegistration = true;
const std::optional<ui::DisplayConnectionType> mConnectionType;
+ const std::optional<uint8_t> mPort;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
};
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 1e8cd0a..1395fb6 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,6 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "TransactionApplicationTest"
+#include <cstdint>
+
#include <binder/Binder.h>
#include <common/test/FlagUtils.h>
#include <compositionengine/Display.h>
@@ -33,8 +35,8 @@
#include <vector>
#include "FrontEnd/TransactionHandler.h"
+#include "QueuedTransactionState.h"
#include "TestableSurfaceFlinger.h"
-#include "TransactionState.h"
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -69,38 +71,32 @@
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
- struct TransactionInfo {
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags = 0;
- sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- InputWindowCommands inputWindowCommands;
- int64_t desiredPresentTime = 0;
- bool isAutoTimestamp = true;
- FrameTimelineInfo frameTimelineInfo;
- std::vector<client_cache_t> uncacheBuffers;
- uint64_t id = static_cast<uint64_t>(-1);
- std::vector<uint64_t> mergedTransactionIds;
- static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
+ struct TransactionInfo : public TransactionState {
+ TransactionInfo() {
+ mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ mId = static_cast<uint64_t>(-1);
+ }
};
- void checkEqual(TransactionInfo info, TransactionState state) {
- EXPECT_EQ(0u, info.states.size());
+ void checkEqual(const TransactionInfo& info, const QueuedTransactionState& state) {
+ EXPECT_EQ(0u, info.mComposerStates.size());
EXPECT_EQ(0u, state.states.size());
- EXPECT_EQ(0u, info.displays.size());
+ EXPECT_EQ(0u, info.mDisplayStates.size());
EXPECT_EQ(0u, state.displays.size());
- EXPECT_EQ(info.flags, state.flags);
- EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
+ EXPECT_EQ(info.mFlags, state.flags);
+ EXPECT_EQ(info.mDesiredPresentTime, state.desiredPresentTime);
}
void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
mTransactionNumber++;
- transaction.flags |= flags;
- transaction.desiredPresentTime = desiredPresentTime;
- transaction.isAutoTimestamp = isAutoTimestamp;
- transaction.frameTimelineInfo = frameTimelineInfo;
+ transaction.mFlags |= flags;
+ transaction.mDesiredPresentTime = desiredPresentTime;
+ transaction.mIsAutoTimestamp = isAutoTimestamp;
+ transaction.mFrameTimelineInfo = frameTimelineInfo;
+ transaction.mHasListenerCallbacks = mHasListenerCallbacks;
+ transaction.mListenerCallbacks = mCallbacks;
}
void NotPlacedOnTransactionQueue(uint32_t flags) {
@@ -111,12 +107,7 @@
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
FrameTimelineInfo{});
nsecs_t applicationTime = systemTime();
- mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id, transaction.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transaction));
// If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
// SF to commit the transaction. If this is animation, it should not time out waiting.
@@ -138,12 +129,7 @@
setupSingle(transaction, flags, /*desiredPresentTime*/ time + s2ns(1), false,
FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transaction.id, transaction.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transaction));
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
@@ -169,12 +155,7 @@
/*isAutoTimestamp*/ true, FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
- transactionA.displays, transactionA.flags,
- transactionA.applyToken, transactionA.inputWindowCommands,
- transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
- transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionA.id, transactionA.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionA));
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -184,12 +165,7 @@
mFlinger.flushTransactionQueues();
applicationSentTime = systemTime();
- mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
- transactionB.displays, transactionB.flags,
- transactionB.applyToken, transactionB.inputWindowCommands,
- transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
- transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
- transactionB.id, transactionB.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionB));
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -222,12 +198,7 @@
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
FrameTimelineInfo{});
- mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
- transactionA.displays, transactionA.flags, transactionA.applyToken,
- transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id,
- transactionA.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionA));
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -243,12 +214,7 @@
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
FrameTimelineInfo{});
- mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
- transactionA.displays, transactionA.flags, transactionA.applyToken,
- transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
- mHasListenerCallbacks, mCallbacks, transactionA.id,
- transactionA.mergedTransactionIds);
+ mFlinger.setTransactionState(std::move(transactionA));
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -257,12 +223,10 @@
// transaction here (sending a null applyToken to fake it as from a
// different process) to re-query and reset the cached expected present time
TransactionInfo empty;
- empty.applyToken = sp<IBinder>();
- mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
- empty.applyToken, empty.inputWindowCommands,
- empty.desiredPresentTime, empty.isAutoTimestamp,
- empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id,
- empty.mergedTransactionIds);
+ empty.mApplyToken = sp<IBinder>();
+ empty.mHasListenerCallbacks = mHasListenerCallbacks;
+ empty.mListenerCallbacks = mCallbacks;
+ mFlinger.setTransactionState(std::move(empty));
// flush transaction queue should flush as desiredPresentTime has
// passed
@@ -318,7 +282,7 @@
auto applyToken2 = sp<BBinder>::make();
// Transaction 1 has a buffer with an unfired fence. It should not be ready to be applied.
- TransactionState transaction1;
+ QueuedTransactionState transaction1;
transaction1.applyToken = applyToken1;
transaction1.id = 42069;
transaction1.states.emplace_back();
@@ -340,7 +304,7 @@
transaction1.isAutoTimestamp = true;
// Transaction 2 should be ready to be applied.
- TransactionState transaction2;
+ QueuedTransactionState transaction2;
transaction2.applyToken = applyToken2;
transaction2.id = 2;
transaction2.isAutoTimestamp = true;
@@ -406,9 +370,9 @@
const auto kFrameTimelineInfo = FrameTimelineInfo{};
setupSingle(transaction, kFlags, kDesiredPresentTime, kIsAutoTimestamp, kFrameTimelineInfo);
- transaction.applyToken = applyToken;
+ transaction.mApplyToken = applyToken;
for (const auto& state : states) {
- transaction.states.push_back(state);
+ transaction.mComposerStates.push_back(state);
}
return transaction;
@@ -419,8 +383,8 @@
EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
std::unordered_set<uint32_t> createdLayers;
- for (auto transaction : transactions) {
- for (auto& state : transaction.states) {
+ for (auto& transaction : transactions) {
+ for (auto& state : transaction.mComposerStates) {
auto layerId = static_cast<uint32_t>(state.state.layerId);
if (createdLayers.find(layerId) == createdLayers.end()) {
mFlinger.addLayer(layerId);
@@ -434,8 +398,8 @@
for (auto transaction : transactions) {
std::vector<ResolvedComposerState> resolvedStates;
- resolvedStates.reserve(transaction.states.size());
- for (auto& state : transaction.states) {
+ resolvedStates.reserve(transaction.mComposerStates.size());
+ for (auto& state : transaction.mComposerStates) {
ResolvedComposerState resolvedState;
resolvedState.state = std::move(state.state);
resolvedState.externalTexture =
@@ -446,15 +410,9 @@
resolvedStates.emplace_back(resolvedState);
}
- TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
- transaction.displays, transaction.flags,
- transaction.applyToken,
- transaction.inputWindowCommands,
- transaction.desiredPresentTime,
- transaction.isAutoTimestamp, {}, systemTime(),
- mHasListenerCallbacks, mCallbacks, getpid(),
- static_cast<int>(getuid()), transaction.id,
- transaction.mergedTransactionIds);
+ QueuedTransactionState transactionState(std::move(transaction),
+ std::move(resolvedStates), {}, systemTime(),
+ getpid(), static_cast<int>(getuid()));
mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
@@ -955,12 +913,12 @@
TEST(TransactionHandlerTest, QueueTransaction) {
TransactionHandler handler;
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.applyToken = sp<BBinder>::make();
transaction.id = 42;
handler.queueTransaction(std::move(transaction));
handler.collectTransactions();
- std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+ std::vector<QueuedTransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index af02330..b36ad21 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -30,7 +30,7 @@
TEST(TransactionProtoParserTest, parse) {
const sp<IBinder> displayHandle = sp<BBinder>::make();
- TransactionState t1;
+ QueuedTransactionState t1;
t1.originPid = 1;
t1.originUid = 2;
t1.frameTimelineInfo.vsyncId = 3;
@@ -66,7 +66,7 @@
display.token = nullptr;
}
display.width = 85;
- t1.displays.add(display);
+ t1.displays.push_back(display);
}
class TestMapper : public TransactionProtoParser::FlingerDataMapper {
@@ -86,7 +86,7 @@
TransactionProtoParser parser(std::make_unique<TestMapper>(displayHandle));
perfetto::protos::TransactionState proto = parser.toProto(t1);
- TransactionState t2 = parser.fromProto(proto);
+ QueuedTransactionState t2 = parser.fromProto(proto);
ASSERT_EQ(t1.originPid, t2.originPid);
ASSERT_EQ(t1.originUid, t2.originUid);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index f8f08c7..036d8c4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -49,19 +49,19 @@
void queueAndCommitTransaction(int64_t vsyncId) {
frontend::Update update;
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = static_cast<uint64_t>(vsyncId * 3);
transaction.originUid = 1;
transaction.originPid = 2;
mTracing.addQueuedTransaction(transaction);
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
update.transactions.emplace_back(transaction);
mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
flush();
}
void verifyEntry(const perfetto::protos::TransactionTraceEntry& actualProto,
- const std::vector<TransactionState>& expectedTransactions,
+ const std::vector<QueuedTransactionState>& expectedTransactions,
int64_t expectedVsyncId) {
EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
ASSERT_EQ(actualProto.transactions().size(),
@@ -92,10 +92,10 @@
};
TEST_F(TransactionTracingTest, addTransactions) {
- std::vector<TransactionState> transactions;
+ std::vector<QueuedTransactionState> transactions;
transactions.reserve(100);
for (uint64_t i = 0; i < 100; i++) {
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = i;
transaction.originPid = static_cast<int32_t>(i);
transaction.mergedTransactionIds = std::vector<uint64_t>{i + 100, i + 102};
@@ -108,13 +108,13 @@
int64_t firstTransactionSetVsyncId = 42;
frontend::Update firstUpdate;
firstUpdate.transactions =
- std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+ std::vector<QueuedTransactionState>(transactions.begin() + 50, transactions.end());
mTracing.addCommittedTransactions(firstTransactionSetVsyncId, 0, firstUpdate, {}, false);
int64_t secondTransactionSetVsyncId = 43;
frontend::Update secondUpdate;
secondUpdate.transactions =
- std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
+ std::vector<QueuedTransactionState>(transactions.begin(), transactions.begin() + 50);
mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
flush();
@@ -140,7 +140,7 @@
getLayerCreationArgs(mChildLayerId, mParentLayerId,
/*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
/*addToRoot=*/true));
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
layerState.layerId = mParentLayerId;
@@ -164,7 +164,7 @@
// add transactions that modify the layer state further so we can test that layer state
// gets merged
{
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = 51;
ResolvedComposerState layerState;
layerState.layerId = mParentLayerId;
@@ -278,7 +278,7 @@
/*layerIdToMirror=*/mLayerId, /*flags=*/0,
/*addToRoot=*/false));
- TransactionState transaction;
+ QueuedTransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
layerState.layerId = mLayerId;
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 918107d..ccf6a9c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -304,6 +304,53 @@
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
}
+TEST_F(VSyncPredictorTest, recoverAfterDriftedVSyncAreReplacedWithCorrectVSync) {
+ SET_FLAG_FOR_TEST(flags::vsync_predictor_recovery, true);
+ auto constexpr idealPeriodNs = 4166666;
+ auto constexpr minFrameIntervalNs = 8333333;
+ auto constexpr idealPeriod = Fps::fromPeriodNsecs(idealPeriodNs);
+ auto constexpr minFrameRate = Fps::fromPeriodNsecs(minFrameIntervalNs);
+ hal::VrrConfig vrrConfig{.minFrameIntervalNs = minFrameIntervalNs};
+ ftl::NonNull<DisplayModePtr> mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), idealPeriod, vrrConfig));
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ true);
+ // Curated list of VSyncs that causes the VSync drift.
+ std::vector<nsecs_t> const simulatedVsyncs{74473665741, 74481774375, 74489911818, 74497993491,
+ 74506000833, 74510002150, 74513904390, 74517748707,
+ 74521550947, 74525383187, 74529165427, 74533067667,
+ 74536751484, 74540653724, 74544282649, 74548084889,
+ 74551917129, 74555699369, 74559601609, 74563601611,
+ 74567503851, 74571358168, 74575260408, 74578889333,
+ 74582691573, 74586523813, 74590306053, 74593589870,
+ 74597492110, 74601121035, 74604923275, 74608755515,
+ 74612537755, 74616166680, 74619795605, 74623424530,
+ 74627043455, 74630645695, 74634245935, 74637778175,
+ 74641291992, 74644794232, 74648275157, 74651575397,
+ 74654807637, 74658007877, 74661176117, 74664345357};
+ for (auto const& timestamp : simulatedVsyncs) {
+ vrrTracker.addVsyncTimestamp(timestamp);
+ }
+ auto slope = vrrTracker.getVSyncPredictionModel().slope;
+ // Without using the idealPeriod for the calculation of the VSync predictor mode the
+ // the slope would be 3343031
+ EXPECT_THAT(slope, IsCloseTo(4347805, mMaxRoundingError));
+ EXPECT_FALSE(vrrTracker.needsMoreSamples());
+
+ auto lastVsync = 74664345357;
+ // Add valid VSyncs to replace the drifted VSyncs
+ for (int i = 0; i <= kHistorySize; i++) {
+ lastVsync += minFrameIntervalNs;
+ EXPECT_TRUE(vrrTracker.addVsyncTimestamp(lastVsync));
+ }
+ EXPECT_FALSE(vrrTracker.needsMoreSamples());
+ slope = vrrTracker.getVSyncPredictionModel().slope;
+ // Corrected slop is closer to the idealPeriod
+ // when valid vsync are inserted otherwise this would still be 3349673
+ EXPECT_THAT(slope, IsCloseTo(idealPeriodNs, mMaxRoundingError));
+}
+
TEST_F(VSyncPredictorTest, handlesVsyncChange) {
auto const fastPeriod = 100;
auto const fastTimeBase = 100;
@@ -402,11 +449,7 @@
std::vector<nsecs_t> const simulatedVsyncs{
158929578733000,
158929306806205, // oldest TS in ringbuffer
- 158929650879052,
- 158929661969209,
- 158929684198847,
- 158929695268171,
- 158929706370359,
+ 158929650879052, 158929661969209, 158929684198847, 158929695268171, 158929706370359,
};
auto const idealPeriod = 11111111;
auto const expectedPeriod = 11113919;
@@ -421,9 +464,9 @@
EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
- // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
- // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
- // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+ // (timePoint - oldestTS) % expectedPeriod works out to be: 10702663
+ // (timePoint - oldestTS) / expectedPeriod works out to be: 37.96
+ // so failure to account for the offset will floor the ordinal to 37, which was in the past.
auto const timePoint = 158929728723871;
auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
EXPECT_THAT(prediction, Ge(timePoint));
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 21ee071..3589553 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -22,6 +22,8 @@
#include <chrono>
#include <thread>
+#include <scheduler/Time.h>
+
#include "Scheduler/VsyncConfiguration.h"
using namespace testing;
@@ -39,6 +41,10 @@
: impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration,
appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration,
hwcMinWorkDuration) {}
+
+ TestableWorkDuration(Fps currentFps, Duration minSfDuration, Duration maxSfDuration,
+ Duration appDuration)
+ : impl::WorkDuration(currentFps, minSfDuration, maxSfDuration, appDuration) {}
};
class WorkDurationTest : public testing::Test {
@@ -168,6 +174,35 @@
EXPECT_EQ(mWorkDuration.getCurrentConfigs().hwcMinWorkDuration, 1234ns);
}
+TEST_F(WorkDurationTest, workDurationIsARange) {
+ const Duration minSfDuration = Duration::fromNs(10'500'000);
+ const Duration maxSfDuration = Duration::fromNs(20'500'000);
+ const Duration appDuration = Duration::fromNs(16'000'000);
+ const TestableWorkDuration workDuration{60_Hz, minSfDuration, maxSfDuration, appDuration};
+
+ auto currentOffsets = workDuration.getCurrentConfigs();
+ auto offsets = workDuration.getConfigsForRefreshRate(60_Hz);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
+ EXPECT_EQ(offsets.late.appOffset, 6'833'334);
+
+ EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns);
+ EXPECT_EQ(offsets.late.appWorkDuration, 16'000'000ns);
+
+ EXPECT_EQ(offsets.early.sfOffset, -3'833'333);
+ EXPECT_EQ(offsets.early.appOffset, 13'500'001);
+
+ EXPECT_EQ(offsets.early.sfWorkDuration, 20'500'000ns);
+ EXPECT_EQ(offsets.early.appWorkDuration, 16'000'000ns);
+
+ EXPECT_EQ(offsets.earlyGpu.sfOffset, -3'833'333);
+ EXPECT_EQ(offsets.earlyGpu.appOffset, 13'500'001);
+
+ EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 20'500'000ns);
+ EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'000'000ns);
+}
+
class TestablePhaseOffsets : public impl::PhaseOffsets {
public:
TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 0d5266e..00e4cc6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -190,6 +190,13 @@
MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*));
MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id));
MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id));
+ MOCK_METHOD(Error, startHdcpNegotiation,
+ (Display, const aidl::android::hardware::drm::HdcpLevels& levels));
+ MOCK_METHOD(Error, getLuts,
+ (Display, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*));
+ MOCK_METHOD4(getLayerPresentFences,
+ Error(Display, std::vector<Layer>*, std::vector<int>*, std::vector<int64_t>*));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index ec065a7..a20b9e1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -116,6 +116,12 @@
MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override));
MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
(override));
+ MOCK_METHOD(hal::Error, startHdcpNegotiation,
+ (const aidl::android::hardware::drm::HdcpLevels& levels), (override));
+ MOCK_METHOD(hal::Error, getLuts,
+ (const std::vector<android::sp<android::GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*),
+ (override));
};
class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
index 88f83d2..449c45b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -44,7 +44,8 @@
MOCK_METHOD(bool, allocateVirtualDisplay, (HalVirtualDisplayId, ui::Size, ui::PixelFormat*),
(override));
MOCK_METHOD(void, allocatePhysicalDisplay,
- (hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>), (override));
+ (hal::HWDisplayId, PhysicalDisplayId, uint8_t port, std::optional<ui::Size>),
+ (override));
MOCK_METHOD(std::shared_ptr<HWC2::Layer>, createLayer, (HalDisplayId), (override));
MOCK_METHOD(status_t, getDeviceCompositionChanges,
@@ -81,7 +82,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));
@@ -151,6 +152,11 @@
MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId));
MOCK_METHOD(status_t, setDisplayPictureProfileHandle,
(PhysicalDisplayId, const PictureProfileHandle&));
+ MOCK_METHOD(status_t, startHdcpNegotiation,
+ (PhysicalDisplayId, const aidl::android::hardware::drm::HdcpLevels&));
+ MOCK_METHOD(status_t, getLuts,
+ (PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&,
+ std::vector<aidl::android::hardware::graphics::composer3::Luts>*));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 3036fec..cce4d2a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -55,7 +55,6 @@
MOCK_METHOD(void, onHdcpLevelsChanged,
(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
(override));
- MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
index 5c4512a..5abee16 100644
--- a/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
@@ -63,6 +63,12 @@
MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override));
MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
+ MOCK_METHOD(std::shared_ptr<SessionManager>, getSessionManager, (), (override));
+ MOCK_METHOD(sp<IBinder>, getOrCreateSessionManagerForBinder, (uid_t uid), (override));
+ MOCK_METHOD(void, setQueuedWorkload, (ftl::Flags<Workload> workload), (override));
+ MOCK_METHOD(void, setScreenshotWorkload, (), (override));
+ MOCK_METHOD(void, setCommittedWorkload, (ftl::Flags<Workload> workload), (override));
+ MOCK_METHOD(void, setCompositedWorkload, (ftl::Flags<Workload> workload), (override));
};
} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 0bedcd1..02c3ecd 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,15 +15,23 @@
*/
#pragma once
+#include <android-base/file.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
#include <gui/AidlUtil.h>
#include <gui/SyncScreenCaptureListener.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/FenceResult.h>
+#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/String8.h>
#include <functional>
#include "TransactionUtils.h"
+#include <filesystem>
+#include <fstream>
+
namespace android {
using gui::aidl_utils::statusTFromBinderStatus;
@@ -174,6 +182,146 @@
}
}
+ static void writePng(const std::filesystem::path& path, const void* pixels, uint32_t width,
+ uint32_t height, uint32_t stride) {
+ AndroidBitmapInfo info{
+ .width = width,
+ .height = height,
+ .stride = stride,
+ .format = ANDROID_BITMAP_FORMAT_RGBA_8888,
+ .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE,
+ };
+
+ std::ofstream file(path, std::ios::binary);
+ ASSERT_TRUE(file.is_open());
+
+ auto writeFunc = [](void* filePtr, const void* data, size_t size) -> bool {
+ auto file = reinterpret_cast<std::ofstream*>(filePtr);
+ file->write(reinterpret_cast<const char*>(data), size);
+ return file->good();
+ };
+
+ int compressResult = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels,
+ ANDROID_BITMAP_COMPRESS_FORMAT_PNG,
+ /*(ignored) quality=*/100, &file, writeFunc);
+ ASSERT_EQ(compressResult, ANDROID_BITMAP_RESULT_SUCCESS);
+ file.close();
+ }
+
+ static void readImage(const std::filesystem::path& filename, std::vector<uint8_t>& outBytes,
+ int& outWidth, int& outHeight) {
+ std::ifstream file(filename, std::ios::binary | std::ios::ate);
+ ASSERT_TRUE(file.is_open()) << "Failed to open " << filename;
+
+ size_t fileSize = file.tellg();
+ file.seekg(0, std::ios::beg);
+ std::vector<char> fileData(fileSize);
+ file.read(fileData.data(), fileSize);
+ file.close();
+
+ AImageDecoder* decoder = nullptr;
+ int createResult = AImageDecoder_createFromBuffer(fileData.data(), fileSize, &decoder);
+
+ ASSERT_EQ(createResult, ANDROID_IMAGE_DECODER_SUCCESS);
+
+ const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder);
+ outWidth = AImageDecoderHeaderInfo_getWidth(headerInfo);
+ outHeight = AImageDecoderHeaderInfo_getHeight(headerInfo);
+ int32_t format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo);
+ ASSERT_EQ(format, ANDROID_BITMAP_FORMAT_RGBA_8888);
+
+ size_t stride = outWidth * 4; // Assuming RGBA format
+ size_t bufferSize = stride * outHeight;
+
+ outBytes.resize(bufferSize);
+ int decodeResult = AImageDecoder_decodeImage(decoder, outBytes.data(), stride, bufferSize);
+ ASSERT_EQ(decodeResult, ANDROID_IMAGE_DECODER_SUCCESS);
+ AImageDecoder_delete(decoder);
+ }
+
+ static void writeGraphicBufferToPng(const std::string& path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path.c_str(), O_WRONLY | O_CREAT, S_IWUSR)};
+ ASSERT_GE(fd.get(), 0);
+
+ void* pixels = nullptr;
+ int32_t stride = 0;
+ auto lockStatus = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ ASSERT_GE(lockStatus, 0);
+
+ writePng(path, pixels, buffer->getWidth(), buffer->getHeight(), stride);
+
+ auto unlockStatus = buffer->unlock();
+ ASSERT_GE(unlockStatus, 0);
+ }
+
+ // Tries to read an image from executable directory
+ // If the test fails, the screenshot is written to $TMPDIR
+ void expectBufferMatchesImageFromFile(const Rect& rect,
+ const std::filesystem::path& pathRelativeToExeDir) {
+ ASSERT_NE(nullptr, mOutBuffer);
+ ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+
+ int bufferWidth = int32_t(mOutBuffer->getWidth());
+ int bufferHeight = int32_t(mOutBuffer->getHeight());
+ int bufferStride = mOutBuffer->getStride() * 4;
+
+ std::vector<uint8_t> imagePixels;
+ int imageWidth;
+ int imageHeight;
+ readImage(android::base::GetExecutableDirectory() / pathRelativeToExeDir, imagePixels,
+ imageWidth, imageHeight);
+ int imageStride = 4 * imageWidth;
+
+ ASSERT_TRUE(rect.isValid());
+
+ ASSERT_GE(rect.left, 0);
+ ASSERT_GE(rect.bottom, 0);
+
+ ASSERT_LE(rect.right, bufferWidth);
+ ASSERT_LE(rect.bottom, bufferHeight);
+
+ ASSERT_LE(rect.right, imageWidth);
+ ASSERT_LE(rect.bottom, imageHeight);
+
+ int tolerance = 4; // arbitrary
+ for (int32_t y = rect.top; y < rect.bottom; y++) {
+ for (int32_t x = rect.left; x < rect.right; x++) {
+ const uint8_t* bufferPixel = mPixels + y * bufferStride + x * 4;
+ const uint8_t* imagePixel =
+ imagePixels.data() + (y - rect.top) * imageStride + (x - rect.left) * 4;
+
+ int dr = bufferPixel[0] - imagePixel[0];
+ int dg = bufferPixel[1] - imagePixel[1];
+ int db = bufferPixel[2] - imagePixel[2];
+ int da = bufferPixel[3] - imagePixel[3];
+ int dist = std::abs(dr) + std::abs(dg) + std::abs(db) + std::abs(da);
+
+ bool pixelMatches = dist < tolerance;
+
+ if (!pixelMatches) {
+ std::filesystem::path outFilename = pathRelativeToExeDir.filename();
+ outFilename.replace_extension();
+ outFilename += "_actual.png";
+ std::filesystem::path outPath = std::filesystem::temp_directory_path() /
+ "SurfaceFlinger_test_screenshots" / outFilename;
+ writeGraphicBufferToPng(outPath, mOutBuffer);
+
+ ASSERT_TRUE(pixelMatches)
+ << String8::format("pixel @ (%3d, %3d): "
+ "expected [%3d, %3d, %3d, %3d], got [%3d, %3d, %3d, "
+ "%3d], "
+ "wrote screenshot to '%s'",
+ x, y, imagePixel[0], imagePixel[1], imagePixel[2],
+ imagePixel[3], bufferPixel[0], bufferPixel[1],
+ bufferPixel[2], bufferPixel[3], outPath.c_str())
+ .c_str();
+ return;
+ }
+ }
+ }
+ }
+
Color getPixelColor(uint32_t x, uint32_t y) {
if (!mOutBuffer || mOutBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_RGBA_8888) {
return {0, 0, 0, 0};
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
index 8b4a6be..77a68d9 100644
--- a/services/surfaceflinger/tests/vsync/vsync.cpp
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -41,7 +41,7 @@
while ((n = q->getEvents(buffer, 1)) > 0) {
for (int i=0 ; i<n ; i++) {
- if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ if (buffer[i].header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) {
printf("event vsync: count=%d\t", buffer[i].vsync.count);
}
if (oldTimeStamp) {
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4735ae5..4b12cc2 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -26,6 +26,7 @@
srcs: [
"VibratorCallbackScheduler.cpp",
+ "VibratorController.cpp",
"VibratorHalController.cpp",
"VibratorHalWrapper.cpp",
"VibratorManagerHalController.cpp",
@@ -41,22 +42,17 @@
},
shared_libs: [
+ "android.hardware.vibrator-V3-ndk",
"libbinder_ndk",
- "libhidlbase",
"liblog",
"libutils",
- "android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
],
cflags: [
"-Wall",
"-Werror",
- "-Wunused",
"-Wunreachable-code",
+ "-Wunused",
],
local_include_dirs: ["include"],
diff --git a/services/vibratorservice/VibratorController.cpp b/services/vibratorservice/VibratorController.cpp
new file mode 100644
index 0000000..21924e9
--- /dev/null
+++ b/services/vibratorservice/VibratorController.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "VibratorController"
+
+#ifndef qDoWithRetries
+#define qDoWithRetries(op) doWithRetries(op, __FUNCTION__)
+#endif
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/Log.h>
+
+#include <vibratorservice/VibratorController.h>
+
+using ::aidl::android::hardware::vibrator::Effect;
+using ::aidl::android::hardware::vibrator::EffectStrength;
+using ::aidl::android::hardware::vibrator::IVibrator;
+
+using Status = ::ndk::ScopedAStatus;
+
+using namespace std::placeholders;
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+inline bool isStatusUnsupported(const Status& status) {
+ // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported.
+ return status.getStatus() == STATUS_UNKNOWN_TRANSACTION ||
+ status.getExceptionCode() == EX_UNSUPPORTED_OPERATION;
+}
+
+inline bool isStatusTransactionFailed(const Status& status) {
+ // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported.
+ return status.getStatus() != STATUS_UNKNOWN_TRANSACTION &&
+ status.getExceptionCode() == EX_TRANSACTION_FAILED;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool VibratorProvider::isDeclared() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mIsDeclared.has_value()) {
+ return *mIsDeclared;
+ }
+
+ bool isDeclared = AServiceManager_isDeclared(mServiceName.c_str());
+ if (!isDeclared) {
+ ALOGV("Vibrator HAL service not declared.");
+ }
+
+ mIsDeclared.emplace(isDeclared);
+ return isDeclared;
+}
+
+std::shared_ptr<IVibrator> VibratorProvider::waitForVibrator() {
+ if (!isDeclared()) {
+ return nullptr;
+ }
+
+ auto vibrator = IVibrator::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(mServiceName.c_str())));
+ if (vibrator) {
+ ALOGV("Successfully connected to Vibrator HAL service.");
+ } else {
+ ALOGE("Error connecting to declared Vibrator HAL service.");
+ }
+
+ return vibrator;
+}
+
+std::shared_ptr<IVibrator> VibratorProvider::checkForVibrator() {
+ if (!isDeclared()) {
+ return nullptr;
+ }
+
+ auto vibrator = IVibrator::fromBinder(
+ ndk::SpAIBinder(AServiceManager_checkService(mServiceName.c_str())));
+ if (vibrator) {
+ ALOGV("Successfully reconnected to Vibrator HAL service.");
+ } else {
+ ALOGE("Error reconnecting to declared Vibrator HAL service.");
+ }
+
+ return vibrator;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+bool VibratorController::init() {
+ if (!mVibratorProvider->isDeclared()) {
+ return false;
+ }
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mVibrator == nullptr) {
+ mVibrator = mVibratorProvider->waitForVibrator();
+ }
+ return mVibratorProvider->isDeclared();
+}
+
+Status VibratorController::off() {
+ return qDoWithRetries(std::bind(&IVibrator::off, _1));
+}
+
+Status VibratorController::setAmplitude(float amplitude) {
+ return qDoWithRetries(std::bind(&IVibrator::setAmplitude, _1, amplitude));
+}
+
+Status VibratorController::setExternalControl(bool enabled) {
+ return qDoWithRetries(std::bind(&IVibrator::setExternalControl, _1, enabled));
+}
+
+Status VibratorController::alwaysOnEnable(int32_t id, const Effect& effect,
+ const EffectStrength& strength) {
+ return qDoWithRetries(std::bind(&IVibrator::alwaysOnEnable, _1, id, effect, strength));
+}
+
+Status VibratorController::alwaysOnDisable(int32_t id) {
+ return qDoWithRetries(std::bind(&IVibrator::alwaysOnDisable, _1, id));
+}
+
+// -------------------------------------------------------------------------------------------------
+
+std::shared_ptr<IVibrator> VibratorController::reconnectToVibrator() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVibrator = mVibratorProvider->checkForVibrator();
+ return mVibrator;
+}
+
+Status VibratorController::doWithRetries(const VibratorController::VibratorOp& op,
+ const char* logLabel) {
+ if (!init()) {
+ ALOGV("Skipped %s because Vibrator HAL is not declared", logLabel);
+ return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator not declared");
+ }
+ std::shared_ptr<IVibrator> vibrator;
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ vibrator = mVibrator;
+ }
+
+ if (!vibrator) {
+ ALOGE("Skipped %s because Vibrator HAL is declared but failed to load", logLabel);
+ return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE,
+ "IVibrator declared but failed to load");
+ }
+
+ auto status = doOnce(vibrator.get(), op, logLabel);
+ for (int i = 1; i < MAX_ATTEMPTS && isStatusTransactionFailed(status); i++) {
+ vibrator = reconnectToVibrator();
+ if (!vibrator) {
+ // Failed to reconnect to vibrator HAL after a transaction failed, skip retries.
+ break;
+ }
+ status = doOnce(vibrator.get(), op, logLabel);
+ }
+
+ return status;
+}
+
+Status VibratorController::doOnce(IVibrator* vibrator, const VibratorController::VibratorOp& op,
+ const char* logLabel) {
+ auto status = op(vibrator);
+ if (!status.isOk()) {
+ if (isStatusUnsupported(status)) {
+ ALOGV("Vibrator HAL %s is unsupported: %s", logLabel, status.getMessage());
+ } else {
+ ALOGE("Vibrator HAL %s failed: %s", logLabel, status.getMessage());
+ }
+ }
+ return status;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 283a5f0..302e3e1 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -18,8 +18,6 @@
#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/binder_manager.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <hardware/vibrator.h>
#include <utils/Log.h>
@@ -31,15 +29,10 @@
using aidl::android::hardware::vibrator::CompositePrimitive;
using aidl::android::hardware::vibrator::Effect;
using aidl::android::hardware::vibrator::EffectStrength;
+using aidl::android::hardware::vibrator::IVibrator;
using std::chrono::milliseconds;
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = aidl::android::hardware::vibrator;
-
namespace android {
namespace vibrator {
@@ -53,9 +46,9 @@
return nullptr;
}
- auto serviceName = std::string(Aidl::IVibrator::descriptor) + "/default";
+ auto serviceName = std::string(IVibrator::descriptor) + "/default";
if (AServiceManager_isDeclared(serviceName.c_str())) {
- std::shared_ptr<Aidl::IVibrator> hal = Aidl::IVibrator::fromBinder(
+ std::shared_ptr<IVibrator> hal = IVibrator::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
if (hal) {
ALOGV("Successfully connected to Vibrator HAL AIDL service.");
@@ -63,30 +56,9 @@
}
}
- sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService();
- if (halV1_0 == nullptr) {
- ALOGV("Vibrator HAL service not available.");
- gHalExists = false;
- return nullptr;
- }
-
- sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0);
- if (halV1_3) {
- ALOGV("Successfully connected to Vibrator HAL v1.3 service.");
- return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3);
- }
- sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0);
- if (halV1_2) {
- ALOGV("Successfully connected to Vibrator HAL v1.2 service.");
- return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2);
- }
- sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0);
- if (halV1_1) {
- ALOGV("Successfully connected to Vibrator HAL v1.1 service.");
- return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1);
- }
- ALOGV("Successfully connected to Vibrator HAL v1.0 service.");
- return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0);
+ ALOGV("Vibrator HAL service not available.");
+ gHalExists = false;
+ return nullptr;
}
// -------------------------------------------------------------------------------------------------
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 3ddc4f2..5d4c17d 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "VibratorHalWrapper"
#include <aidl/android/hardware/vibrator/IVibrator.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <hardware/vibrator.h>
#include <cmath>
@@ -33,32 +32,18 @@
using aidl::android::hardware::vibrator::Effect;
using aidl::android::hardware::vibrator::EffectStrength;
using aidl::android::hardware::vibrator::FrequencyAccelerationMapEntry;
+using aidl::android::hardware::vibrator::IVibrator;
using aidl::android::hardware::vibrator::PrimitivePwle;
using aidl::android::hardware::vibrator::VendorEffect;
using std::chrono::milliseconds;
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace Aidl = aidl::android::hardware::vibrator;
-
namespace android {
namespace vibrator {
// -------------------------------------------------------------------------------------------------
-template <class T>
-bool isStaticCastValid(Effect effect) {
- T castEffect = static_cast<T>(effect);
- auto iter = hardware::hidl_enum_range<T>();
- return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end());
-}
-
-// -------------------------------------------------------------------------------------------------
-
Info HalWrapper::getInfo() {
getCapabilities();
getPrimitiveDurations();
@@ -131,9 +116,10 @@
return HalResult<void>::unsupported();
}
-HalResult<void> HalWrapper::composePwleV2(const CompositePwleV2&, const std::function<void()>&) {
+HalResult<milliseconds> HalWrapper::composePwleV2(const CompositePwleV2&,
+ const std::function<void()>&) {
ALOGV("Skipped composePwleV2 because it's not available in Vibrator HAL");
- return HalResult<void>::unsupported();
+ return HalResult<milliseconds>::unsupported();
}
HalResult<Capabilities> HalWrapper::getCapabilities() {
@@ -260,7 +246,7 @@
if (!result.isOk()) {
return;
}
- std::shared_ptr<Aidl::IVibrator> newHandle = result.value();
+ std::shared_ptr<IVibrator> newHandle = result.value();
if (newHandle) {
std::lock_guard<std::mutex> lock(mHandleMutex);
mHandle = std::move(newHandle);
@@ -359,11 +345,18 @@
return HalResultFactory::fromStatus(getHal()->composePwle(primitives, cb));
}
-HalResult<void> AidlHalWrapper::composePwleV2(const CompositePwleV2& composite,
- const std::function<void()>& completionCallback) {
+HalResult<milliseconds> AidlHalWrapper::composePwleV2(
+ const CompositePwleV2& composite, const std::function<void()>& completionCallback) {
// This method should always support callbacks, so no need to double check.
auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
- return HalResultFactory::fromStatus(getHal()->composePwleV2(composite, cb));
+
+ milliseconds totalDuration(0);
+ for (const auto& primitive : composite.pwlePrimitives) {
+ totalDuration += milliseconds(primitive.timeMillis);
+ }
+
+ return HalResultFactory::fromStatus<milliseconds>(getHal()->composePwleV2(composite, cb),
+ totalDuration);
}
HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() {
@@ -506,219 +499,13 @@
frequencyToOutputAccelerationMap);
}
-std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
+std::shared_ptr<IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
}
// -------------------------------------------------------------------------------------------------
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::ping() {
- return HalResultFactory::fromReturn(getHal()->ping());
-}
-
-template <typename I>
-void HidlHalWrapper<I>::tryReconnect() {
- sp<I> newHandle = I::tryGetService();
- if (newHandle) {
- std::lock_guard<std::mutex> lock(mHandleMutex);
- mHandle = std::move(newHandle);
- }
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout,
- const std::function<void()>& completionCallback) {
- auto status = getHal()->on(timeout.count());
- auto ret = HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
- if (ret.isOk()) {
- mCallbackScheduler->schedule(completionCallback, timeout);
- }
- return ret;
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::off() {
- auto status = getHal()->off();
- return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::setAmplitude(float amplitude) {
- uint8_t amp = static_cast<uint8_t>(amplitude * std::numeric_limits<uint8_t>::max());
- auto status = getHal()->setAmplitude(amp);
- return HalResultFactory::fromStatus(status.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) {
- ALOGV("Skipped setExternalControl because Vibrator HAL does not support it");
- return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) {
- ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available");
- return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) {
- ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available");
- return HalResult<void>::unsupported();
-}
-
-template <typename I>
-HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() {
- hardware::Return<bool> result = getHal()->supportsAmplitudeControl();
- Capabilities capabilities =
- result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE;
- return HalResultFactory::fromReturn<Capabilities>(std::move(result), capabilities);
-}
-
-template <typename I>
-template <typename T>
-HalResult<milliseconds> HidlHalWrapper<I>::performInternal(
- perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength,
- const std::function<void()>& completionCallback) {
- V1_0::Status status;
- int32_t lengthMs;
- auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
- status = retStatus;
- lengthMs = retLengthMs;
- };
-
- V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength);
- auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback);
- milliseconds length = milliseconds(lengthMs);
-
- auto ret = HalResultFactory::fromReturn<milliseconds>(std::move(result), status, length);
- if (ret.isOk()) {
- mCallbackScheduler->schedule(completionCallback, length);
- }
-
- return ret;
-}
-
-template <typename I>
-sp<I> HidlHalWrapper<I>::getHal() {
- std::lock_guard<std::mutex> lock(mHandleMutex);
- return mHandle;
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_0::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_0::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_1::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_1::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternal(&V1_1::IVibrator::perform_1_1, getHal(),
- static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<milliseconds> HidlHalWrapperV1_2::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_2::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternal(&V1_2::IVibrator::perform_1_1, getHal(),
- static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_2::Effect>(effect)) {
- return performInternal(&V1_2::IVibrator::perform_1_2, getHal(),
- static_cast<V1_2::Effect>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-// -------------------------------------------------------------------------------------------------
-
-HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) {
- auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled));
- return HalResultFactory::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR));
-}
-
-HalResult<milliseconds> HidlHalWrapperV1_3::performEffect(
- Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
- if (isStaticCastValid<V1_0::Effect>(effect)) {
- return performInternal(&V1_3::IVibrator::perform, getHal(),
- static_cast<V1_0::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_1::Effect_1_1>(effect)) {
- return performInternal(&V1_3::IVibrator::perform_1_1, getHal(),
- static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_2::Effect>(effect)) {
- return performInternal(&V1_3::IVibrator::perform_1_2, getHal(),
- static_cast<V1_2::Effect>(effect), strength, completionCallback);
- }
- if (isStaticCastValid<V1_3::Effect>(effect)) {
- return performInternal(&V1_3::IVibrator::perform_1_3, getHal(),
- static_cast<V1_3::Effect>(effect), strength, completionCallback);
- }
-
- ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s",
- Aidl::toString(effect).c_str());
- return HalResult<milliseconds>::unsupported();
-}
-
-HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() {
- Capabilities capabilities = Capabilities::NONE;
-
- sp<V1_3::IVibrator> hal = getHal();
- auto amplitudeResult = hal->supportsAmplitudeControl();
- if (!amplitudeResult.isOk()) {
- return HalResultFactory::fromReturn<Capabilities>(std::move(amplitudeResult), capabilities);
- }
-
- auto externalControlResult = hal->supportsExternalControl();
- if (amplitudeResult.withDefault(false)) {
- capabilities |= Capabilities::AMPLITUDE_CONTROL;
- }
- if (externalControlResult.withDefault(false)) {
- capabilities |= Capabilities::EXTERNAL_CONTROL;
-
- if (amplitudeResult.withDefault(false)) {
- capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
- }
- }
-
- return HalResultFactory::fromReturn<Capabilities>(std::move(externalControlResult),
- capabilities);
-}
-
-// -------------------------------------------------------------------------------------------------
-
}; // namespace vibrator
}; // namespace android
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index 494f88f..31b6ed0 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -20,7 +20,10 @@
#include <vibratorservice/VibratorManagerHalController.h>
-namespace Aidl = aidl::android::hardware::vibrator;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
namespace android {
@@ -29,9 +32,9 @@
std::shared_ptr<ManagerHalWrapper> connectManagerHal(std::shared_ptr<CallbackScheduler> scheduler) {
static bool gHalExists = true;
if (gHalExists) {
- auto serviceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
+ auto serviceName = std::string(IVibratorManager::descriptor) + "/default";
if (AServiceManager_isDeclared(serviceName.c_str())) {
- std::shared_ptr<Aidl::IVibratorManager> hal = Aidl::IVibratorManager::fromBinder(
+ std::shared_ptr<IVibratorManager> hal = IVibratorManager::fromBinder(
ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str())));
if (hal) {
ALOGV("Successfully connected to VibratorManager HAL AIDL service.");
@@ -41,6 +44,7 @@
}
}
+ ALOGV("VibratorManager HAL service not available.");
gHalExists = false;
return std::make_shared<LegacyManagerHalWrapper>();
}
@@ -150,10 +154,10 @@
return apply(cancelSyncedFn, "cancelSynced");
}
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession(
- const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+HalResult<std::shared_ptr<IVibrationSession>> ManagerHalController::startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
const std::function<void()>& completionCallback) {
- hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn =
+ hal_fn<std::shared_ptr<IVibrationSession>> startSessionFn =
[&](std::shared_ptr<ManagerHalWrapper> hal) {
return hal->startSession(ids, config, completionCallback);
};
diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp
index 3db8ff8..bab3f58 100644
--- a/services/vibratorservice/VibratorManagerHalWrapper.cpp
+++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp
@@ -20,7 +20,10 @@
#include <vibratorservice/VibratorManagerHalWrapper.h>
-namespace Aidl = aidl::android::hardware::vibrator;
+using aidl::android::hardware::vibrator::IVibrationSession;
+using aidl::android::hardware::vibrator::IVibrator;
+using aidl::android::hardware::vibrator::IVibratorManager;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
namespace android {
@@ -41,10 +44,9 @@
return HalResult<void>::unsupported();
}
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession(
- const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&,
- const std::function<void()>&) {
- return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported();
+HalResult<std::shared_ptr<IVibrationSession>> ManagerHalWrapper::startSession(
+ const std::vector<int32_t>&, const VibrationSessionConfig&, const std::function<void()>&) {
+ return HalResult<std::shared_ptr<IVibrationSession>>::unsupported();
}
HalResult<void> ManagerHalWrapper::clearSessions() {
@@ -87,11 +89,11 @@
std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator(
int32_t vibratorId, std::shared_ptr<CallbackScheduler> callbackScheduler) {
- std::function<HalResult<std::shared_ptr<Aidl::IVibrator>>()> reconnectFn = [=, this]() {
- std::shared_ptr<Aidl::IVibrator> vibrator;
+ std::function<HalResult<std::shared_ptr<IVibrator>>()> reconnectFn = [=, this]() {
+ std::shared_ptr<IVibrator> vibrator;
auto status = this->getHal()->getVibrator(vibratorId, &vibrator);
- return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrator>>(std::move(status),
- vibrator);
+ return HalResultFactory::fromStatus<std::shared_ptr<IVibrator>>(std::move(status),
+ vibrator);
};
auto result = reconnectFn();
if (!result.isOk()) {
@@ -110,8 +112,8 @@
}
void AidlManagerHalWrapper::tryReconnect() {
- auto aidlServiceName = std::string(Aidl::IVibratorManager::descriptor) + "/default";
- std::shared_ptr<Aidl::IVibratorManager> newHandle = Aidl::IVibratorManager::fromBinder(
+ auto aidlServiceName = std::string(IVibratorManager::descriptor) + "/default";
+ std::shared_ptr<IVibratorManager> newHandle = IVibratorManager::fromBinder(
ndk::SpAIBinder(AServiceManager_checkService(aidlServiceName.c_str())));
if (newHandle) {
std::lock_guard<std::mutex> lock(mHandleMutex);
@@ -198,15 +200,14 @@
return HalResultFactory::fromStatus(getHal()->triggerSynced(cb));
}
-HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession(
- const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config,
+HalResult<std::shared_ptr<IVibrationSession>> AidlManagerHalWrapper::startSession(
+ const std::vector<int32_t>& ids, const VibrationSessionConfig& config,
const std::function<void()>& completionCallback) {
auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback);
- std::shared_ptr<Aidl::IVibrationSession> session;
+ std::shared_ptr<IVibrationSession> session;
auto status = getHal()->startSession(ids, config, cb, &session);
- return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status),
- std::move(
- session));
+ return HalResultFactory::fromStatus<std::shared_ptr<IVibrationSession>>(std::move(status),
+ std::move(session));
}
HalResult<void> AidlManagerHalWrapper::cancelSynced() {
@@ -227,7 +228,7 @@
return HalResultFactory::fromStatus(getHal()->clearSessions());
}
-std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() {
+std::shared_ptr<IVibratorManager> AidlManagerHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
}
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 915d6c7..6fc5cf3 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -29,15 +29,10 @@
],
shared_libs: [
"libbinder_ndk",
- "libhidlbase",
"liblog",
"libutils",
"libvibratorservice",
"android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
],
cflags: [
"-Wall",
diff --git a/services/vibratorservice/include/vibratorservice/VibratorController.h b/services/vibratorservice/include/vibratorservice/VibratorController.h
new file mode 100644
index 0000000..691c8ae
--- /dev/null
+++ b/services/vibratorservice/include/vibratorservice/VibratorController.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_OS_VIBRATOR_CONTROLLER_H
+#define ANDROID_OS_VIBRATOR_CONTROLLER_H
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+namespace vibrator {
+
+// -------------------------------------------------------------------------------------------------
+
+/* Provider for IVibrator HAL service instances. */
+class VibratorProvider {
+public:
+ using IVibrator = ::aidl::android::hardware::vibrator::IVibrator;
+
+ VibratorProvider() : mServiceName(std::string(IVibrator::descriptor) + "/default") {}
+ virtual ~VibratorProvider() = default;
+
+ /* Returns true if vibrator HAL service is declared in the device, false otherwise. */
+ virtual bool isDeclared();
+
+ /* Connects to vibrator HAL, possibly waiting for the declared service to become available. */
+ virtual std::shared_ptr<IVibrator> waitForVibrator();
+
+ /* Connects to vibrator HAL if declared and available, without waiting. */
+ virtual std::shared_ptr<IVibrator> checkForVibrator();
+
+private:
+ std::mutex mMutex;
+ const std::string mServiceName;
+ std::optional<bool> mIsDeclared GUARDED_BY(mMutex);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/* Controller for Vibrator HAL handle.
+ * This relies on VibratorProvider to connect to the underlying Vibrator HAL service and reconnects
+ * after each transaction failed call. This also ensures connecting to the service is thread-safe.
+ */
+class VibratorController {
+public:
+ using Effect = ::aidl::android::hardware::vibrator::Effect;
+ using EffectStrength = ::aidl::android::hardware::vibrator::EffectStrength;
+ using IVibrator = ::aidl::android::hardware::vibrator::IVibrator;
+ using Status = ::ndk::ScopedAStatus;
+ using VibratorOp = std::function<Status(IVibrator*)>;
+
+ VibratorController() : VibratorController(std::make_shared<VibratorProvider>()) {}
+ VibratorController(std::shared_ptr<VibratorProvider> vibratorProvider)
+ : mVibratorProvider(std::move(vibratorProvider)), mVibrator(nullptr) {}
+ virtual ~VibratorController() = default;
+
+ /* Connects HAL service, possibly waiting for the declared service to become available.
+ * This will automatically be called at the first API usage if it was not manually called
+ * beforehand. Call this manually during the setup phase to avoid slowing the first API call.
+ * Returns true if HAL service is declared, false otherwise.
+ */
+ bool init();
+
+ /* Turn vibrator off. */
+ Status off();
+
+ /* Set vibration amplitude in [0,1]. */
+ Status setAmplitude(float amplitude);
+
+ /* Enable/disable external control. */
+ Status setExternalControl(bool enabled);
+
+ /* Enable always-on for given id, with given effect and strength. */
+ Status alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength);
+
+ /* Disable always-on for given id. */
+ Status alwaysOnDisable(int32_t id);
+
+private:
+ /* Max number of attempts to perform an operation when it fails with transaction error. */
+ static constexpr int MAX_ATTEMPTS = 2;
+
+ std::mutex mMutex;
+ std::shared_ptr<VibratorProvider> mVibratorProvider;
+ std::shared_ptr<IVibrator> mVibrator GUARDED_BY(mMutex);
+
+ /* Reconnects HAL service without waiting for the service to become available. */
+ std::shared_ptr<IVibrator> reconnectToVibrator();
+
+ /* Perform given operation on HAL with retries on transaction failures. */
+ Status doWithRetries(const VibratorOp& op, const char* logLabel);
+
+ /* Perform given operation on HAL with logs for error/unsupported results. */
+ static Status doOnce(IVibrator* vibrator, const VibratorOp& op, const char* logLabel);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace vibrator
+
+}; // namespace android
+
+#endif // ANDROID_OS_VIBRATOR_CONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index a1cb3fa..9b3202b 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed
+
#ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H
#define ANDROID_OS_VIBRATORHALCONTROLLER_H
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 339a6e1..68568d4 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed
+
#ifndef ANDROID_OS_VIBRATORHALWRAPPER_H
#define ANDROID_OS_VIBRATORHALWRAPPER_H
@@ -22,7 +24,6 @@
#include <android-base/thread_annotations.h>
#include <android/binder_manager.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <binder/IServiceManager.h>
#include <vibratorservice/VibratorCallbackScheduler.h>
@@ -105,26 +106,6 @@
: fromFailedStatus<T>(std::move(status));
}
- template <typename T>
- static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status&& status, T data) {
- return (status == hardware::vibrator::V1_0::Status::OK)
- ? HalResult<T>::ok(std::move(data))
- : fromFailedStatus<T>(std::move(status));
- }
-
- template <typename T, typename R>
- static HalResult<T> fromReturn(hardware::Return<R>&& ret, T data) {
- return ret.isOk() ? HalResult<T>::ok(std::move(data))
- : fromFailedReturn<T, R>(std::move(ret));
- }
-
- template <typename T, typename R>
- static HalResult<T> fromReturn(hardware::Return<R>&& ret,
- hardware::vibrator::V1_0::Status status, T data) {
- return ret.isOk() ? fromStatus<T>(std::move(status), std::move(data))
- : fromFailedReturn<T, R>(std::move(ret));
- }
-
static HalResult<void> fromStatus(status_t status) {
return (status == android::OK) ? HalResult<void>::ok()
: fromFailedStatus<void>(std::move(status));
@@ -134,17 +115,6 @@
return status.isOk() ? HalResult<void>::ok() : fromFailedStatus<void>(std::move(status));
}
- static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status&& status) {
- return (status == hardware::vibrator::V1_0::Status::OK)
- ? HalResult<void>::ok()
- : fromFailedStatus<void>(std::move(status));
- }
-
- template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>&& ret) {
- return ret.isOk() ? HalResult<void>::ok() : fromFailedReturn<void, R>(std::move(ret));
- }
-
private:
template <typename T>
static HalResult<T> fromFailedStatus(status_t status) {
@@ -166,23 +136,6 @@
}
return HalResult<T>::failed(status.getMessage());
}
-
- template <typename T>
- static HalResult<T> fromFailedStatus(hardware::vibrator::V1_0::Status&& status) {
- switch (status) {
- case hardware::vibrator::V1_0::Status::UNSUPPORTED_OPERATION:
- return HalResult<T>::unsupported();
- default:
- auto msg = "android::hardware::vibrator::V1_0::Status = " + toString(status);
- return HalResult<T>::failed(msg.c_str());
- }
- }
-
- template <typename T, typename R>
- static HalResult<T> fromFailedReturn(hardware::Return<R>&& ret) {
- return ret.isDeadObject() ? HalResult<T>::transactionFailed(ret.description().c_str())
- : HalResult<T>::failed(ret.description().c_str());
- }
};
// -------------------------------------------------------------------------------------------------
@@ -423,8 +376,8 @@
virtual HalResult<void> performPwleEffect(const std::vector<PrimitivePwle>& primitives,
const std::function<void()>& completionCallback);
- virtual HalResult<void> composePwleV2(const CompositePwleV2& composite,
- const std::function<void()>& completionCallback);
+ virtual HalResult<std::chrono::milliseconds> composePwleV2(
+ const CompositePwleV2& composite, const std::function<void()>& completionCallback);
protected:
// Shared pointer to allow CallbackScheduler to outlive this wrapper.
@@ -511,8 +464,9 @@
const std::vector<PrimitivePwle>& primitives,
const std::function<void()>& completionCallback) override final;
- HalResult<void> composePwleV2(const CompositePwleV2& composite,
- const std::function<void()>& completionCallback) override final;
+ HalResult<std::chrono::milliseconds> composePwleV2(
+ const CompositePwleV2& composite,
+ const std::function<void()>& completionCallback) override final;
protected:
HalResult<Capabilities> getCapabilitiesInternal() override final;
@@ -547,108 +501,6 @@
std::shared_ptr<IVibrator> getHal();
};
-// Wrapper for the HDIL Vibrator HALs.
-template <typename I>
-class HidlHalWrapper : public HalWrapper {
-public:
- HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle)
- : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {}
- virtual ~HidlHalWrapper() = default;
-
- HalResult<void> ping() override final;
- void tryReconnect() override final;
-
- HalResult<void> on(std::chrono::milliseconds timeout,
- const std::function<void()>& completionCallback) override final;
- HalResult<void> off() override final;
-
- HalResult<void> setAmplitude(float amplitude) override final;
- virtual HalResult<void> setExternalControl(bool enabled) override;
-
- HalResult<void> alwaysOnEnable(int32_t id, HalWrapper::Effect effect,
- HalWrapper::EffectStrength strength) override final;
- HalResult<void> alwaysOnDisable(int32_t id) override final;
-
-protected:
- std::mutex mHandleMutex;
- sp<I> mHandle GUARDED_BY(mHandleMutex);
-
- virtual HalResult<Capabilities> getCapabilitiesInternal() override;
-
- template <class T>
- using perform_fn =
- hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength,
- hardware::vibrator::V1_0::IVibrator::perform_cb);
-
- template <class T>
- HalResult<std::chrono::milliseconds> performInternal(
- perform_fn<T> performFn, sp<I> handle, T effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback);
-
- sp<I> getHal();
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.0.
-class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> {
-public:
- HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_0::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_0() = default;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.1.
-class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> {
-public:
- HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_1::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_1() = default;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.2.
-class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> {
-public:
- HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_2::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_2() = default;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-};
-
-// Wrapper for the HDIL Vibrator HAL v1.3.
-class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> {
-public:
- HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler,
- sp<hardware::vibrator::V1_3::IVibrator> handle)
- : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler),
- std::move(handle)) {}
- virtual ~HidlHalWrapperV1_3() = default;
-
- HalResult<void> setExternalControl(bool enabled) override final;
-
- HalResult<std::chrono::milliseconds> performEffect(
- HalWrapper::Effect effect, HalWrapper::EffectStrength strength,
- const std::function<void()>& completionCallback) override final;
-
-protected:
- HalResult<Capabilities> getCapabilitiesInternal() override final;
-};
-
// -------------------------------------------------------------------------------------------------
}; // namespace vibrator
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index 92527eb..92c6286 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -27,12 +27,9 @@
test_suites: ["device-tests"],
srcs: [
"VibratorCallbackSchedulerTest.cpp",
+ "VibratorControllerTest.cpp",
"VibratorHalControllerTest.cpp",
"VibratorHalWrapperAidlTest.cpp",
- "VibratorHalWrapperHidlV1_0Test.cpp",
- "VibratorHalWrapperHidlV1_1Test.cpp",
- "VibratorHalWrapperHidlV1_2Test.cpp",
- "VibratorHalWrapperHidlV1_3Test.cpp",
"VibratorManagerHalControllerTest.cpp",
"VibratorManagerHalWrapperAidlTest.cpp",
"VibratorManagerHalWrapperLegacyTest.cpp",
@@ -43,17 +40,12 @@
"-Wextra",
],
shared_libs: [
+ "android.hardware.vibrator-V3-ndk",
"libbase",
"libbinder_ndk",
- "libhidlbase",
"liblog",
- "libvibratorservice",
"libutils",
- "android.hardware.vibrator-V3-ndk",
- "android.hardware.vibrator@1.0",
- "android.hardware.vibrator@1.1",
- "android.hardware.vibrator@1.2",
- "android.hardware.vibrator@1.3",
+ "libvibratorservice",
],
static_libs: [
"libgmock",
diff --git a/services/vibratorservice/test/VibratorControllerTest.cpp b/services/vibratorservice/test/VibratorControllerTest.cpp
new file mode 100644
index 0000000..11ec75b
--- /dev/null
+++ b/services/vibratorservice/test/VibratorControllerTest.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "VibratorControllerTest"
+
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <utils/Log.h>
+#include <thread>
+
+#include <vibratorservice/VibratorController.h>
+
+#include "test_mocks.h"
+#include "test_utils.h"
+
+using ::aidl::android::hardware::vibrator::Effect;
+using ::aidl::android::hardware::vibrator::EffectStrength;
+using ::aidl::android::hardware::vibrator::IVibrator;
+
+using namespace android;
+using namespace testing;
+
+const auto kReturnOk = []() { return ndk::ScopedAStatus::ok(); };
+const auto kReturnUnsupported = []() {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+};
+const auto kReturnTransactionFailed = []() {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+};
+const auto kReturnUnknownTransaction = []() {
+ return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION);
+};
+const auto kReturnIllegalArgument = []() {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+/* Provides mock IVibrator instance for testing. */
+class FakeVibratorProvider : public vibrator::VibratorProvider {
+public:
+ FakeVibratorProvider()
+ : mIsDeclared(true),
+ mMockVibrator(ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>()),
+ mConnectCount(0),
+ mReconnectCount(0) {}
+ virtual ~FakeVibratorProvider() = default;
+
+ bool isDeclared() override { return mIsDeclared; }
+
+ std::shared_ptr<IVibrator> waitForVibrator() override {
+ mConnectCount++;
+ return mIsDeclared ? mMockVibrator : nullptr;
+ }
+
+ std::shared_ptr<IVibrator> checkForVibrator() override {
+ mReconnectCount++;
+ return mIsDeclared ? mMockVibrator : nullptr;
+ }
+
+ void setDeclared(bool isDeclared) { mIsDeclared = isDeclared; }
+
+ int32_t getConnectCount() { return mConnectCount; }
+
+ int32_t getReconnectCount() { return mReconnectCount; }
+
+ std::shared_ptr<StrictMock<vibrator::MockIVibrator>> getMockVibrator() { return mMockVibrator; }
+
+private:
+ bool mIsDeclared;
+ std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator;
+ int32_t mConnectCount;
+ int32_t mReconnectCount;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class VibratorControllerTest : public Test {
+public:
+ void SetUp() override {
+ mProvider = std::make_shared<FakeVibratorProvider>();
+ mController = std::make_unique<vibrator::VibratorController>(mProvider);
+ ASSERT_NE(mController, nullptr);
+ }
+
+protected:
+ std::shared_ptr<FakeVibratorProvider> mProvider = nullptr;
+ std::unique_ptr<vibrator::VibratorController> mController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(VibratorControllerTest, TestInitServiceDeclared) {
+ ASSERT_TRUE(mController->init());
+ ASSERT_EQ(1, mProvider->getConnectCount());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+
+ // Noop when wrapper was already initialized.
+ ASSERT_TRUE(mController->init());
+ ASSERT_EQ(1, mProvider->getConnectCount());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestInitServiceNotDeclared) {
+ mProvider->setDeclared(false);
+
+ ASSERT_FALSE(mController->init());
+ ASSERT_EQ(0, mProvider->getConnectCount());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+
+ ASSERT_FALSE(mController->init());
+ ASSERT_EQ(0, mProvider->getConnectCount());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestFirstCallTriggersInit) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnOk);
+
+ auto status = mController->off();
+ ASSERT_TRUE(status.isOk());
+ ASSERT_EQ(1, mProvider->getConnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestSuccessfulResultDoesNotRetry) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnOk);
+
+ auto status = mController->off();
+ ASSERT_TRUE(status.isOk());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestUnsupportedOperationResultDoesNotRetry) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnUnsupported);
+
+ auto status = mController->off();
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestUnknownTransactionResultDoesNotRetry) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnUnknownTransaction);
+
+ auto status = mController->off();
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestOperationFailedDoesNotRetry) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnIllegalArgument);
+
+ auto status = mController->off();
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(0, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestTransactionFailedRetriesOnlyOnce) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(2))
+ .WillRepeatedly(kReturnTransactionFailed);
+
+ auto status = mController->off();
+ ASSERT_FALSE(status.isOk());
+ ASSERT_EQ(1, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestTransactionFailedThenSucceedsReturnsSuccessAfterRetries) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(2))
+ .WillOnce(kReturnTransactionFailed)
+ .WillRepeatedly(kReturnOk);
+
+ auto status = mController->off();
+ ASSERT_TRUE(status.isOk());
+ ASSERT_EQ(1, mProvider->getReconnectCount());
+}
+
+TEST_F(VibratorControllerTest, TestOff) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), off())
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnOk);
+
+ auto status = mController->off();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(VibratorControllerTest, TestSetAmplitude) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.1f)))
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnOk);
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.2f)))
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnIllegalArgument);
+
+ ASSERT_TRUE(mController->setAmplitude(0.1f).isOk());
+ ASSERT_FALSE(mController->setAmplitude(0.2f).isOk());
+}
+
+TEST_F(VibratorControllerTest, TestSetExternalControl) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(false)))
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnOk);
+ EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(true)))
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnIllegalArgument);
+
+ ASSERT_TRUE(mController->setExternalControl(false).isOk());
+ ASSERT_FALSE(mController->setExternalControl(true).isOk());
+}
+
+TEST_F(VibratorControllerTest, TestAlwaysOnEnable) {
+ EXPECT_CALL(*mProvider->getMockVibrator().get(),
+ alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT)))
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnOk);
+ EXPECT_CALL(*mProvider->getMockVibrator().get(),
+ alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM)))
+ .Times(Exactly(1))
+ .WillRepeatedly(kReturnIllegalArgument);
+
+ ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk());
+ ASSERT_FALSE(mController->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM).isOk());
+}
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index c58e05c..7545148 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -787,5 +787,6 @@
result = mWrapper->composePwleV2(composite, callback);
ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(300ms, result.value());
ASSERT_EQ(1, *callbackCounter.get());
}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
deleted file mode 100644
index 04dbe4e..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_0Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-#include <android/persistable_bundle_aidl.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-#include <thread>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-
-using aidl::android::hardware::vibrator::Braking;
-using aidl::android::hardware::vibrator::CompositeEffect;
-using aidl::android::hardware::vibrator::CompositePrimitive;
-using aidl::android::hardware::vibrator::CompositePwleV2;
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-using aidl::android::hardware::vibrator::IVibrator;
-using aidl::android::hardware::vibrator::PrimitivePwle;
-using aidl::android::hardware::vibrator::PwleV2Primitive;
-using aidl::android::hardware::vibrator::VendorEffect;
-using aidl::android::os::PersistableBundle;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_0 : public V1_0::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<void>, ping, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_0Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_0>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) {
- EXPECT_CALL(*mMockHal.get(), ping())
- .Times(Exactly(2))
- .WillOnce([]() { return hardware::Return<void>(); })
- .WillRepeatedly([]() {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
-
- ASSERT_TRUE(mWrapper->ping().isOk());
- ASSERT_TRUE(mWrapper->ping().isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1))))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint32_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- });
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint32_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
- });
- EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint32_t) {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->on(1ms, callback).isOk());
- ASSERT_EQ(1, *callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported());
- ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed());
- ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) {
- EXPECT_CALL(*mMockHal.get(), off())
- .Times(Exactly(4))
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
- .WillOnce([]() {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- })
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
- .WillRepeatedly([]() {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
-
- ASSERT_TRUE(mWrapper->off().isOk());
- ASSERT_TRUE(mWrapper->off().isUnsupported());
- ASSERT_TRUE(mWrapper->off().isFailed());
- ASSERT_TRUE(mWrapper->off().isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(1))))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); });
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint8_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- });
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint8_t) {
- return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE);
- });
- EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4))))
- .Times(Exactly(1))
- .WillRepeatedly([](uint8_t) {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- auto maxAmplitude = std::numeric_limits<uint8_t>::max();
- ASSERT_TRUE(mWrapper->setAmplitude(1.0f / maxAmplitude).isOk());
- ASSERT_TRUE(mWrapper->setAmplitude(2.0f / maxAmplitude).isUnsupported());
- ASSERT_TRUE(mWrapper->setAmplitude(3.0f / maxAmplitude).isFailed());
- ASSERT_TRUE(mWrapper->setAmplitude(4.0f / maxAmplitude).isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) {
- ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
- ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) {
- ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) {
- ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) {
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(2))
- .WillOnce([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- })
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
-
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
- vibrator::Info info = mWrapper->getInfo();
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
- ASSERT_TRUE(info.supportedEffects.isUnsupported());
- ASSERT_TRUE(info.supportedBraking.isUnsupported());
- ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
- ASSERT_TRUE(info.primitiveDurations.isUnsupported());
- ASSERT_TRUE(info.primitiveDelayMax.isUnsupported());
- ASSERT_TRUE(info.pwlePrimitiveDurationMax.isUnsupported());
- ASSERT_TRUE(info.compositionSizeMax.isUnsupported());
- ASSERT_TRUE(info.pwleSizeMax.isUnsupported());
- ASSERT_TRUE(info.minFrequency.isUnsupported());
- ASSERT_TRUE(info.resonantFrequency.isUnsupported());
- ASSERT_TRUE(info.frequencyResolution.isUnsupported());
- ASSERT_TRUE(info.qFactor.isUnsupported());
- ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
- ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
- return hardware::Return<bool>(false);
- });
-
- ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) {
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() {
- return hardware::Return<bool>(true);
- });
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(
- std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- vibrator::Info info = mWrapper->getInfo();
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, info.capabilities.value());
- ASSERT_TRUE(info.supportedEffects.isUnsupported());
- ASSERT_TRUE(info.supportedBraking.isUnsupported());
- ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
- ASSERT_TRUE(info.primitiveDurations.isUnsupported());
- ASSERT_TRUE(info.minFrequency.isUnsupported());
- ASSERT_TRUE(info.resonantFrequency.isUnsupported());
- ASSERT_TRUE(info.frequencyResolution.isUnsupported());
- ASSERT_TRUE(info.qFactor.isUnsupported());
- ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
- ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
- ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _))
- .Times(Exactly(2))
- .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 10);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) {
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- // Using TICK that is only available in v1.1
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isUnsupported());
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformVendorEffectUnsupported) {
- PersistableBundle vendorData; // empty
- VendorEffect vendorEffect;
- vendorEffect.vendorData = vendorData;
- vendorEffect.strength = EffectStrength::LIGHT;
- vendorEffect.scale = 1.0f;
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->performVendorEffect(vendorEffect, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) {
- std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects;
- singleEffect.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f));
- multipleEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
- multipleEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported());
- ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformPwleEffectUnsupported) {
- std::vector<PrimitivePwle> emptyPrimitives, multiplePrimitives;
- multiplePrimitives.push_back(vibrator::TestFactory::createActivePwle(0, 1, 0, 1, 10ms));
- multiplePrimitives.push_back(vibrator::TestFactory::createBrakingPwle(Braking::NONE, 100ms));
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->performPwleEffect(emptyPrimitives, callback).isUnsupported());
- ASSERT_TRUE(mWrapper->performPwleEffect(multiplePrimitives, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_0Test, TestComposePwleV2Unsupported) {
- CompositePwleV2 composite;
- composite.pwlePrimitives = {
- PwleV2Primitive(/*amplitude=*/0.2, /*frequency=*/50, /*time=*/100),
- PwleV2Primitive(/*amplitude=*/0.5, /*frequency=*/150, /*time=*/100),
- PwleV2Primitive(/*amplitude=*/0.8, /*frequency=*/250, /*time=*/100),
- };
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- ASSERT_TRUE(mWrapper->composePwleV2(composite, callback).isUnsupported());
-
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
deleted file mode 100644
index b0a6537..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_1Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_1 : public V1_1::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_1,
- (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
- (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_1Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_1>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _))
- .Times(Exactly(2))
- .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_1::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 0);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) {
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- // Using THUD that is only available in v1.2
- auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isUnsupported());
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
deleted file mode 100644
index dfe3fa0..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_2Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_2 : public V1_2::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_1,
- (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
- (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_2,
- (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_2Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_2>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _))
- .Times(Exactly(2))
- .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 10);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) {
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- // Using TEXTURE_TICK that is only available in v1.3
- auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isUnsupported());
- // No callback is triggered.
- ASSERT_EQ(0, *callbackCounter.get());
-}
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
deleted file mode 100644
index 8624332..0000000
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "VibratorHalWrapperHidlV1_3Test"
-
-#include <aidl/android/hardware/vibrator/IVibrator.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <utils/Log.h>
-#include <thread>
-
-#include <vibratorservice/VibratorCallbackScheduler.h>
-#include <vibratorservice/VibratorHalWrapper.h>
-
-#include "test_mocks.h"
-#include "test_utils.h"
-
-namespace V1_0 = android::hardware::vibrator::V1_0;
-namespace V1_1 = android::hardware::vibrator::V1_1;
-namespace V1_2 = android::hardware::vibrator::V1_2;
-namespace V1_3 = android::hardware::vibrator::V1_3;
-
-using aidl::android::hardware::vibrator::Effect;
-using aidl::android::hardware::vibrator::EffectStrength;
-using aidl::android::hardware::vibrator::IVibrator;
-
-using namespace android;
-using namespace std::chrono_literals;
-using namespace testing;
-
-// -------------------------------------------------------------------------------------------------
-
-class MockIVibratorV1_3 : public V1_3::IVibrator {
-public:
- MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override));
- MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override));
- MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override));
- MOCK_METHOD(hardware::Return<void>, perform,
- (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_1,
- (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb),
- (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_2,
- (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
- MOCK_METHOD(hardware::Return<void>, perform_1_3,
- (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override));
-};
-
-// -------------------------------------------------------------------------------------------------
-
-class VibratorHalWrapperHidlV1_3Test : public Test {
-public:
- void SetUp() override {
- mMockHal = new StrictMock<MockIVibratorV1_3>();
- mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>();
- mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal);
- ASSERT_NE(mWrapper, nullptr);
- }
-
-protected:
- std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr;
- std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr;
- sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr;
-};
-
-// -------------------------------------------------------------------------------------------------
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true)))
- .Times(Exactly(2))
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); })
- .WillRepeatedly([]() {
- return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION);
- });
- EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false)))
- .Times(Exactly(2))
- .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); })
- .WillRepeatedly([]() {
- return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- ASSERT_TRUE(mWrapper->setExternalControl(true).isOk());
- ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported());
- ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
- ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoSuccessful) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(true);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
- vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
- mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyAmplitudeControl) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(true);
- });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoOnlyExternalControl) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(true);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoNoCapabilities) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(false); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- }
-
- ASSERT_EQ(vibrator::Capabilities::NONE, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoFailed) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
-
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoCachesResult) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() {
- return hardware::Return<bool>(false);
- });
- }
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(
- std::thread([&]() { ASSERT_TRUE(mWrapper->getInfo().capabilities.isOk()); }));
- }
- std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
-
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetInfoDoesNotCacheFailedResult) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
-
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() {
- return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1));
- });
-
- EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(true); });
- EXPECT_CALL(*mMockHal.get(), supportsExternalControl())
- .Times(Exactly(1))
- .WillRepeatedly([]() { return hardware::Return<bool>(false); });
- }
-
- // Call to supportsAmplitudeControl failed.
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
- // Call to supportsExternalControl failed.
- ASSERT_TRUE(mWrapper->getInfo().capabilities.isFailed());
-
- // Returns successful result from third call.
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-
- // Returns cached successful result.
- ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, mWrapper->getInfo().capabilities.value());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength,
- MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
- auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback);
-
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-}
-
-TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) {
- {
- InSequence seq;
- EXPECT_CALL(*mMockHal.get(),
- perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::OK, 10);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms)))
- .Times(Exactly(1))
- .WillRepeatedly(vibrator::TriggerSchedulerCallback());
- EXPECT_CALL(*mMockHal.get(),
- perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM),
- _))
- .Times(Exactly(1))
- .WillRepeatedly(
- [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::UNSUPPORTED_OPERATION, 0);
- return hardware::Return<void>();
- });
- EXPECT_CALL(*mMockHal.get(),
- perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG),
- _))
- .Times(Exactly(2))
- .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) {
- cb(V1_0::Status::BAD_VALUE, 0);
- return hardware::Return<void>();
- })
- .WillRepeatedly(
- [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) {
- return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
- });
- }
-
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
-
- auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback);
- ASSERT_TRUE(result.isOk());
- ASSERT_EQ(10ms, result.value());
- ASSERT_EQ(1, *callbackCounter.get());
-
- result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback);
- ASSERT_TRUE(result.isUnsupported());
-
- result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback);
- ASSERT_TRUE(result.isFailed());
-
- // Callback not triggered for unsupported and on failure
- ASSERT_EQ(1, *callbackCounter.get());
-}